Cassandra IO吞吐量优化实践


之前在某个集群已经碰到过同样的问题,业务查询是单行查询,系统IOPS不高,但IO吞吐量先达到磁盘瓶颈,IO使用率100%,导致数据库整体性能上不去。此集群查询量增加后,尤其是用户表查询迁移过来后,虽然业务查询是走分区键的单行查询,但是操作系统的IO吞吐量同样先达到了瓶颈。IO使用率接近100%。

针对这个问题,首先尝试在系统IO能力上做了优化,将磁盘数量从两个分拆到四个,理论上IO吞吐能力增加了1倍。但是这种优化治标不治本,随着查询量增长,很快IO使用率又接近了瓶颈。

最近几天,对IO吞吐量大的问题进行了深入分析和测试,明确了原因。

查询官方文档,分析Cassandra一次读取操作的过程如下:

几个概念:

Cassandra的表默认是启用压缩的,默认压缩参数如下:

compression = {'chunk_length_in_kb': '64', 'class':'org.apache.cassandra.io.compress.LZ4Compressor'}

默认会使用LZ4压缩算法做分块压缩,默认是64K一个块。那么每次读取sstable的数据,最少必须读取64K,然后解压整个块,才能从中找到某个分区。chunk_length_in_kb,这个参数控制压缩块大小,块越大,压缩比越大。

从原理上分析,分块压缩这块肯定会导致单次IO较大,单行查询可能只需几B的数据返回,但落到磁盘也至少是32K(64K压缩后)的数据读取,会导致磁盘吞吐量高。

由于Linux操作系统的页大小是4KB,一次IO最小是4KB。在测试环境修改压缩参数,将表的块大小修改为4KB,并做一次合并操作,数据重新压缩写入新的sstable文件。对优化后的表进行单行读取压测,发现效果显著。于是对线上数据库查询最多的用户表也做了优化操作,第二天观察发现效果不大。

继续分析可能导致单次IO较大的原因。对操作系统相关参数检查,发现几块SSD磁盘预读大小均配置为4096,也就是4M。

cat /sys/class/block/vdc/queue/read_ahead_kb
4096

为了提高I/O性能,Linux操作系统使用了readahead(预读)技术,该技术假定后续读取可能需要文件中的某些数据,因此会将该文件中的更多内容读入内存,而不只是所请求的部分。较高的 readahead 值可增加吞吐量,但是会占用更多内存和 IOPS。较低的 readahead 值可增加 IOPS,但是会牺牲吞吐量。对于随机访问数据较多的场景,一般是建议设置较低的readahead值;而顺序访问数据较多,需优化吞吐量的场景(如Hadoop作业),更适合设置较大的readahead值。

了解了readahead的特点后,重点怀疑readahead值设置较大会导致IO吞吐量放大。线上数据库调整了一个节点的配置,第二天发现调整过的节点IO吞吐量下降明显。在查询QPS没有明显变化的情况下,IO吞吐量从前两天的500MB/s降到了13MB/s,IO使用率、IOPS、CPU使用率、系统负载等指标均有不同程度下降。后续对其他节点做同步优化,将整个数据库集群负载降低到一个非常安全的水平。

至此,此Cassandra集群IO吞吐量放大问题原因明确,主要是受readahead参数和表压缩参数配置的影响。当前线上数据库这两个参数均使用默认值,数据库还是需要针对不同业务场景做针对性的优化。