一、先说清楚问题:为什么磁盘会成为Cassandra的瓶颈
咱们平时用 Cassandra,最怕的就是写入突然变慢,甚至时不时地抖一下。很多人第一反应是“机器负载高了”,结果一看 CPU、内存都挺正常,最后才发现是磁盘在拖后腿。磁盘就像一个仓库管理员,你不停地往仓库里送东西,他收货、上架的速度跟不上,前面排队的人自然就着急了。Cassandra 的写入路径上,数据会先写一份到 CommitLog(相当于临时记账本),再写内存表,然后定期刷到真正的数据文件里。如果记账本和数据文件都放在同一块磁盘上,那就等于一个人既要记账又要搬货,忙不过来的时候写入就会卡顿。更麻烦的是,一旦磁盘出现故障或者老化,那种忽快忽慢的“延迟抖动”非常难排查。所以,要想让 Cassandra 跑得稳,就得从最底层的磁盘开始认真调。
二、SSD选型:别只看容量,要看IOPS和寿命
2.1 SSD关键参数怎么看
很多朋友买 SSD 只关心容量多大、价格多少,其实对 Cassandra 这种写密集的场景来说,更重要的是随机写能力(IOPS)和寿命。IOPS 就是每秒钟能执行多少次读写操作,Cassandra 的 CommitLog 是顺序写,数据刷盘是随机写,两者对 IOPS 的要求都不低。如果选了便宜的 QLC 盘,平时用着还行,一旦刷盘高峰来了,写入速度就可能掉到让你怀疑人生。
寿命要看两个指标:TBW(总写入字节数)和 DWPD(每天全盘写入次数)。假设你有一块 1TB 的盘,DWPD 是 1,表示每天可以写 1TB 数据。Cassandra 节点如果写入量大,一天的写入量可能是容量的一倍甚至更多,这时候 DWPD 只有 0.3 的盘就很容易提前报废。
2.2 选型建议与注意事项
我的建议是,生产环境至少选择企业级 NVMe SSD,比如 Intel、三星、美光这些品牌的企业级产品。企业级盘在掉电保护、磨损均衡和性能一致性上比消费级强太多。另外,不要盲目追求最新型号,稳定性比参数更重要。可以先在测试环境跑一下 fio 工具,模拟 Cassandra 的写入模式,看看在高负载下 IOPS 会不会大幅波动。下面是一个用 fio 测试随机写的小例子,大家可以直接复制到服务器上跑。
# 技术栈:Shell + fio
# 先创建一个测试目录,比如 /data/fio_test
mkdir -p /data/fio_test
cd /data/fio_test
# 用 fio 模拟 4K 随机写,队列深度 32,持续 60 秒
# --direct=1 表示绕过文件系统缓存,直接测磁盘
# --rw=randwrite 表示随机写
# --bs=4k 表示每次写 4KB
# --size=2G 表示每个线程写 2GB 的数据
# --runtime=60 表示跑 60 秒
# --group_reporting 表示汇总所有线程的报告
fio --name=randwrite_test \
--ioengine=libaio \
--direct=1 \
--rw=randwrite \
--bs=4k \
--size=2G \
--numjobs=4 \
--iodepth=32 \
--runtime=60 \
--group_reporting
# 跑完以后,重点关注 iops 和 clat 这两行
# iops 如果保持在几万以上,说明盘还行
# clat 如果出现大量超过几十毫秒的尾巴,说明延迟抖动严重
跑这个测试的时候,最好让测试文件大小比内存大一些,不然缓存会影响结果。另外,测试完记得清理一下测试文件,别占着磁盘空间。
三、文件系统挂载参数:小细节大作用
3.1 常用文件系统对比
Cassandra 官方推荐用 ext4 或 XFS。ext4 比较成熟,默认参数就能用;XFS 在并发写入和扩展性上更好,很多生产环境都在用。但不管用哪个,挂载参数都得好好调。很多人直接挂载的时候啥也不写,结果默认参数下 atime 更新、barrier 开启,这些都会增加不必要的写操作,无形中拖慢速度。
3.2 ext4/xfs挂载参数实战
以 XFS 为例,常见优化是关闭 atime 更新、开启 no barrier(或者现在的 nofail 情况),减少一些内存缓存刷新动作。不过要注意,关闭 barrier 在电源不稳定时有数据损坏风险,所以如果机房断电保护做得不好,还是别关。另一个重要参数是分配策略,XFS 的 allocsize 设置为 1MB 甚至更大,可以减少文件碎片。下面展示一个挂载数据盘并应用到 Cassandra 上的过程,注意每一步都有注释。
# 技术栈:Shell + mkfs.xfs + mount
# 假设你的数据盘在 /dev/sdb,先格式化
# 这里的参数解释:
# -f 表示强制格式化,之前有分区表也没关系
# -d agcount=4 表示分配组数量,大磁盘可以适当增加
sudo mkfs.xfs -f -d agcount=4 /dev/sdb
# 创建挂载点
sudo mkdir -p /data/cassandra
# 挂载时设置优化参数
# noatime:不更新文件访问时间,减少额外写IO
# nodiratime:不更新目录访问时间,也是减少写IO
# allocsize=1m:每次分配1MB空间,减少碎片
# 重点:不要用 nobarrier,除非你很清楚后果
sudo mount -t xfs -o noatime,nodiratime,allocsize=1m /dev/sdb /data/cassandra
# 查看挂载是否成功,以及参数是否生效
sudo mount | grep /data/cassandra
# 如果你想让重启后自动挂载,需要写入 /etc/fstab
# 注意用 UUID 而不是设备名,因为设备名可能会变
# 先获取 UUID
sudo blkid /dev/sdb
# 然后把类似下面这样一行加到 /etc/fstab 里
# UUID=你的UUID /data/cassandra xfs noatime,nodiratime,allocsize=1m 0 0
挂载好了以后,记得用 df -h 确认一下空间,再用 dd 或者 fio 快速验证一下读写是否正常。别小看这几个参数,实际生产环境里,把 atime 关掉后,磁盘写压力能减少好几个百分点,尤其是小文件特别多的场景。
四、CommitLog分离部署:让日志和数据的道路分开
4.1 CommitLog为什么要分开
前面说了,CommitLog 是顺序写,数据刷盘是随机写。如果两者在同一块盘上,顺序写会被随机写拖累,随机写也会因为顺序写的占用而变得更慢。这就像一条双向两车道的路,大货车和小轿车挤在一起,谁也跑不快。所以最直接的办法是,给 CommitLog 单独准备一块物理盘(或者一个独立的 RAID 卷),让它们各走各的道。这样一来,写入请求先快速落到 CommitLog 上,就能立刻返回成功,大大降低延迟抖动。
4.2 具体部署方法
部署方法其实很简单,核心就是修改 Cassandra 的配置文件 cassandra.yaml。需要把 commitlog_directory 指向新的盘,同时把数据目录 data_file_directories 指向原来的数据盘。如果你还要把 hints 和 saved_caches 也分开,那么可以一并配置。下面是一个完整的操作步骤。
# 技术栈:Shell + Cassandra配置修改
# 假设你已经给 commitlog 买了一块新盘 /dev/sdc
# 先格式化并挂载到 /data/commitlog
sudo mkfs.xfs -f /dev/sdc
sudo mkdir -p /data/commitlog
sudo mount -t xfs -o noatime,nodiratime,allocsize=1m /dev/sdc /data/commitlog
# 然后修改 cassandra.yaml
# 通常路径是 /etc/cassandra/cassandra.yaml
# 先备份原始文件
sudo cp /etc/cassandra/cassandra.yaml /etc/cassandra/cassandra.yaml.bak
# 用 sed 修改,也可以手动 vi 编辑
# 把 commitlog_directory 改成 /data/commitlog
sudo sed -i 's|commitlog_directory:.*|commitlog_directory: /data/commitlog|' /etc/cassandra/cassandra.yaml
# 确保 data_file_directories 指向数据盘
# 如果你的数据目录在 /data/cassandra/data,配置就是:
# data_file_directories:
# - /data/cassandra/data
# 这里用 grep 检查一下
sudo grep -n "commitlog_directory\|data_file_directories" /etc/cassandra/cassandra.yaml
# 修改完后,需要把原来的 commitlog 目录里的旧文件复制到新位置
# 注意先停 Cassandra
sudo systemctl stop cassandra
# 如果你的原 commitlog 目录在 /data/cassandra/commitlog
# 先复制过去,保持文件权限
sudo cp -a /data/cassandra/commitlog/* /data/commitlog/
# 然后清空原 commitlog 目录,防止重复读取
sudo rm -rf /data/cassandra/commitlog/*
# 最后启动 Cassandra
sudo systemctl start cassandra
# 用命令查看 commitlog 目录是不是新盘
sudo df -h /data/commitlog
这里面最容易被忽略的是权限问题。Cassandra 进程通常以 cassandra 用户运行,所以新挂载的目录必须保证 cassandra 用户有读写权限。记得用 chown 修改属主,不然后面启动会失败。
# 技术栈:Shell
# 修改 /data/commitlog 属主为 cassandra 用户
sudo chown -R cassandra:cassandra /data/commitlog
# 数据盘目录也一样处理
sudo chown -R cassandra:cassandra /data/cassandra/data
五、综合调优示例:一套可落地的配置脚本
单点优化做完了,我们再串起来看一个完整的自动化脚本。这个脚本可以帮你完成从磁盘格式化、挂载、目录创建、配置修改到重启服务的全过程。注意,这个脚本需要在新的数据盘上执行,并且假设你的 Cassandra 版本是 4.x。
# 技术栈:Shell
#!/bin/bash
# 这个脚本假设:
# 数据盘是 /dev/sdb
# commitlog 盘是 /dev/sdc
# Cassandra 数据目录是 /data/cassandra
# 使用 ext4 文件系统,因为 ext4 更通用
set -e # 遇到错误立即退出
echo "=== 开始格式化数据盘和 commitlog 盘 ==="
sudo mkfs.ext4 -F /dev/sdb
sudo mkfs.ext4 -F /dev/sdc
echo "=== 创建挂载点 ==="
sudo mkdir -p /data/cassandra
sudo mkdir -p /data/commitlog
echo "=== 挂载并设置参数 ==="
# ext4 常用优化:noatime,nodiratime,barrier=0
# 注意:barrier=0 适合有 UPS 的机房,如果你不确定,不要加
sudo mount -o noatime,nodiratime,barrier=0 /dev/sdb /data/cassandra
sudo mount -o noatime,nodiratime,barrier=0 /dev/sdc /data/commitlog
echo "=== 写入 fstab 保证重启生效 ==="
# 这里用 UUID 更稳妥,简单起见直接写设备名,实际生产请用 UUID
echo '/dev/sdb /data/cassandra ext4 defaults,noatime,nodiratime,barrier=0 0 0' | sudo tee -a /etc/fstab
echo '/dev/sdc /data/commitlog ext4 defaults,noatime,nodiratime,barrier=0 0 0' | sudo tee -a /etc/fstab
echo "=== 设置目录属主 ==="
sudo mkdir -p /data/cassandra/data
sudo mkdir -p /data/commitlog
sudo chown -R cassandra:cassandra /data/cassandra
sudo chown -R cassandra:cassandra /data/commitlog
echo "=== 修改 Cassandra 配置 ==="
# 备份配置
sudo cp /etc/cassandra/cassandra.yaml /etc/cassandra/cassandra.yaml.bak
# 修改 commitlog 路径
sudo sed -i 's|commitlog_directory:.*|commitlog_directory: /data/commitlog|' /etc/cassandra/cassandra.yaml
# 修改数据目录路径,这里简单地把原数据目录字符串替换
sudo sed -i 's|/var/lib/cassandra/data|/data/cassandra/data|' /etc/cassandra/cassandra.yaml
echo "=== 重启 Cassandra ==="
sudo systemctl restart cassandra
echo "=== 验证状态 ==="
sudo systemctl status cassandra --no-pager
sudo df -h /data/cassandra /data/commitlog
这个脚本虽然能用,但有几个地方要注意:第一,如果是已有数据的旧节点,不能简单删数据,应该先做快照或者迁移;第二,不同操作系统挂载参数写法可能略有差别;第三,fstab 里的设备名在重启后可能变化,一定要用 UUID。下面给你一个获取 UUID 并写入 fstab 的补充示例。
# 技术栈:Shell
# 获取所有磁盘的 UUID
sudo blkid /dev/sdb /dev/sdc
# 假设你得到的 UUID 分别是 U1 和 U2,那么写入 fstab 的正确姿势是:
# UUID=U1 /data/cassandra ext4 defaults,noatime,nodiratime,barrier=0 0 0
# UUID=U2 /data/commitlog ext4 defaults,noatime,nodiratime,barrier=0 0 0
六、应用场景、优缺点与注意事项
6.1 应用场景
这套调优方案非常适合以下几种场景:一是 Cassandra 集群的写入压力大,经常出现写入超时;二是业务对延迟敏感,比如实时推荐、风控系统,延迟抖动会影响用户体验;三是节点数量较多,磁盘故障率升高,希望通过更好的隔离降低单点风险。尤其是日志类、时序类数据,因为写入量大而且从不清除,必须提前做好磁盘规划。
6.2 技术优缺点
优点很直接:写入延迟更稳定,因为 CommitLog 和刷盘不再抢资源;磁盘故障影响面变小,如果数据盘坏了,CommitLog 还在,至少能减少数据丢失风险;整体吞吐量也能提升,因为两件事可以并行做。
缺点也别忘了:成本增加,需要额外一块盘;运维复杂一点,需要维护两个挂载点和配置;如果配置不当(比如权限错、路径错),会导致 Cassandra 启动失败。另外,关闭 barrier 虽然能提升性能,但万一断电也可能损坏文件系统,这个风险必须权衡。
6.3 注意事项
第一,千万不要在已有数据的节点上直接改 commitlog 路径然后重启,一定要先备份数据、清空旧 commitlog,否则可能启动报错。第二,监控要跟上,建议为 commitlog 盘和数据盘分别配置磁盘使用率、IO 等待时间和 I/O 错误告警。第三,定期检查文件系统健康状态,SSD 磨损到后期会出现大量坏块,提前在 SMART 信息里能看到。第四,Cassandra 版本不同配置项名称可能略有差异,修改前查一下官方文档。第五,如果使用的是云主机,虚拟盘的性能不一定稳定,最好选择有明确 IOPS 保证的云盘类型。
七、总结
Cassandra 磁盘调优这件事,说复杂也复杂,说简单也简单。核心思路就是:选一块好盘(SSD 要关注 IOPS 和寿命),挂载时设置合理的参数(关掉 atime,谨慎使用 barrier),然后把 CommitLog 和数据文件分开部署。这三点做好了,大部分写入延迟抖动和磁盘故障带来的麻烦都能被你挡在门外。上面给出的 fio 测试、挂载参数和分离部署脚本,都是可以直接拿去用的,但一定要根据你自己的环境做调整。生产环境没有银弹,每一项改动都要经过测试和监控验证。记住,磁盘是 Cassandra 最忠实的伙伴,你对它好一点,它就不给你脸色看。
评论
围绕“生产环境Cassandra磁盘I/O性能调优深入SSD选型文件系统挂载参数与CommitLog分离部署策略解决写入延迟抖动和磁盘故障”参与讨论