前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >性能分析之又见jbd2引起IO高

性能分析之又见jbd2引起IO高

作者头像
高楼Zee
发布2019-07-17 17:48:28
20.7K0
发布2019-07-17 17:48:28
举报
文章被收录于专栏:7DGroup7DGroup

之前遇到过jbd2引起IO高的问题,直接关掉了日志的功能解决的。写了一个文章,但写的不够细。最近又见类似问题,这里重新整理下对jbd2的内容。

什么原因会导致jbd2引起IO高?

  1. 磁盘满.
  2. 系统bug;所知bug号:Bug 39072 - jbd2 writes on disk every few seconds。
  3. 即使没有以上问题。在ext4上有一个新加入的参数barrier,是用来保证文件系统的完整性的。 [Barrier解释](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/block/barrier.txt?id=09d60c701b64b509f328cac72970eb894f485b9e)。 这个值默认是1,即是打开状态。在这个状态下,打开jbd2也是会导致性能下降的,这个玩意的设计逻辑就是为了损失掉性能保证文件完整性。 这是个选择题,要么不用它,要么性能差。 但是这个功能不能和设备映射器同时使用,也即是,如果你使用了逻辑卷、软RAID、多路径磁盘,则这个值不生效。

jbd2是个什么?

代码语言:javascript
复制
The Journaling Block Device (JBD) provides a filesystem-independent interface for filesystem journaling. ext3, ext4 and OCFS2 are known to use JBD. OCFS2 starting from Linux 2.6.28[1] and ext4 use a fork of JBD called JBD2.[2]

文件系统的日志功能,jbd2是ext4文件系统版本。

检查是否存在jbd2进程

代码语言:javascript
复制
[root@7dgroup2 ~]# ps -ef|grep jbd2
root       267     2  0 Aug21 ?        00:06:17 [jbd2/vda1-8]
root     24428 22755  0 09:48 pts/0    00:00:00 grep --color=auto jbd2
[root@7dgroup2 ~]#

检查文件系统的功能

代码语言:javascript
复制
[root@7dgroup2 ~]# dumpe2fs /dev/vda1 | grep has_journal
dumpe2fs 1.42.9 (28-Dec-2013)
Filesystem features:      has_journal ext_attr resize_inode dir_index filetype needs_recovery sparse_super large_file
[root@7dgroup2 ~]#

存在has_journal。

问题现象

在使用iotop看的时候,会有如下信息出现。

代码语言:javascript
复制
Total DISK READ: 46.15 M/s | Total DISK WRITE: 8.24 K/s
  TID  PRIO  USER    DISK READ  DISK WRITE  SWAPIN    IO>    COMMAND
 4036 be/4 search  56.87 K/s  26.45 K/s  0.00 % 87.64 % [jbd2/dm-0-4]

解决方案一

关闭日志功能。

代码语言:javascript
复制
tune2fs -o journal_data_writeback /dev/vda1
tune2fs -O "^has_journal" /dev/vda1
e2fsck -f /dev/vda1

如果使用tune2fs时候,提示disk正在mount,如果是非系统盘下,你可以使用

代码语言:javascript
复制
fuser -km /home #杀死所有使用/home下的进程
umount /dev/vda1 #umount

之后在使用上面的命令进行移除has_journal。

解决方案二

如果是bug的话,可以用这种方式解决。如果是不是bug,这种方式也解决不了,所以要先判断下引起问题的原因再选择解决方案。

升级系统内核。

代码语言:javascript
复制
yum update kernel

解决方案三

禁用Barrier的同时修改commit的值。这个方式可以解决barrier引起的性能下降,但是解决不了系统bug的问题。

修改commit值,降低文件系统提交次数或者禁用barrier特性; 建议文件系统参数为:

代码语言:javascript
复制
defaults,noatime,nodiratime,barrier=0,data=writeback,commit=60

然后重新挂载。

代码语言:javascript
复制
mount -o remount,commit=60 /data

其中barrier=0是禁用barrier特性,commit=60是减少提交次数。 减少提交次数只能缓解。

解决方案四

如果不是bug,并且不想禁用barrier时,用此方式缓解。

