TiKV 持久化机制学习分享

TiKV 凭借其独特的 Multi-Raft Group 架构和底层 RocksDB 存储引擎,实现了数据的高效、可靠存储以及分布式环境下的强一致性。让我们从Raft 日志的持久化、RocksDB 的 LSM-Tree 结构、WAL、MemTable、SST 文件、ColumnFamily 的划分等多个方面,学习其如何保证数据持久性、一致性和高可用性,深入了解 TiKV 持久化机制。

1. TiKV 整体架构与持久化概览

TiKV 是一个分布式事务型键值数据库,它为 TiDB 提供了满足 ACID 约束的分布式事务接口,并通过 Raft 协议保证了多副本数据的一致性与高可用性。在 TiKV 的架构中,数据被逻辑上划分为多个 Region(类似 OceanBase 数据库的数据分片 Tablet),每个 Region 是一段连续的 Key 范围。每个 Region 拥有多个副本,组成一个 Raft Group,其中一个副本被选举为 Leader,负责处理读写请求,其他副本为 Follower。

TiKV 的持久化主要依赖于其内部集成的 RocksDB 存储引擎。为了优化不同类型数据的存储和访问模式,每个 TiKV 实例内部维护着两个独立的 RocksDB 实例 :

  • RocksDB Raft (raftdb):此实例专门用于存储 Raft 协议的日志。Raft 日志是实现分布式一致性的核心,它记录了所有对数据的修改操作,确保了在节点故障时数据可以被正确恢复和同步。
  • RocksDB KV (kvdb):此实例用于存储实际的用户数据和 MVCC (Multi-Version Concurrency Control) 信息。为了提高写入效率,同一个 TiKV 节点上的所有 Region 数据都存储在同一个 kvdb 实例中,这样不同 Region 的写入操作可以合并,从而减少随机 I/O,提高写入吞吐量。

2. RocksDB 存储引擎解析

RocksDB 是一款高性能键值存储引擎,其核心是采用了 LSM-Tree (Log-Structured Merge Tree) 架构。LSM-Tree 的设计理念是将随机写入转化为顺序写入,从而显著提升写入性能。RocksDB 的主要组成部分及其工作原理如下:

2.1 Write Ahead Log (WAL)

Write Ahead Log (WAL 即预写入) 是 RocksDB 持久化的首要保障。所有写入操作在被应用到内存中的数据结构之前,都会首先以追加 (append-only) 的方式写入到磁盘上的 WAL 文件中。这种“先写日志,后写内存”的机制确保了即使在系统发生崩溃或断电的情况下,尚未持久化到 MemTable 的数据也能通过 WAL 进行恢复,从而保证了数据的原子性和持久性。

2.2 MemTable 与 SST 文件

MemTable 是 RocksDB 在内存中的数据结构,采用跳表 (SkipList) 实现,用于缓存最新的写入数据。MemTable 提供了高效的写入和查询能力。当 MemTable 的大小达到预设阈值(如 128 MB)时,它会变为 Immutable MemTable(不可变 MemTable),停止接收新的写入。随后,后台线程会负责将 Immutable MemTable 的内容刷新 (Flush) 到磁盘,生成一个 SST (Sorted String Table) 文件。同时,一个新的 MemTable 会被创建以继续接收新的写入操作。

SST 文件是 RocksDB 在磁盘上的主要存储单元,它们是经过排序的键值对集合。SST 文件被组织成多层结构,每一层的数据量是上一层的 10 倍,这意味着约 90% 的数据存储在最底层。这种分层结构是 LSM-Tree 架构的关键,它通过将数据按层级组织,优化了读写性能和空间利用率。

2.3 ColumnFamily (CF) 详解

在 TiKV 的 kvdb 实例中,为了更好地管理和优化不同类型的数据,进一步划分为四个 ColumnFamily。每个 CF 针对其存储的数据类型进行了特定的优化:

ColumnFamily 存储内容 特点与作用
raft Region 元信息 存储 TiKV 中各个 Region 的元数据,例如 Region 的 ID、范围等。这部分数据量极小,但对集群的正常运行至关重要。
lock 事务锁信息 主要用于存储悲观事务的悲观锁以及分布式事务的 Prewrite 锁。在事务提交后,lock CF 中的数据会很快被删除,因此在正常情况下,该 CF 的数据量通常很小(少于 1 GB)。如果 lock CF 中的数据量异常增大,可能表明存在大量事务阻塞或系统出现故障。
write 用户数据 (MVCC) 及短 Value 存储用户数据的 MVCC 信息(包括事务的开始时间 start_ts 和提交时间 commit_ts)以及长度小于等于 255 字节的 Value。此外,TiDB 的二级索引数据也主要存储在此 CF 中。
default 长 Value 专门用于存储长度超过 255 字节的用户数据。这种分离存储有助于优化不同大小 Value 的读写性能。

