中间件中的WAL、Page Cache、刷盘机制

WALPage Cache、刷盘机制

WAL

Write Ahead Log简称WAL,在分布式存储系统中的元数据更新中应用得十分广泛。WAL的主要意思是说在将元数据的变更操作写入到持久稳定的数据库之前,先预先写入到一个log中,然后再由另外的操作将log apply到外部的持久数据库里去。这种模式会减少掉每次的数据库写入操作,尤其当系统要处理大量的transaction操作的时候,WAL的方式相比较于实时同步数据库的方式有着更高的效率。

WAL还有一点很重要的帮助是可以在disaster recovery过程中起到状态恢复的作用,系统在load完元数据db后,再把未来得及提交的WAL apply进来,就能恢复成和之前最终一致的状态。

参考: spongecaptain.cool/post/distri…

Page Cache

Linux系统中为了减少对磁盘的IO操作,会将打开的磁盘内容进行缓存,而缓存的地方则是物理内存,进而将对磁盘的访问转换成对内存的访问,有效提高程序的速度。Linux的缓存方式是利用物理内存缓存磁盘上的内容,称为页缓存(page cache)。

页缓存是由内存中的物理页面组成的,其内容对应磁盘上的物理块。页缓存的大小会根据系统的内存空闲大小进行动态调整,它可以通过占用内存以扩张大小,也可以自我收缩以缓解内存使用压力。

参考:

[1] www.cnblogs.com/leadership/…

[2] juejin.cn/post/684490…

MYSQL

刷盘指的是从Buffer Pool写到Page Cache.

fsync指的是从Page Cache持久化数据到磁盘,涉及IO。

Redo Log的一些参数:

innodb_flush_logs_at_trx_commit

  • 取值0:每次提交事务都只把redo log留在redo log buffer中
  • 取值1:每次提交事务都将redo log 持久化到磁盘上,也就是write+fsync
  • 取值2:每次都把redo log写到系统的page cache中,也就是只write,不fsync

binlog的一些参数:

sync_binlog

  • 取值0:每次提交都将binlogbinlog cache中 write到磁盘上,而不fsync到磁盘
  • 取值1:每次提交事务都将binlog fsync到磁盘上
  • 取值N:每次提交事务都将binlog write到磁盘上,累计N个事务之后,执行fsync

redo log的write和fsync没有连接在一起,其实是考虑到了组提交的功能,在并发的场景下,可以让这一组一次性提交的redo log更多一点,从而一次性fsync更多的组员。

innodb采用了WAL技术(先写log,再写磁盘)来减少磁盘写。但是我们上面的分析过程不难看出,在事务提交的过程中,redo log prepare阶段会进行一次fsync磁盘,binlog 阶段也会fsync一次磁盘,这似乎并没有减少与磁盘的交互,MySQL这样设计的意义是什么?

  • redo log和binlog都是顺序写,顺序写比数据页的随机写节约时间,性能更高
  • 组提交机制使得我们不用每个事务都进行写磁盘操作,而是将多个写操作放在一个组里面,这样可以大幅度降低磁盘的IOPS

参考:

[1] cloud.tencent.com/developer/a…

Redis

AOF写入磁盘的顺序

应用程序的AOF BufferOS Cache →磁盘

Redis AOF的一些配置:

配置 含义 效率 安全
appendfsync=always 同步持久化每一次修改操作 效率最低 安全最高,最多丢失一个事件循环的数据
appendfsync=everysec 每秒向aof文件同步一次 效率最折中 安全最折中,最多丢失1S的数据(但是实际上是有可能大于1S的)
appendfsync=no 关闭向aof文件写入修改 效率最高 安全最低,最多上一次保存AOF文件到当前时刻的全部数据

参考:

[1] blog.csdn.net/my_momo_csd…

Kafka

Kafka 通过 log.flush.interval.messages 以及log.flush.interval.ms等属性进行按照消息量以及时间间隔将数据通过fsync系统调用持久化到磁盘,在默认情况下进行异步刷盘,即每次写操作并不直接导致调用一次 fsync

kafka提供3个参数来优化刷盘机制

  • log.flush.interval.messages //多少条消息刷盘1次
  • log.flush.interval.ms //隔多长时间刷盘1次
  • log.flush.scheduler.interval.ms //周期性的刷盘。

参考:

[1] spongecaptain.cool/post/distri…

[2] blog.csdn.net/u013128262/…

Elasticsearch

ElasticSearch底层依赖lucene来提供搜索能力,一个lucene index由许多独立的Segments组成。

lucene_index

Segment是最小的数据存储单元。其中包含了文档中的词汇字典、词汇字典的倒排索引以及Document的字段数据。因此Segment直接提供了搜索功能。但是Segment能提供搜索的前提是数据必须被提交,即文档经过一系列的处理之后生成倒排索引等一系列数据。可以想见这个过程是比较耗时的。因此Elasticsearch并不会每接收到一条数据就提交到一个Segment中,一方面是因为这样耗时太长,另一方面是这样会生成巨量的Segment,降低了IO性能。

Elasticsearch采取的机制是将数据添加到lucenelucene内部会维护一个数据缓冲区,此时数据都是不可搜索的。每隔一段时间(默认为1秒),Elasticsearch会执行一次refresh操作:lucene中所有的缓存数据都被写入到一个新的Segment,清空缓存数据。此时数据就可以被搜索。当然,每次执行refresh操作都会生成一个新的Segment文件,这样一来Segment文件有大有小,相当碎片化。Elasticsearch内部会开启一个线程将小的Segment合并(Merge)成大的Segment,减少碎片化,降低文件打开数,提升IO性能。

不过这样也带来一个问题。数据写入缓冲区中,没有及时保存到磁盘中,一旦发生程序崩溃或者服务器宕机,数据就会发生丢失。**为了保证可靠性,Elasticsearch引入了Translog(事务日志)。每次数据被添加或者删除,都要在Translog中添加一条记录。**这样一旦发生崩溃,数据可以从Translog中恢复。

不过,不要以为数据被写到Translog中就已经被保存到磁盘了。**一般情况下,对磁盘文件的write操作,更新的只是内存中的页缓存,而脏页面不会立即更新到磁盘中,而是由操作系统统一调度,如由专门的flusher内核线程在满足一定条件时(如一定时间间隔、内存中的脏页面达到一定比例)将脏页面同步到磁盘上。**因此如果服务器在write之后、磁盘同步之前宕机,则数据会丢失。这时需要调用操作系统提供的fsync功能来确保文件所有已修改的内容被正确同步到磁盘上。

Elasticsearch提供了几个参数配置来控制Translog的同步:

  • index.translog.durability

    该参数控制如何同步Translog数据。有两个选项:

    • request(默认):每次请求(包括index、delete、update、bulk)都会执行fsync,将Translog的数据同步到磁盘中。
    • async:异步提交Translog数据。和下面的index.translog.sync_interval参数配合使用,每隔sync_interval的时间将Translog数据提交到磁盘,这样一来性能会有所提升,但是在间隔时间内的数据就会有丢失的风险。
  • index.translog.sync_interval:该参数控制Translog的同步时间间隔。默认为5秒。

  • index.translog.flush_threshold_size:该参数控制Translog的大小,默认为512MB。防止过大的Translog影响数据恢复所消耗的时间。一旦达到了这个大小就会触发flush操作,生成一个新的Translog

下面我们来看一看涉及到Translog的操作。

参考:

[1] blog.wangqi.love/articles/El…

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享