解决TiKV节点JBD2进程I/O占用高的问题
今天事情甚多,草草写一篇,尽量赶在1点之前休息。
问题
我们的TiDB集群上线以来,所有TiKV节点的所有磁盘I/O Util指标总是偏高,大多数时候在50%以上,极端情况下接近90%。集群采用的阿里云ECS配备的都是PL2级别的ESSD云盘(标称吞吐量峰值750MB/s、10w IOPs,且添加了挂载参数noatime
和nodelalloc
),主要负载是通过DM同步与简单查询50+张业务库大表,理论上不应该出现如此高的I/O占用。Grafana反映出的监控数据如下,IOPs和写延迟均正常。
思考
使用iotop
命令观察到名为jbd2的进程I/O百分比很大,远大于tikv-server,但是实际读写量又很小。(vdc、vdd、vde是单机3个TiKV实例专用的硬盘)
jbd2是什么鬼?Wikipedia的简介如下:
JBD, or journaling block device, is a generic block device journaling layer in the Linux kernel written by Stephen Tweedie from Red Hat. JBD is filesystem-independent. ext3, ext4 and OCFS2 are known to use JBD.
JBD exists in two versions, JBD and JBD2. JBD was created with ext3 in 1998. JBD2 was forked from JBD in 2006 with ext4, with the goal of supporting a 64-bit (as opposed to 32-bit-only in JBD) block number.
JBD是日志块设备(journaling block device)的简称,位于文件系统层和块设备驱动层之间。它负责记录块设备的操作日志,并提供原子性、事务性地将操作刷写到设备的能力。也就是说,它能够保证操作系统失败时,文件系统的完整性和一致性不受到破坏(一批原子操作要么全部flush,要么全部不flush),作用有些类似于存储引擎中的WAL或者redo log。ext4文件系统采用的是JBD的一个分支,即JBD2。
首先考虑是不是JBD2本身出了问题?通过查找资料,发现有可能是一个内核bug(参见https://bugzilla.kernel.org/show_bug.cgi?id=39072)。但是它的年代比较久远了,我们所用的内核版本是3.10.0,早已修复,所以继续怀疑是否真的有什么操作导致JDB2频繁进行刷写。
再次观察iotop的输出,发现tikv-server中与raftstore相关的I/O占了主要部分。梳理一下TiKV配置中raftstore的参数,注意到raftstore.sync-log
的嫌疑较大。TiDB官方FAQ中的解读如下。
由上述文字不难推测出,如果sync-log = false
,那么写入Raft log时只会调用write()写入到缓冲区,而不会调用fsync()立即强制持久化。反之,如果sync-log = true
,就会调用fsync()了。StackOverflow上的一个答案可以佐证:
结合JBD2的作用,基本可以确定是sync-log
开关打开导致频繁fsync(),进而造成其I/O占用居高不下。于是我们修改TiKV的配置关掉它,并滚动重启所有TiKV实例,可以发现I/O Util回落到了相对正常的水平。
解决
关闭sync_log非常简单直接,但却是以牺牲数据可靠性作为trade-off的(当然对于云环境而言影响不算大)。如果不想在TiDB层面做改动,则需要调整磁盘挂载参数来变相降低JBD2的负载。例如:
- 在挂载参数中添加
data=writeback
启用ext4的写回日志模式(默认为ordered
),即只产生元数据日志,不产生数据日志,并且不保证数据与元数据落盘的顺序; - 在挂载参数中添加
commit=60
来降低ext4主动提交事务的频率,单位为秒,默认为5秒。
另外,可以用fio
等工具对空盘(注意一定要是空盘)做个fsync IOPs的测试,如果此值过低,应考虑更换性能更高的盘。
The End
看官若不了解Raft Consensus,可以参见笔者很久之前写过的一篇简单总结。
民那晚安晚安。