想尽办法降低IO,缓解IO压力。这种方式也会导致其他系统资源用不上去。 比如说在mysql中把syncbinlog加大,同时将innodbflushlogattrxcommit增加。 比如说在应用中减少IO的读写。

bug的根源

在之前的版本中出现问题有一个原因是ext4文件系统出现bug。 这个bug出现的比较早了,我看kernel tracker里最早的信息是2011年,如果如果是用的老版本,我建议先做升级。如果没有升级条件,只能用上面的关闭日志功能的解决方案。

bug原因是: 在这段代码中:

代码语言:javascript
复制
int __jbd2_log_start_commit(journal_t *journal, tid_t target)
{
        /*
         * Are we already doing a recent enough commit?
         */
        if (!tid_geq(journal->j_commit_request, target)) {
                /*
                 * We want a new commit: OK, mark the request and wakup the
                 * commit thread.  We do _not_ do the commit ourselves.
                 */
                journal->j_commit_request = target;
                jbd_debug(1, "JBD: requesting commit %d/%d\n",
                          journal->j_commit_request,
                          journal->j_commit_sequence);
                wake_up(&journal->j_wait_commit);
                return 1;
        }
        return 0;
}

中的tid_geq的函数是这样实现的。

代码语言:javascript
复制
static inline int tid_geq(tid_t x, tid_t y)
{
        int difference = (x - y);
        return (difference >= 0);
}

假设jcommitrequest值为2157483647,而target的值为0,看上去 if (!tidgeq(journal->jcommit_request, target))这个判断是不会走的。 但是unsigned int的x减去0之后,转为difference时,difference的定义是int型,此时的结果是多少呢?是-2137483649。 为什么呢?因为unsigned int类型的最大值是2147483647。

代码语言:javascript
复制
printf ("%d.\n", 0x7FFFFFFF);

而2157483647 - 0的这个结果显然溢出了,变成了负数。比如,你可以尝试这样打印。

代码语言:javascript
复制
printf ("%d.\n", 0x8FFFFFFFF);

结果就变成了:-1。 有兴趣的,可以自己写个简单的源码试一下。

代码语言:javascript
复制
#include <stdio.h>

int main( void )
{
        unsigned int x=2157483647;
        unsigned int y=0;
        int diff=0;
        diff = x - y;
        printf ("the diff is %ld.\n", diff);
        return 0;
}

执行之后是什么呢?

代码语言:javascript
复制
the diff is -2117515188..

可见在这种情况下,因为溢出的变量导致if (!tidgeq(journal->jcommit_request, target))走到了。

这个unsigned int的变量是jbd2给每个transaction的tid,tid是一直增加的,因为这个类型容易溢出,所以用tidgeq来判断下,意思是2157483647这个tid已经提交了,所以把1000号的transaction commit掉,于是执行了wakeup(&journal->jwaitcommit);。但是执行之后才发现,原来并没有运行中的事务,于是系统就疯了。 在trace jbd2的可以看到target有0的情况。实际上,大部分的target都不会是0,这个0是因为ialloc.c中的idatasynctid没有正确赋值,所以使用了默认的0。 idatasynctid是在创建inode或者ext4iget()时更新的,如果应用在打开某些文件后就不再关闭,只是一直更新,这时extent树是不变的(ext4使用extent取代了传统的block映射方式),但是jcommit_request随着jbd2日志的提交而不断增加,所以最后这个差值会在业务运行到一定时间之后出现负值。 如果是这个bug引起的话,可以看到的现象是jbd2这个进程长时间占着99%的IO。

影响版本

有此问题的os版本,只根据我使用过的版本列的:

代码语言:javascript
复制
CentOS6.5-64bit
CentOS6.9-64bit

内核版本:

代码语言:javascript
复制
2.6.32-131.0.15.el6.x86_64
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-09-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 7DGroup 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么原因会导致jbd2引起IO高?
  • jbd2是个什么?
  • 检查是否存在jbd2进程
  • 检查文件系统的功能
  • 问题现象
  • 解决方案一
  • 解决方案二
  • 解决方案三
  • 解决方案四
  • bug的根源
  • 影响版本
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档