fightclub

一次zabbix前端优化调整

最近经常发现zabbix的dashbord的最近20个问题以及系统状态的刷新偶尔会多几秒延时,圈圈转啊转,让人很不爽,于是决定折腾一下。

最开始着手的就是数据库。

系统参数调整

最初的时候我把postgre中的fsync关闭,并且将数据最为庞大的各个history和trends的分区改成了unlogged模式。效果非常明显。

那么下一步就是对一些常规的系统参数进行精细调整,毕竟很多都是简单有效的方法。

文件系统调整

之前不知道看过哪个评测报告,说对于随机读写,ext3是要超过ext4的。虽然不知道真假,但是还是秉着宁可信其有的方式使用了的ext3分区。

对于文件系统最常见的优化就是关闭atime模式和设置更快的日志模式。

开启atime功能意味着在读取数据的时候也会造成inode的刷新,而inode的刷新也会带来文件系统日志的变更。而atime一般来说是没多大作用,关闭atime也不会出现什么不良反应,属于首先开刀的地方。

其次是日志模式。默认的ordered方式即使先写用户数据再写元数据的模式,也就是和windows一样。当然也有更慢的更安全的journal模式和更快却没那么安全的writeback模式。我的做法是把history和trends单独的表空间采取writeback模式,也算是一种折中。当然也可以完全关闭日志,不过这就太过分了。

再次是关闭jdb barrier。按照ext3 manpage的说法:

1
2
3
4
5
6
7
8
9
10
11
12
barrier=0 / barrier=1
This disables / enables the use of write barriers in the jbd code.
barrier=0 disables, barrier=1 enables (default).
This also requires an IO stack which can support barriers,
and if jbd gets an error on a barrier write,
it will disable barriers again with a warning.
Write barriers enforce proper on-disk ordering of journal commits,
making volatile disk write caches safe to use,
at some performance penalty.
If your disks are battery-backed in one way or another,
disabling barriers may safely improve performance.

jdb barrier确保日志的提交顺序。如果磁盘的有电源的,可以关闭以提高性能。
对于使用阵列存储的我来说,这个应该不必担心,关闭即可。

另外,文件系统的块大小应该也是会存在影响的。因为数据库的页面以8K为基本单位,而文件系统默认以4K为基本单位。按照直觉的,应该是修改成一致性应该会好一些。不过没亲测过,就暂缓调整了。

内核调整

对于数据库来讲,内存的调整应该是最为主要的。

为了防止页面换出,我直接禁用了交换分区。把后台的pdflush脏页回写频度调高以优化可能的fsync造成的性能影响。实际上,我应该先用strace统计一下。这个地方可以mark一下,有空再试。

对于postgre文档中建议的共享内存段以及信号量的调整,我现在没看到有什么问题,就没去调整。

最后,我将postgre的shard buffer的内存页面大小修改成2M的大页面。理论上讲,减少页面的数量能增加TLB的命中率,更高效的利用cpu缓存。对于数据库这种需要大块分配内存的应用来讲是有利的。

我的数据库内存是16G,按照官方文档中建议的方法将postgre共享内存设置成所有内存的1/4。也就是4G,其他部分用作操作系统的透明文件缓存。因为还有一些零碎的内存,所以整体需要的大页面数量,会比4G更多一些。

官方文档中的建议的方法是取master进程统计的vm峰值, 即:

1
cat /proc/$(head -1 /data/postmaster.pid)/status | grep -i vmpeak

再除以页面大小,即:

1
grep -i hugepagesize /proc/meminfo

得到的值就是nr_hugepages的值。

另外,官方文档还建议关闭操作系统的透明大页面功能。并将postgre的大页面设置成on,这样就可以让postgre独享大页面了。

我做了一个systemd一次性启动服务来实现这个需求。虽然直接用rc.local的方式更为简单,但是systemd不建议继续使用rc.local,就按照新标准来吧。

1
2
3
4
5
6
#!/usr/bin/env sh
# 禁用透明大页面
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
1
2
3
4
5
6
7
8
9
10
11
12
13
[Unit]
Description = Disable transparent huge page
ConditionPathExists = /sys/kernel/mm/transparent_hugepage/enabled
ConditionPathExists = /sys/kernel/mm/transparent_hugepage/defrag
Before = postgresql.service
[Service]
ExecStart = /usr/sbin/disable_tran_huge_page.sh
Type = oneshot
RemainAfterExit = yes
[Install]
WantedBy=multi-user.target

表索引调整

把基础环境调整后,开始进入正题。调整具体的功能还是要从最直接的地方开刀。也就是找到两个功能卡的对应的表和sql。

表很容易猜,而sql除了查找php的源代码,其实还有更简单的方法。

在postgre中,可以使用pg_stat_statements插件,对sql的执行情况进行统计。把统计视图中调用次数最多的10个sql拉出来看看,就能查到是哪个sql有问题了。

1
select * from pg_stat_statements order by calls desc limit 10;

果然,排名第一的就是events表。语句模板为

1
select eventid,source,object.objectid,clock,value,acknowledged,ns from events where eventid = ?

最慢的执行时间为7051ms,简直弱爆了。

这张表的eventid为主键,有个默认的btree索引,但是是非聚集的。

因为可以想象的是,这个搜索的条件带有明显的聚集特性。因此,把整个表改成按照eventid进行聚集索引,是个不错的主意。为了验证这个结论,我在测试服务器上插入了1000w的模拟数据并且用pgbench进行测试,1000w行的数据在集中搜索100行数据的情况下聚集索引大概快了20%,效果还是挺明显的。

postgre中的聚集索引创建方法为

1
cluster events using events_pkey;

注意运行完cluster后要使用analyze进行重新统计,否则规划器会生成非常差劲的执行计划。
另外,需要定期运行cluster命令以更新聚集索引。

尾声

一番折腾,不知道是不是心理作用,感觉似乎快了一些。下一步可以折腾一下php。也许可以换个php 7试试看。