ext4支持3种DATA模式,用来区分记录journal的行为。
ext4的journal类似于PostgreSQL的XLOG,可以用来做灾难恢复,以及确保数据的一致性。
文件在ext4中分两部分存储,一部分是文件的metadata,另一部分是data。
metadata和data的操作日志journal也是分开管理的。你可以让ext4记录metadata的journal,而不记录data的journal。
这取决于mount ext4时的data参数。
1.
data=journal All data are committed into the journal prior to being
written into the main file system. Enabling
this mode will disable delayed allocation and
O_DIRECT support.
在将data写入文件系统前,必须等待metadata和data的journal已经落盘了。性能最差,并且不支持文件操作的delalloc,O_DIRECT flag (参考 man open)。
当调用fsync时,文件系统的操作包含如下顺序:
fsync(data journal) -> fsync(metadata journal) -> fsync(data) -> fsync(metadata)
lock metadata long time between fsync(metadata journal) and fsync(metadata)?
2.
data=ordered (*) All data are forced directly out to the main file
system prior to its metadata being committed to the
journal.
这个模式不记录data的journal,只记录metadata的journal日志,但是在写metadata的journal前,必须先确保data已经落盘。
当调用fsync时,文件系统的操作包含如下顺序:
fsync(metadata journal) -> fsync(data)(确保data先落盘) -> fsync(metadata)
lock metadata long time between fsync(metadata journal) and fsync(metadata)?
3.
data=writeback Data ordering is not preserved, data may be written
into the main file system after its metadata has been
committed to the journal.
不记录data journal,仅记录metadata journal。并且不保证data比metadata先落盘。
当调用fsync时,文件系统的操作包含如下顺序:
fsync(metadata journal) -> fsync(metadata)
另外需要注意metadata的操作在单个ext4文件系统中是串行的,所以如果某个用户的metadata操作堵塞了的话,会影响所有人操作同一个文件系统的metadata。
即使使用writeback,也会有这样的情况发生,例如某个用户疯狂的写metadata的(例如大批量的创建小文件,调用fsync)。
典型的例子:
fsync(data)这一步如果很慢,会导致其他人写metadata等待的现象(写metadata包括很多,例如创建新文件,读写方式打开文件(改变文件大小))。
查看进程栈:
10249正在fsync:
10255正在创建新文件,被堵塞:
[ 解决思路 ]
1. 通过调整内核刷dirty page的比例和唤醒时间,可以让内核频繁的收回脏页,从而降低以上问题出现的概率,不过这种做法有利有弊。
相关的参数:
相关进程:
在调整前,请务必了解这些参数的含义,以及Linux是如何处理的。
系统缓存相关的几个内核参数 (还有2个是指定bytes的,含义和ratio差不多):
1. /proc/sys/vm/dirty_background_ratio
该文件表示脏数据到达系统整体内存的百分比,此时触发pdflush进程把脏数据写回磁盘。
缺省设置:10
当用户调用write时,如果发现系统中的脏数据大于这阈值(或dirty_background_bytes ),会触发pdflush进程去写脏数据,但是用户的write调用会立即返回,无需等待。pdflush刷脏页的标准是让脏页降低到该阈值以下。
即使cgroup限制了用户进程的IOPS,也无所谓。
2. /proc/sys/vm/dirty_expire_centisecs
该文件表示如果脏数据在内存中驻留时间超过该值,pdflush进程在下一次将把这些数据写回磁盘。
缺省设置:3000(1/100秒)
3. /proc/sys/vm/dirty_ratio
该文件表示如果进程产生的脏数据到达系统整体内存的百分比,此时用户进程自行把脏数据写回磁盘。
缺省设置:40
当用户调用write时,如果发现系统中的脏数据大于这阈值(或dirty_bytes ),需要自己把脏数据刷回磁盘,降低到这个阈值以下才返回。
注意,此时如果cgroup限制了用户进程的IOPS,那就悲剧了。
4. /proc/sys/vm/dirty_writeback_centisecs
该文件表示pdflush进程的唤醒间隔,周期性把超过dirty_expire_centisecs时间的脏数据写回磁盘。
缺省设置:500(1/100秒)
系统一般在下面三种情况下回写dirty页:
1. 定时方式: 定时回写是基于这样的原则:/proc/sys/vm/dirty_writeback_centisecs的值表示多长时间会启动回写线程,由这个定时器启动的回写线程只回写在内存中为dirty时间超过(/proc/sys/vm/dirty_expire_centisecs / 100)秒的页(这个值默认是3000,也就是30秒),一般情况下dirty_writeback_centisecs的值是500,也就是5秒,所以默认情况下系统会5秒钟启动一次回写线程,把dirty时间超过30秒的页回写,要注意的是,这种方式启动的回写线程只回写超时的dirty页,不会回写没超时的dirty页,可以通过修改/proc中的这两个值,细节查看内核函数wb_kupdate。
2. 内存不足的时候: 这时并不将所有的dirty页写到磁盘,而是每次写大概1024个页面,直到空闲页面满足需求为止
3. 写操作时发现脏页超过一定比例:
当脏页占系统内存的比例超过/proc/sys/vm/dirty_background_ratio 的时候,write系统调用会唤醒pdflush回写dirty page,直到脏页比例低于/proc/sys/vm/dirty_background_ratio,但write系统调用不会被阻塞,立即返回.
当脏页占系统内存的比例超/proc/sys/vm/dirty_ratio的时候, write系统调用会被被阻塞,主动回写dirty page,直到脏页比例低于/proc/sys/vm/dirty_ratio
大数据量项目中的感触:
1 如果写入量巨大,不能期待系统缓存的自动回刷机制,最好采用应用层调用fsync或者sync。如果写入量大,甚至超过了系统缓存自动刷回的速度,就有可能导致系统的脏页率超过/proc/sys/vm/dirty_ratio, 这个时候,系统就会阻塞后续的写操作,这个阻塞有可能有5分钟之久,是我们应用无法承受的。因此,一种建议的方式是在应用层,在合适的时机调用fsync。
2 对于关键性能,最好不要依赖于系统cache的作用,如果对性能的要求比较高,最好在应用层自己实现cache,因为系统cache受外界影响太大,说不定什么时候,系统cache就被冲走了。
3 在logic设计中,发现一种需求使用系统cache实现非常合适,对于logic中的高楼贴,在应用层cache实现非常复杂,而其数量又非常少,这部分请求,可以依赖于系统cache发挥作用,但需要和应用层cache相配合,应用层cache可以cache住绝大部分的非高楼贴的请求,做到这一点后,整个程序对系统的io就主要在高楼贴这部分了。这种情况下,系统cache可以做到很好的效果。
2. 另一种思路是,先调用fdatasync,再调用fsync,来降低堵塞时间。fdatasync某些场景是不需要刷metadata的,但是某些场景依旧需要刷metadata(例如涉及到文件大小的改变),所以这个解决办法并不适用于所有场景。
最终的思路都是让刷metadata尽量快,尽量避免刷metadata时其data还没有刷到磁盘,等待刷其data的时间。
在某些应用场景,例如使用cgroup限制了IOPS的场景,需要非常注意。(这种场景下,内核进程刷脏页就非常有效,因为一般不限制内核进程的IOPS)。
详解:
ext4支持使用额外的journal设备来存储journal,如果你确定要这么做的话,建议使用IOPS性能好的块设备作为journal设备。
这和数据库的REDO日志要使用低延迟的块设备是一样的道理。
mkfs.ext4时可以指定journal设备。
3. man open
2.6.33之前的内核不支持o_dsync,所以只能编程时使用先fdatasync再fsync的方式。
4. man fsync
fsync会将脏data和metadata都刷到磁盘,fdatasync则只将脏data刷到磁盘(某些情况下也刷metadata)。
所以我们可以在调用fsync前,先调用fdatasync,将脏data刷到磁盘,再调用fsync,减少锁metadata的时间。
|
|