在写完上一篇《Pull or Push》之后,原本计划这一片写《存储层设计》,但是临时改变主意了,想先写一篇介绍一下消息中间件最最基础也是最核心的部分:write-ahead logging(WAL)。

什么是WAL

"In computer science, write-ahead logging (WAL) is a family of techniques for providing atomicity and durability (two of the ACID properties) in database systems."——维基百科

在计算机领域,WAL(Write-ahead logging,预写式日志)是数据库系统提供原子性和持久化的一系列技术。

在使用WAL的系统中,所有的修改都先被写入到日志中,然后再被应用到系统状态中。通常包含redo和undo两部分信息。

为什么需要使用WAL,然后包含redo和undo信息呢?举个例子,如果一个系统直接将变更应用到系统状态中,那么在机器掉电重启之后系统需要知道操作是成功了,还是只有部分成功或者是失败了(为了恢复状态)。如果使用了WAL,那么在重启之后系统可以通过比较日志和系统状态来决定是继续完成操作还是撤销操作。

redo log

redo log称为重做日志,每当有操作时,在数据变更之前将操作写入redo log,这样当发生掉电之类的情况时系统可以在重启后继续操作。

undo log

undo log称为撤销日志,当一些变更执行到一半无法完成时,可以根据撤销日志恢复到变更之间的状态。

MySQL中用redo log来在系统Crash重启之类的情况时修复数据(事务的持久性),而undo log来保证事务的原子性。

WAL在消息中间件中的应用

WAL可以说是消息中间件的基础,也是所有存储类系统的基础。

在消息中间件中,WAL没有MySQL中那么复杂,我们只需要记redo log。消息直接存储在redo log中,只要写redo log完成了,那么消息就写入完成了:

  1. 消息写入redo log就表明持久化了

  2. 且不会出现原子性的问题,消息写入即成功,没写入即失败

存储结构

使用WAL存储数据,就需要去组织存储文件,比如MySQL的binlog文件。在消息中间件中也需要类似的形式去组织redo log,即消息的存储文件。

我们采用固定大小的存储文件,这样在索引消息的时候,只要知道偏移量,就能找到对应的存储文件。比如下面是文件大小为1024的存储文件示例:

消息被不断的追加到最新的存储文件中。

消息在文件中的存储格式大致如上:

  • 采用二进制的格式存储消息

  • 消息是不定长的

所以这里会需要一个结构来索引消息。索引到一条完整的消息只需要两个元素:偏移量、大小。只要有这两个元素就可以从存储日志中读取一段完整的数据(一条完整的消息)。

以上的结构是最简单的消息中间件存储的模型,虽然离真正应用到实践中还有一些距离,但是核心思想是一致的。

考虑这几个问题:

  1. 消息具体的存储协议,即存储文件中消息需要包含哪些内容

  2. 如何优化索引结构,支持消息回溯、消息过滤等功能

确定一下这几个问题的解决方案基本就是一个可以使用的消息中间件的WAL了。

Crash Recovery

上文说了redo log用于在系统Crash之后做恢复(在中间件中也直接作为数据存储),那么redo log具体我们可以如何使用它进行系统状态的恢复呢?

在消息写入的过程中,这里的状态只有一个,就是消息索引。只要消息索引构建了,那么消息就可以被消息;如果消息索引没构建,那么这条消息就是不可消费的(等价于消息没有写入)。

那么这里的恢复就是在系统Crash之后如何构建索引。

比如上图的状态是已经构建了消息0-3的索引,需要构建消息4-7的索引信息。

这个实现很简单:

  • 消息一旦构建了索引,就记录checkpoint;checkpoint可以定期刷盘

  • 系统恢复过程中读取checkpoint之后的消息构建索引;如果读取的消息不完整,则丢弃消息(可以采用CRC验证之类的方式来校验消息是否完成)

因为消息没有完整写入redo log的情况(写入时系统Crash了)系统并不会响应Producer消息发送成功(只有成功刷盘了才会响应成功),所以直接丢弃不完整的消息并不会对系统语义产生影响(不完整的消息没有响应Producer,也没有构建索引)。

结语

上文介绍了一下WAL,简要的描述了消息中间件存储模型和Crash Recovery,还有一些遗留的问题:

  1. 如何实现消息回溯和过滤,如何支持多tag过滤?

  2. 消息需要写文件且刷盘,如何保证写入性能?

  3. 消费时需要从文件读取消息,如何保证性能?

  4. 序列化的消息需要包含哪些属性才能完整的恢复出索引?

往期文章:

Push or Pull?

消息中间件核心实体(1)

消息中间件核心实体(0)

消息的写入和读取流程

NameServer模块划分

Client模块划分

Broker模块划分

消息中间件架构讨论

业务方对消息中间件的需求

消息中间件中的一些概念

什么是分布式消息中间件?

什么是WAL?的更多相关文章

  1. HBase的Write Ahead Log (WAL) —— 整体架构、线程模型

    解决的问题 HBase的Write Ahead Log (WAL)提供了一种高并发.持久化的日志保存与回放机制.每一个业务数据的写入操作(PUT / DELETE)执行前,都会记账在WAL中. 如果出 ...

  2. HBase的Write Ahead Log (WAL) —— API与基本概念

    HBase的数据写入操作,会先记录到HLog中,再真正写入到MemStore中.前者是对写入友好的格式,后者是对查询友好的格式.所以前者吞吐量更高,写入成功率大,提高了系统的可靠性,“基本”可以实现宕 ...

  3. sqlite之WAL模式

    链接 概述 在3.7.0以后,WAL(Write-Ahead Log)模式可以使用,是另一种实现事务原子性的方法. WAL的优点 在大多数情况下更快 并行性更高.因为读操作和写操作可以并行. 文件IO ...

  4. Sqlite学习笔记(三)&&WAL性能测试

    WAL是SQLite3.7.0版本引入的一个重大改进.SQLite官网宣称在很多使用场景下,WAL模型的性能都要好于默认的DELETE模式.下面将针对几个主要场景对WAL性能做测试,测试的硬件与xxx ...

  5. 预写式日志(Write-Ahead Logging (WAL))

    SQL Server中使用了WAL(Write-Ahead Logging)技术来保证事务日志的ACID特性.而且大大减少了IO操作. WAL的核心思想是:在数据写入到数据库之前,先写入到日志.再将日 ...

  6. InnoDB的WAL方式学习

    之前写过一篇博文,<不好的MySQL过程编写习惯>(http://www.cnblogs.com/wingsless/p/5041838.html).这篇博文里强调了不要循环的提交事务,尽 ...

  7. SQLite的WAL机制

    标注:本文部分有黏贴这里的资料,另外还加了一些自己的笔记 使用CoreData或者SQLite3的时候,我们创建的数据库, 在存储的文件夹中有三个文件:分别为:**.sqlite  **.sqlite ...

  8. Hbase的WAL在RegionServer基本调用过程

    版权声明:本文由熊训德原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/221 来源:腾云阁 https://www.qclo ...

  9. Hbase WAL线程模型源码分析

    版权声明:本文由熊训德原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/257 来源:腾云阁 https://www.qclo ...

  10. WAL

    WAL record format typedef struct XLogRecord{pg_crc32         xl_crc; /* CRC for this record */XLogRe ...

随机推荐

  1. Centos 安装boost库

    1.在http://www.boost.org/下载boost安装包boost_1_65_1.tar.gz 2.在Centos上解压tar -zxvf  boost_1_65_1.tar.gz后,cd ...

  2. node实现一个WEBSOCKET服务器

    早点时候翻译了篇实现一个websocket服务器-理论篇,简单介绍了下理论基础,本来打算放在一起,但是感觉太长了大家可能都看不下去.不过发现如果拆开的话,还是不可避免的要提及理论部分.用到的地方就简要 ...

  3. C# DateDateTimePicker设置属性ShowCheckBox为True

    DateDateTimePicker的属性ShowCheckBox为True后,可以使用时间控件的复选框. 但是如果,你想设置CheckBox的选中状态为False的话,那么请注意赋时间值和赋状态值的 ...

  4. springmvc关于前台日期作为实体类对象参数类型转换错误

    页面报错: 后台错误: Field error in object 'user' on field 'birthday': rejected value [2013-06-24]; codes [ty ...

  5. java学习总结篇一--写在正式成为码农一年后

    一直想写一写工作了一年多的总结与感悟,今天正好有时间,也有这个兴致,随手总结一下这一年来学习及工作的情况. 大学时很无奈地被选择了计算机专业,本人对计算机,不讨厌,也算不上多喜欢.只是当惯了好学生,好 ...

  6. python re 正则匹配 split sub

    import re 编译: motif='([ST])Q' seq="SQAAAATQ" regrex=re.compile(motif) #编译成正则对象 regrex=re.c ...

  7. oralce11g导出dmp然后导入Oracle10g

    一次Oracle11g数据库导入 Oracle10g数据库操作笔记 11g备份导入10g的时候会抛错直接阻止导入. 但是有时候还必须得把11g的数据库导入到10g我今天就遇到了这种情况. 一开始 ...

  8. Zookeeper 集群安装

    负载均衡(Load Balance)是分布式系统架构设计中必须考虑的因素之一,它通常是指,将请求/数据[均匀]分摊到多个操作单元上执行,负载均衡的关键在于[均匀].常见互联网分布式架构如上,分为客户端 ...

  9. 《iOS Human Interface Guidelines》——Multitasking

    多任务处理 多任务处理让人们在屏幕上(以及合适的iPad模式)查看多个app,而且在近期使用的app中高速地切换. 在iOS 9中.人们能够使用多任务处理UI(例如以下所看到的)来选择一个近期使用的a ...

  10. ListView原理

    表明转载自http://blog.csdn.net/iispring/article/details/50967445 在自己定义Adapter时,我们经常会重写Adapter的getView方法,该 ...