WAL
、Page 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/…
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:每次提交都将
binlog
从binlog
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 Buffer
→OS 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
组成。
Segment
是最小的数据存储单元。其中包含了文档中的词汇字典、词汇字典的倒排索引以及Document的字段数据。因此Segment
直接提供了搜索功能。但是Segment
能提供搜索的前提是数据必须被提交,即文档经过一系列的处理之后生成倒排索引等一系列数据。可以想见这个过程是比较耗时的。因此Elasticsearch
并不会每接收到一条数据就提交到一个Segment
中,一方面是因为这样耗时太长,另一方面是这样会生成巨量的Segment
,降低了IO性能。
Elasticsearch
采取的机制是将数据添加到lucene
,lucene
内部会维护一个数据缓冲区,此时数据都是不可搜索的。每隔一段时间(默认为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
的操作。
参考: