HOOOS

TiKV Titan 存储引擎应对 SSD 硬件空洞与文件系统碎片的深层优化实践

0 15 存储架构兵器谱 Titan引擎TiKVSSD性能优化
Apple

在 TiDB/TiKV 的大规模生产实践中,为了应对大 Value 带来的写放大问题,我们通常会开启 Titan 存储引擎。Titan 通过 KV 分离(Key-Value Separation)将大 Value 从 LSM-tree 的 SST 文件中剥离,写入独立的 Blob 文件中。

然而,这种设计在解决 LSM-tree 写放大的同时,也引入了新的挑战:随着数据的频繁更新与删除,Blob 文件内部会产生大量的逻辑空洞(Discardable Data)。当 Titan 的垃圾回收(GC)机制、文件系统的空间分配器(Block Allocator)以及 SSD 的闪存转换层(FTL)三者行为失调时,就会引发严重的物理硬件空洞文件系统碎片化

这直接表现为吞吐量骤降、P99 延迟抖动(Write Stall),甚至在磁盘空间充足的情况下频繁触发 I/O 阻塞。本文将从底层技术原理出发,提供一套系统化的深度优化指南。


现象背后的技术本质:为什么会产生空洞与碎片?

要解决问题,必须先理清 Titan、文件系统和 SSD 三者之间的交互链条。

+-------------------------------------------------------+
|                 Titan 存储引擎 (KV分离)                |
|  - 频繁的 Blob 文件 GC 与重写                           |
+-------------------------------------------------------+
                           │
                           ▼ (产生大量稀疏写入与文件截断)
+-------------------------------------------------------+
|               文件系统层 (XFS / ext4)                  |
|  - Inode Extent 树极度碎片化 (逻辑碎片)                 |
+-------------------------------------------------------+
                           │
                           ▼ (频繁触发 discard/fstrim)
+-------------------------------------------------------+
|                SSD 硬件 FTL (闪存转换层)               |
|  - 物理块空洞化、FTL 映射表频繁抖动、触发强 GC          |
+-------------------------------------------------------+
  1. Titan 的逻辑空洞:当用户更新或删除大 Value 时,Titan 不会立即修改物理 Blob 文件,而是直接写入新值。旧值所在的 Blob 文件区域便成了“逻辑空洞”。
  2. 文件系统的逻辑碎片:Titan 依赖 GC 线程异步读取、重写这些 Blob 文件。频繁的创建、追加、重写和删除操作,会导致文件系统(如 XFS)的物理块分配器无法连续分配空间。这就产生了大量的 Extent 碎片(区段碎片)。一个 Blob 文件可能被拆成数万个物理不连续的 Extent,导致读取时产生严重的随机 I/O。
  3. SSD 的物理空洞(FTL 抖动):即使文件系统通过 fallocateTRIM 释放了空间,SSD 的物理 NAND 闪存并不能直接覆盖写入。频繁的空间释放与不连续写入,导致 FTL(Flash Translation Layer)的物理映射表极度稀疏,触发 SSD 内部极其激进的垃圾回收(Garbage Collection),引发写阻滞(Write Stall)

维度一:Titan 引擎层参数调优(源头治理)

优化第一步是调整 Titan 自身的配置,控制 Blob 文件的生命周期,降低对底层文件系统和硬件的冲击。

1. 提高大 Value 判定阈值,防止小 Blob 泛滥

如果 min-blob-size 设置过小(例如几 KB),会导致生成海量的小 Blob 文件。这些小文件不仅让 LSM-tree 失去了管理优势,还极易引发文件系统 Inode 碎片化。

  • 调优建议:生产环境建议将 min-blob-size 至少设为 32KB64KB
[rocksdb.defaultcf.titan]
min-blob-size = "32KB" # 仅对大于该值的数据进行 KV 分离

2. 平滑控制 Titan GC 频率与水线

GC 过于激进会导致严重的写放大,并频繁触发 SSD 物理写入;GC 过于滞后则会导致磁盘空间被空洞占满。我们需要在两者之间寻找平衡。

  • discardable-ratio(触发 GC 的废弃数据比例阈值):默认值通常是 0.5。如果遭遇严重的 SSD 性能抖动,可以适当调高该值(如 0.6),允许保留更多的空洞以换取更低的 GC 频率;如果磁盘空间吃紧,则降低该值(如 0.4)。
  • max-background-gc:限制并发 GC 线程数,防止其抢占正常读写的 I/O 带宽。
[rocksdb.defaultcf.titan]
discardable-ratio = 0.5            # 触发 Blob 文件重写的水线
max-background-gc = 4             # 限制 GC 线程数,避免 I/O 竞争
merge-small-file-threshold = "8MB" # 将分散的小 Blob 合并,减少碎片

维度二:文件系统(XFS/ext4)与内核优化(承上启下)

文件系统是衔接引擎与硬件的桥梁。对于高吞吐的数据库节点,文件系统的挂载参数与空间分配策略至关重要。

1. 禁用在线 discard,改为周期性 fstrim

如果在挂载文件系统时使用了 discard 参数,每当 Titan GC 删除 Blob 文件时,文件系统都会实时向 SSD 发送 TRIM 指令。这会导致 FTL 频繁阻塞以进行地址映射更新。

  • 优化方案:修改 /etc/fstab,去掉 discard 挂载选项。
  • 正确做法:使用 systemctl 启用 fstrim.timer,或者在业务低峰期通过 Crontab 定时执行 fstrim -a
# 检查挂载参数,确保没有 discard
mount | grep -E "ext4|xfs"

# 启用系统自带的每周定时 TRIM
systemctl enable --now fstrim.timer

2. 优化 XFS 分配行为(针对 XFS 用户)

TiDB 官方推荐使用 XFS 文件系统。针对 XFS,可以通过调整挂载参数来减少碎片的产生:

  • allocsize:控制文件预分配大小。增加预分配大小(如 64M)可以使文件系统在分配物理块时尽可能连续,从而显著减少 Extent 碎片的数量。
# 示例挂载参数
UUID=xxxx /data xfs defaults,noatime,nodiratime,allocsize=64M,nobarrier 0 0

(注意:在较新版本的 Linux 内核中,nobarrier 参数可能已被废弃或默认启用安全写入,请根据实际内核版本调整)

3. 定期监控与整理文件系统碎片

在不停机的情况下,可以使用文件系统自带工具对 Titan 的数据目录进行碎片整理。

  • 碎片率检查(以 XFS 为例):
# 查看指定 Blob 文件的碎片情况
filefrag -v /data/tikv/db/titandb/*.blob | head -n 20

# 检查 XFS 文件系统的整体碎片率
xfs_db -c frag /dev/sdb1
  • 在线碎片整理
    如果发现某些大 Blob 文件的 Extent 数量达到了数千甚至上万,建议在低峰期运行 xfs_fsr 进行在线碎片整理:
# 针对 XFS 文件系统执行碎片整理
xfs_fsr /dev/sdb1

维度三:SSD 硬件与 FTL 层的针对性优化(物理托底)

当 Titan 频繁地在文件系统上写写删删时,SSD 硬件层面的表现决定了性能的底线。

1. 预留足够的 Over-Provisioning (OP) 空间

这是应对 SSD 硬件空洞和写入抖动最有效的方法之一。OP(预留空间)是 SSD 内部不暴露给操作系统的隐藏区域,FTL 专门用它来进行垃圾回收和坏块管理。

  • 优化操作:不要把整块 SSD 划分满。在重新初始化设备时,保留 10% - 20% 的未分配空间。例如,一块 1.92TB 的 NVMe SSD,只对其划分 1.6TB 的分区。
  • 这能让 FTL 在面对 Titan 高强度的物理重写时,有充足的物理页面进行合并与搬移,显著降低 FTL 强行 GC 带来的延迟尖峰。

2. 锁定 I/O 调度器

对于现代高速 NVMe SSD,内核默认的 I/O 调度器应该设置为 nonekyber,避免像 bfq 这样复杂的调度器增加 CPU 开销并限制并发队列深度。

# 查看当前 I/O 调度器
cat /sys/block/nvme0n1/queue/scheduler

# 将调度器临时设为 none(对 NVMe 推荐)
echo none > /sys/block/nvme0n1/queue/scheduler

要永久生效,请通过 udev 规则进行配置。

3. 监控 FTL 的磨损与写放大系数 (WA)

定期通过 smartctl 检查 SSD 的健康状态,重点关注 Percentage Used(磨损度)和底层闪存的实际写入量(通过厂商特定的 SMART ID 查询),评估物理写放大。如果发现物理写放大远远大于 Titan 引擎层的写放大,说明 SSD 内部正在经历严重的 FTL 空洞和无效搬移,应立即增大 OP 空间并平滑 Titan 的 GC 参数。


总结:生产环境落地优化路线图

当遭遇因 Titan 导致的物理空洞和碎片性能瓶颈时,建议按照以下步骤实施优化:

实施阶段 核心动作 预期效果
第一步:基础检查 检查并修改 /etc/fstab,禁用 discard,配置定时 fstrim 消除因实时 TRIM 导致的系统级写 Stall。
第二步:引擎调优 提高 min-blob-size 至 32KB+,微调 discardable-ratio(建议 0.45-0.55),限制 max-background-gc 线程。 从源头上减少小文件和频繁的 GC 物理重写。
第三步:文件系统防碎 重新挂载增加 allocsize=64M,低峰期尝试执行 xfs_fsr 提升文件分配连续性,降低 CPU 在解析 Extent 树时的开销。
第四步:物理托底 如果更换或重新格式化磁盘,务必留出 15% 空间做 OP(预留空间)。 为 SSD 硬件 FTL 提供足够的腾挪空间,彻底平抑 P99 抖动。

通过以上三维一体的配置,可以有效缓解 Titan 在大 Value 场景下的底层资源对抗,保证 TiKV 集群在高负载下的高吞吐与低延迟。

点评评价

captcha
健康