2.4 Compaction 机制

Compaction 是 RocksDB 后台运行的关键操作,它负责合并和管理不同层级的 SST 文件,以优化存储空间和读取性能。Compaction 的主要作用包括:

  • 清理旧版本数据:LSM-Tree 结构中,由于更新和删除操作会产生新的键值对,导致同一个 Key 可能存在多个版本。Compaction 会删除过期或被覆盖的旧版本数据,从而减少存储空间占用,降低 空间放大 (Space Amplification)
  • 合并小文件:将多个小型的 SST 文件合并成更大的文件,减少文件数量,提高文件系统的管理效率和读取效率。
  • 消除数据碎片:通过重新组织数据,减少磁盘碎片,提升顺序读写性能。

当 L 0 层的 SST 文件数量过多时,RocksDB 会触发 WriteStall 机制,暂时阻塞写入操作。这是 RocksDB 内部的一种流控机制,旨在防止 L 0 层文件过多导致查询性能急剧下降,从而在写入吞吐和读取延迟之间取得平衡。

3. TiKV 数据写入与持久化流程

TiKV 的数据写入流程是一个精心设计的、多阶段的协同过程,涉及 Raft 协议和 RocksDB 存储引擎的紧密配合,以确保数据的一致性和持久性:

  1. 客户端发起写入请求:客户端向 TiKV 集群的 Leader 副本发起写入请求。例如,一个 PUT key value 操作。
  2. Raft 日志写入与复制
    • Leader 副本收到写入请求后,首先将其封装为一个 Raft 日志条目。这个日志条目包含了对数据的具体修改操作。
    • Leader 将该 Raft 日志条目首先写入自身的 RocksDB Raft (raftdb) 实例的 WAL 中。这一步是确保 Leader 本地持久化的关键。
    • Leader 随后将 Raft 日志条目并行复制给其所有的 Follower 副本。Follower 副本收到日志后,同样会将其写入各自的 raftdb 实例的 WAL 中。
    • 当多数副本(例如,在一个 3 副本的 Raft Group 中,至少有 2 个副本)成功将 Raft 日志写入其本地 WAL 后,Leader 认为该日志条目已 Committed(已提交)。
  3. Raft 日志应用 (Apply)
    • Leader 和 Follower 上的 Apply 线程会异步地将已 Committed 的 Raft 日志条目应用。也即将 Raft 日志中记录的数据修改操作实际写入到 RocksDB KV (kvdb) 内存实例中。
    • 写入 kvdb 的过程遵循 RocksDB 的标准写入路径:先写入 kvdb 的 WAL,再写入 kvdb 的 MemTable。WAL 提供了持久化保障。
  4. 客户端响应:一旦 Raft 日志被多数副本 Committed,Leader 就可以向客户端返回写入成功。需要注意的是,此时数据可能尚未完全写入到 kvdb 的 SST 文件中,但 Raft 协议保证了即使 Leader 在此之后发生故障,数据也不会丢失,因为已经 Committed 的 Raft 日志已在多数副本中持久化,可以通过 Raft 协议进行恢复。

4. 持久化与一致性

TiKV 通过上述机制,提供了强大的数据持久化和一致性保证,这对于构建高可靠的分布式系统至关重要:

  • 数据持久性:Raft WAL 和 RocksDB WAL 的双重写入机制,确保了数据在写入磁盘之前不会丢失。即使在系统崩溃时,也可以通过 WAL 进行数据恢复,保证了数据的完整性。
  • 数据一致性:Raft 协议是实现分布式环境下多副本数据强一致性的核心。任何写入操作都必须经过多数派的确认才能被提交,从而避免了数据分歧和脑裂问题。
  • 高可用性:Raft 协议的 Leader 选举机制和多副本复制,使得 TiKV 集群在部分节点故障(例如 Leader 宕机)时仍能通过选举新的 Leader 继续提供服务,保证了系统的高可用性。

参考文献

1、TiKV 简介: https://docs.pingcap.com/zh/tidb/stable/tikv-overview
2、RocksDB 简介: https://docs.pingcap.com/zh/tidb/stable/rocksdb-overview

1 个赞

基础理论为王,无论用多久的数据库,基础理论都是最重要的

1 个赞

机制复制。排除也很有挑战性

总结到位

总结的很好,要是配上实操演示就更好了

:100:
总结的很好。

有没有实际案例可以参考的

双重写入机制怎么实现的

又来学习一遍

基础知识很重要