TiDB预研-基本模块、初步使用
参考
官网文档:https://docs.pingcap.com/zh/tidb/stable/overview/
快速上手:https://docs.pingcap.com/zh/tidb/stable/quick-start-with-tidb/
SQL基本操作:https://docs.pingcap.com/zh/tidb/stable/basic-sql-operations/
tidb-in-action:https://book.tidb.io/SUMMARY.html
TiDB 查询优化及调优系列(二)TiDB 查询计划简介
TiKV 源码解析 | PingCAP 平凯星辰
https://github.com/pingcap/blog-cn/blob/master/mvcc-in-tikv.md
背景
之前业务需求在xxx表加了question_id、paper_id,单表数据4000w+,索引数量有比较多,索引加不上,数据查不出来…
之前有涉及千万大表连接查询的场景(不常使用),因为有索引,虽然能查出来,但是非常占用MySQL服务器的IO资源…
使用SQL查询当前测试环境数据量
// 查询数据库每个表的行记录数量
SELECTTABLE_NAME AS `Table`,TABLE_ROWS AS `Rows`
FROMinformation_schema.TABLES
WHERETABLE_SCHEMA = 'qbm' limit 1000;// 查询数据库占用磁盘大小
SELECT table_schema AS 'Database Name',ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS 'Size (MB)'
FROM information_schema.tables
WHERE table_schema = 'qbm'GROUP BY table_schema;// 查询数据库总记录数量
SELECT table_schema AS '数据库名',SUM(table_rows) AS '总记录数'
FROM information_schema.tables
WHERE table_schema NOT IN ('mysql','information_schema','performance_schema')
GROUP BY table_schema;
测试环境数据量大小截止2025-04-01 ,其中question_logs、paper_logs已经使用sharding-jdbc分表
测试环境数据占用磁盘空间截止2025-04-01 已经大概646GB
技术选型
TiDB 的同类分布式数据库包括:
- OceanBase:阿里开源的金融级分布式数据库,强一致性和高可用性突出,但开源生态和社区活跃度略逊于 TiDB
- CockroachDB:类似 Google Spanner 的分布式 SQL 数据库,支持跨地域部署和强一致性事务,但国内生态支持较弱
- Cassandra:擅长高写入吞吐量的 NoSQL 数据库,但缺乏事务支持和复杂查询能力。
- ClickHouse:专注于 OLAP 场景的列式数据库,适合实时分析,但事务支持较弱。
数据规模与扩展性
- 适用场景:数据量达到 TB/PB 级且需水平扩展时,TiDB 优于 MySQL(需分库分表。
- 建议:若业务增长快且需弹性伸缩,优先选择 TiDB。
事务与一致性需求
- 适用场景:金融级强一致性(如 ACID 事务)需求时,TiDB 通过 Raft 协议和多副本机制优于 MySQL 半同步复制
- 建议:需分布式事务的场景(如支付系统)优先选择 TiDB 或 CockroachDB[3][8]。
技术栈兼容性
- 适用场景:已有 MySQL 生态且需无缝迁移时,TiDB 兼容 MySQL 协议,迁移成本低[4][8][10]。
- 建议:若团队熟悉 MySQL 且不愿重构业务代码,优先选择 TiDB[10]。
成本考量
- 硬件成本:TiDB 对硬件要求较高(推荐物理机部署 TiKV),但可通过动态扩缩容降低长期成本[5][7]。
- 运维成本:相比分库分表方案,TiDB 的自动化运维(如故障自愈)可减少 DBA 人力投入[1][4]。
TiDB特性
开源分布式关系型数据库NewSQL,支持在线事务、在线数据查询分析、自动水平扩缩容,兼容MySQL协议和MySQL生态等,TiDB 适合高可用、强一致要求较高、数据规模较大等各种应用场景。
- 海量数据水平扩缩容对运维透明,支持对计算、存储在线扩缩容,TiDB 是一种性价比高的解决方案,采用计算、存储分离的架构,可对计算、存储分别进行扩缩容,计算最大支持 512 节点,每个节点最大支持 1000 并发,集群容量最大支持 PB 级别
- 高可用采用数据多副本,通过 Multi-Raft 协议同步事务日志,多数派写入成功事务才能提交,确保数据强一致性且少数副本发生故障时不影响数据的可用性。
- 混合事务和分析处理HTAP,混合事务与分析处理(hybrid transactional analytical processing,HTAP)技术是一种基于一站式架构同时处理事务请求与查询分析请求的技术. HTAP技术不仅消除了从关系型事务数据库到数据仓库的数据抽取、转换和加载过程,还支持实时地分析最新事务数据,主要使用行存储引擎TiKV、列存储引擎TiFlash,列存储引擎TiFlash通过Multi-Raft Learner协议实时的从TiKV复制数据,确保数据强一致性。
- 兼容MySQL协议和MySQL生态,迁移无需或者修改少量代码即可,有全量数据、增量数据迁移的工具。
TiDB整体架构
TiDB系统包含多个模块
- TiDB Server:解析SQL请求分发模块,对外暴露MySQL协议的连接endpoint,接受客户端请求,执行SQL解析和优化,最终生成分布式执行计划,本身无状态不存储数据,支持多节点负载均衡,将实际的数据请求转发给底层的TiKV、TiFlash
- Placement Driver Server:整个集群的大脑,存储管理TiDB集群元数据、分布式事务模块,建议三个实例以上
- 负责存储每个TiKV节点的状态以及管理的数据,根据TiKV节点的心跳上报数据分布状态,提供一个Dashboard管理界面
- 负责给分布式事务分发一个事务ID
- 下发具体的命令给具体的TiKV节点
- 存储模块
- TiKV:存储数据,每个TiKV包含多个Region块,每个Region块存储一个[startKey,endKey)范围的数据,在这层提供API操作key从而实现分布式事务的原生支持,默认隔离级别是快照隔离 SI (Snapshot Isolation) 的隔离级别,在SQL层支持分布式事务就是依靠这个存储引擎提供的API。另外就是TiKV的数据自动会维护多副本,默认是3副本。
- TiFlash:列式存储数据,从TiKV同步数据,主要功能是为分析场景加速。
组件 | 职责 |
---|---|
TiDB Server | 无状态计算层:处理 SQL 请求、生成执行计划、分配自增 ID(<font style="color:rgb(51, 51, 51) !important;">AUTO_INCREMENT</font> /<font style="color:rgb(51, 51, 51) !important;">AUTO_RANDOM</font> )。 |
PD Server | 全局协调者:存储元信息(如自增 ID 区间)、调度 Region、维护全局 TSO。 |
TiKV | 有状态存储层:实际存储数据(按 Region 分片)、处理数据读写请求。 |
TiKV
TiKV存储引擎是一个巨大的map,key按照二进制顺序排序,可以通过seek方法快速定位某个key,使用next按照顺序遍历key。
底层kv存储引擎使用的是RocksDB,没有直接像磁盘写数据。关于RocksDB底层结构是LSM,会分为一个个内存、磁盘SSTable,参考:https://zhuanlan.zhihu.com/p/616209332
多TiKV分片数据一致性依靠的是raft协议,raft协议包含Leader选取、成员变更日志同步等,TiKV写数据不是直接写入RocksDB,而是先写入到Raft日志,写入半数认为写入成功,之后在写入RocksDB,当发生Leader切换,根据Raft日志来进行数据同步。
数据分片单位Region
对于数据存储Region,可以看做一致性Hash的优化,类似Redis集群中的哈希槽,不同的是单位不太一致,Redis集群每个节点负责的是槽范围,而Region好比将哈希槽又分段了,然后每个节点存储多个Region段。
比如一个TiKV节点
将数据划分成 Region 后,TiKV 执行两项重要操作:
- 按 Region 将数据分散到集群中的所有节点,并尽量保证每个节点上的 Region 数量大致相同。
- 以 Region 为单位进行 Raft 的复制和成员管理。
另外就是
- 保证每个TiKV节点负责的Region数量均衡,集群大脑PD Server组件负责均衡分配Region到各节点实现自动水平拓展以及负载均衡(避免数据倾斜),PD中会记录key在哪个Region以及这个Region被哪个TiKV节点负责,完成路由
- 多个TiKV节点(主从)通过Raft日志保持数据一致,所有读写请求均有Leader负责,写操作写到Raft然后同步给从副本。
分布式事务ACID
数据库有多种并发控制方法,这里只介绍以下两种:
- 乐观并发控制(OCC):在事务提交阶段检测冲突
- 悲观并发控制(PCC):在事务执行阶段检测冲突
乐观并发控制期望事务间数据冲突不多,只在提交阶段检测冲突能够获取更高的性能。悲观并发控制更适合数据冲突较多的场景,能够避免乐观事务在这类场景下事务因冲突而回滚的问题,但相比乐观并发控制,在没有数据冲突的场景下,性能相对要差。
TiDB 基于 Google Percolator 实现了支持完整 ACID、基于快照隔离级别(Snapshot Isolation)的分布式乐观事务。https://research.google/pubs/large-scale-incremental-processing-using-distributed-transactions-and-notifications/
TiDB事务支持悲观事务、乐观事务
- 悲观事务和MySQL中的一致,使用BEGIN TRANSACTION … COMMIT/ROLLBACK将事务中多个操作绑定成原子操作,并发事务执行期间之间通过(行)锁来保证正确性。
- 乐观事务也是类似MySQL中的MVCC机制,将修改冲突视为事务提交的一部分,因此对于不是经常修改同一行时,可以跳过获取行锁的过程进而提升性能,但是对于并发事务经常修改同一行冲突的时候,乐观事务的性能可能低于悲观事务。自 v3.0.8 开始,TiDB 集群默认使用悲观事务模式。
但是因为TiDB时分布式系统,事务数据可能存在多个TiKV中,是通过2PC两阶段提交协议保证事务特性。
在 TiKV 层的事务 API 的语义类似下面的伪代码:
tx = tikv.Begin()tx.Set(Key1, Value1)tx.Set(Key2, Value2)tx.Set(Key3, Value3)
tx.Commit()
这个事务中包含3条 Set 操作,TiKV 能保证这些操作要么全部成功,要么全部失败,不会出现中间状态或脏数据。 就如前面提到的,TiDB 的 SQL 层会将 SQL 的执行计划转换成多个 KV 操作,对于上层的同一个业务层的 SQL 事务,在底层也是对应一个 KV 层的事务,这是 TiDB 实现 MySQL 的事务语义的关键。
乐观事务实现
TiDB 基于 Google Percolator 实现了支持完整 ACID、基于快照隔离级别(Snapshot Isolation)的分布式乐观事务。TiDB 乐观事务需要将事务的所有修改都保存在内存中,直到提交时才会写入 TiKV 并检测冲突。
快照隔离级别
参考:https://github.com/pingcap/blog-cn/blob/master/mvcc-in-tikv.md
Percolator 使用多版本并发控制(MVCC)来实现快照隔离级别,与可重复读的区别在于整个事务是在一个一致的快照上执行。TiDB 使用 PD 作为全局授时服务(TSO)来提供单调递增的版本号:
- 事务开始时获取 start timestamp,也是快照的版本号;事务提交时获取 commit timestamp,同时也是数据的版本号
- 事务只能读到在事务 start timestamp 之前最新已提交的数据
- 事务在提交时会根据 timestamp 来检测数据冲突
主要为了读写不加锁,保证并发性能,类似MySQL的MVCC机制,TiDB的MVCC是在给每个key生成一个version快照版本,比如原来数据格式是key1->value1,B事务读取的时候生成快照key1_version1 -> value1,这个时候即使A事务修改key1,也只是在key1_version2-> value1这个版本修改,不会影响。即使该 Key 被多个写操作修改过多次,客户端 B 也可以按照其逻辑时间读到旧的值。
// 没有MVCC的时候
Key1 -> Value
Key2 -> Value
……
KeyN -> Value// 有了MVCC的时候
Key1_Version3 -> Value
Key1_Version2 -> Value
Key1_Version1 -> Value
……
Key2_Version4 -> Value
Key2_Version3 -> Value
Key2_Version2 -> Value
Key2_Version1 -> Value
……
KeyN_Version2 -> Value
KeyN_Version1 -> Value
……
对于同一个 Key 的多个版本,版本号较大的会被放在前面,版本号小的会被放在后面(见 Key-Value 一节,Key 是有序的排列),这样当用户通过一个 Key + Version 来获取 Value 的时候,可以通过 Key 和 Version 构造出 MVCC 的 Key,也就是 Key_Version。然后可以直接通过 RocksDB 的 SeekPrefix(Key_Version) API,定位到第一个大于等于这个 Key_Version 的位置。
快照数据垃圾回收
对于每个事务产生的快照版本,在有新的快照的时候不会被替换,为了防止过多占用大量空间,需要定期GC删除之前已提交事务的快照
GC 会被定期触发,默认情况下每 10 分钟一次。每次 GC 时,首先,TiDB 会计算一个称为 safe point 的时间戳(默认为当前时间减去 10 分钟),接下来 TiDB 会在保证 safe point 之后的快照全部拥有正确数据的前提下,删除更早的过期数据。
// TiDB 的 GC 相关的配置存储于 mysql.tidb 系统表中,可以通过 SQL 语句对这些参数进行查询和更改:
select VARIABLE_NAME, VARIABLE_VALUE from mysql.tidb;// 例如,如果需要将 GC 调整为保留最近一天以内的数据,只需执行下列语句即可:
update mysql.tidb set VARIABLE_VALUE="24h" where VARIABLE_NAME="tikv_gc_life_time";
GC 在执行过程中会删除大量数据,可能会对线上业务造成影响。可通过修改 TiKV 配置中的 gc.max-write-bytes-per-sec 限制 GC worker 每秒数据写入量,降低对正常请求的影响,0 为关闭该功能。
两阶段提交2PC
TiDB 使用两阶段提交(Two-Phase Commit)来保证分布式事务的原子性,分为 Prewrite 和 Commit 两个阶段:
- Prewrite:对事务修改的每个 Key 检测冲突并写入 lock 防止其他事务修改。对于每个事务,TiDB 会从涉及到改动的所有 Key 中选中一个作为当前事务的 Primary Key,事务提交或回滚都需要先修改 Primary Key,以它的提交与否作为整个事务执行结果的标识。
- Commit:Prewrite 全部成功后,先同步提交 Primary Key,成功后事务提交成功,其他 Secondary Keys 会异步提交。
Percolator 将事务的所有状态都保存在底层支持高可用、强一致性的存储系统中,从而弱化了传统两阶段提交中协调者(Coordinator)的作用,所有的客户端都可以根据存储系统中的事务状态对事务进行提交或回滚。
过程举例
- 客户端开始一个事务。
- TiDB 向 PD 获取 tso 作为当前事务的 start timestamp。
- 客户端发起读或写请求。
- 客户端发起 Commit。
- TiDB 开始两阶段提交,保证分布式事务的原子性,让数据真正落盘。
i. TiDB 从当前要写入的数据中选择一个 Key 作为当前事务的 Primary Key。
ii. TiDB 并发地向所有涉及的 TiKV 发起 Prewrite 请求。TiKV 收到 Prewrite 请求后,检查数据版本信息是否存在冲突,符合条件的数据会被加锁。
iii. TiDB 收到所有 Prewrite 响应且所有 Prewrite 都成功。
iv. TiDB 向 PD 获取第二个全局唯一递增版本号,定义为本次事务的 commit timestamp。
v. TiDB 向 Primary Key 所在 TiKV 发起第二阶段提交。TiKV 收到 Commit 操作后,检查锁是否存在并清理 Prewrite 阶段留下的锁。
- TiDB 向客户端返回事务提交成功的信息。
- TiDB 异步清理本次事务遗留的锁信息。
乐观锁写写冲突检测
关于写写冲突检测,冲突检测乐观事务下,从TiKV检测底层数据是否存在写写冲突是一个很重的操作。作为一个分布式系统以及为了优化性能,会设计两个阶段的冲突检测
- 第一个阶段冲写突检测是在TiDB Server,前提是单个TiDB Server内,解析SQL之后在内存记录写入操作,之后的写入操作就从内存做冲突检测,检测到冲突就不用在调用相关的TiKV了
- 第二个阶段写冲突检测是在TiKV,主要发生在prewrite阶段,因为TiDB Server可能是多实例的,所以还是要在TiKV做兜底
1、其中 TiDB Server 层的冲突检测可以关闭,配置项可以启用,txn-local-latches: 事务内存锁相关配置,当本地事务冲突比较多时建议开启。可以在默认值配置文件中对默认值进行修改。
- enable:配置true、false
- capacity:可以理解为哈希表大小,大小会影响冲突检测的准确性,因为不可能吧每个key都放在内存,因此存储的是key的hash值,哈希存在冲突,哈希表越长,冲突的概率越小,默认值1024000,会自动向上调整为 2 的指数倍。每个 slot 占 32 Bytes 内存
在实际应用时,如果业务场景能够预判断写入不存在冲突(如导入数据操作),建议关闭冲突检测。
2、在TiKV层的冲突检测也类似,不同的是,TiKV 的检测会更严格,不允许关闭,只提供了一个 hash 取模值的配置项,可以在默认值配置文件中对默认值进行修改:
- scheduler-concurrency:cheduler 内置一个内存锁机制,防止同时对一个 Key 进行操作。每个 Key hash 到不同的槽。默认值:2048000
乐观锁事务的重试
事务的重试 使用乐观事务模型时,在高冲突率的场景中,事务很容易提交失败。比如 2 个事务同时修改相同行,提交时会有一个事务报错:
ERROR 8005 (HY000) : Write Conflict, txnStartTS is stale
乐观锁并发写可能导致事务失败,比如2PC的prewrite过程检测到写冲突,TiDB提供一个乐观事务重试的机制,可以通过设置 tidb_disable_txn_auto_retry = off 开启自动重试,并通过 tidb_retry_limit 设置重试次数:
- tidb_disable_txn_auto_retry:这个参数控制是否自动重试,默认为 1,即不重试。
- tidb_retry_limit:用来控制重试次数,注意只有第一个参数启用时该参数才会生效。
// session 级别设置:
set @@tidb_disable_txn_auto_retry = 0;
set @@tidb_retry_limit = 10;// tidb_retry_limit 决定了事务重试的最大次数。当它被设置为 0 时,所有事务都不会自动重试,包括自动提交的单语句隐式事务。这是彻底禁用 TiDB 中自动重试机制的方法。
// 禁用自动重试后,所有冲突的事务都会以最快的方式上报失败信息 (try again later) 给应用层。// 全局设置:
set @@global.tidb_disable_txn_auto_retry = 0;
set @@global.tidb_retry_limit = 10;
重试也是有局限性的,可能导致更新丢失,从而破坏可重复读的隔离级别,重试时仅重新执行包含写操作的 SQL 语句,并不涉及读操作的 SQL 语句。但是当前事务中读到数据的时间与事务真正开始的时间发生了变化,写入的版本变成了重试时获取的 start timestamp 而非事务一开始时获取的 start timestamp。
当业务可以忍受更新丢失,不依赖查询结果来更新,不太依赖可重复读,那么可以使用乐观事务重试机制,否则建议在写冲突多的时候直接用TiDB的悲观事务。
表数据、索引映射kv规则
表数据与key-value数据映射规则就是 每行数据存储为一个key-value,需要考虑同一个表数据的紧凑型以及每行数据需要经常CRUD
备注:这里以聚簇索引举例,TiDB支持聚簇索引和非聚簇索引,非聚簇索引存储一行数据需要两个kv,一个是主键作为k,隐式行row_id作为v,另外一个是隐式行row_id作为k,行数据作为v,而聚簇索引则是一个kv,主键作为k,行数据作为v
- 表ID:为了保证一个表的数据存储的key是在一块的,会生成一个表ID
- 行ID:唯一ID(如果没有会自动生成主键ID)
// tablePrefix、recordPrefixSep为常量字符串Key: tablePrefix{TableID}_recordPrefixSep{RowID}
Value: [col1, col2, col3, col4]// 字符串常量
tablePrefix = []byte{'t'}
recordPrefixSep = []byte{'r'}
indexPrefixSep = []byte{'i'}
另外就是索引和key-value数据的映射规则,除了主键索引,支持二级索引(包含唯一索引、非唯一索引),需要根据某一列的值,快速的能查到RowID,进而找到数据。
// 主键、二级索引(唯一索引)
Key: tablePrefix{tableID}_indexPrefixSep{indexID}_indexedColumnsValue
Value: RowID// 二级索引(非唯一索引),需要将RowId放在key,便于左前缀匹配
Key: tablePrefix{TableID}_indexPrefixSep{IndexID}_indexedColumnsValue_{RowID}
Value: null
举个例子
CREATE TABLE User (ID int,Name varchar(20),Role varchar(20),Age int,PRIMARY KEY (ID),KEY idxAge (Age)
);// 表数据,1、2、3表示主键ID,即RowId
1, "TiDB", "SQL Layer", 10
2, "TiKV", "KV Engine", 20
3, "PD", "Manager", 30
4, "SCL", "Manager", 30// 表数据存储kv格式,假设TableId为10
t10_r1 --> ["TiDB", "SQL Layer", 10]
t10_r2 --> ["TiKV", "KV Engine", 20]
t10_r3 --> ["PD", "Manager", 30]
t10_r4 --> ["SCL", "Manager", 30]// 非唯一的二级索引存储kv格式,比如对age字段有非唯一索引,查询age=30会对应两个RowId
t10_i1_10_1 --> null
t10_i1_20_2 --> null
t10_i1_30_3 --> null
t10_i1_30_4 --> null
PD Server
TiDB集群的大脑,负责管理集群元数据(全局最大的自增ID,表结构元数据),号段自增ID分配给TiDB Server,集群数据(状态)的实时调度,分布式事务调度等
PD使用 etcd 来进行元数据存取和高可用支持
表结构元数据管理
TiDB中每个Database和Table都有元信息,也就是其定义以及各项属性,这些信息是持久化TiKV中的,其中每个Database、Table会被分配一个唯一的ID,如m_databaseId_tableId为key,然后value存储序列化之后的元数据。
另外在PD Server中,会专门的存储当前表结构元信息的最新版本号,这个值是全局的,每次DDL的时候该值就会加1,目前,TiDB 把这个键值对持久化存储在 PD Server 中,其 Key 是 “/tidb/ddl/global_schema_version”,Value 是类型为 int64 的版本号值。TiDB 采用 Online Schema 变更算法,有一个后台线程在不断地检查 PD Server 中存储的表结构信息的版本号是否发生变化,并且保证在一定时间内一定能够获取版本的变化。
数据、事务调度
Placement Driver 是TiDB集群的管理模块,同时也负责集群数据的实时调度,调度需要的信息(每个TiKV节点状态、每个Region的Raft log、业务访问操作的统计等)也需要PD Server管理维护。
TiKV集群中每个TiKV实例是一个数据分片,数据以Region为单位进行复制和管理,也就是每个Region会有Leader、Salve主从副本,这些Region会被均匀的分布在TiKV中,其中Leader Region负责读写,Slave Region负责同步Leader发布的Raft log。
为什么需要PD Server这样一个单独的节点来做调度?
考虑一下情景
- 为了提高集群空间利用率,如何将Region合理的分布?
- 主从Region的高可用?一个机房的TiKV掉线,恢复之后不能丢失Raft log
- 集群扩容?当TiKV集群新增了节点,需要合理的将集群中的其他节点数据搬到新节点
- 当一个TiKV掉线,也就是多个主从Region会掉线,可能包含Leader、Salve,需要快速、稳定的容灾
- 从TiKV节点恢复时间维度,短暂掉线(服务重启)是否需要进行调度,如果是长时间掉线(磁盘故障,数据全部丢失),如何调度?
- 从分片Region所需Raft log副本数量维度,短暂掉线(节点掉线,失去副本)副本数量不够,需要选择适当的机器进行补充,短暂掉线恢复(掉线的节点又恢复正常,自动加入集群),需要合理的删除多余副本。
- Leader Region如果分布不均几种在几个节点,会对集群造成影响,如何均匀分配?
- 只有部分Region是访问热点的Region,如何调度均衡?
- 集群在做扩容负载均衡的时候,往往需要搬迁数据,数据迁移耗费大量网路带宽、磁盘IO以及CPU,进而影响在线服务。
以上场景如果同时出现就不太好解决,因此考虑全局信息,同时整个系统也是在动态变化的,因此需要一个中心节点,来对系统的整体状况进行把控调整。
需要做哪些调度?
因此PD Server需要做的调度需求
- 第一类:作为一个分布式高可用存储系统,必须满足的需求
- Region副本数量不能多也不能少
- Region副本需要根据拓扑结构分布在不同的机器,避免单点故障
- 节点宕机或异常是否能够自动合理的快速容灾
- 第二类:作为一个良好的分布式系统,需要考虑的地方
- 维持Leader Region均匀分配
- 维持每个TiKV负责的Region均匀分配
- 维持每个TiKV负责的热点 Region 均匀分配
- 控制负载均衡的速度,避免影响整个TiKV集群
- 管理TiKV节点状态,包括手动上线、下线等
调度的基本操作
- 增加、删除一个Region副本
- 主从Region的切换以及Raft log的 迁移transfer
调度所需要的信息?
调度需要的信息,需要和TiKV集群保持心跳包获取数据
- 每个TiKV节点的状态信息:PD会通过心跳包检测每个Store是否存活,以及是否有新加入的Store,另外一个方面,心跳包也会携带这个TiKV节点(Store)状态信息
- 总磁盘量、可用磁盘量
- 承载的Region数量
- 数据写入、读取的速度
- 发送、接受的Snapshot数量(副本之间可能通过Snapshot同步数据)
- 是否过载
- 每个TiKV节点下每个Region的状态信息,每个Raft Group的Leader也就是Region Leader都会定期的像PD回报Region的状态信息,除此之外,PD 还可以通过扩展的接口接受额外的信息,用来做更准确的决策。比如当某个 Store 的心跳包中断的时候,PD 并不能判断这个节点是临时失效还是永久失效,只能经过一段时间的等待(默认是 30 分钟),如果一直没有心跳包,就认为该 Store 已经下线,再决定需要将这个 Store 上面的 Region 都调度走。但是有的时候,是运维人员主动将某台机器下线,这个时候,可以通过 PD 的管理接口通知 PD 该 Store 不可用,PD 就可以马上判断需要将这个 Store 上面的 Region 都调度走。
- Region Leader的位置、Region Salve的位置
- 掉线副本的个数
- 数据写入、读取的速度
PS:每个TiKV节点的状态信息
调度的策略以及实现?
判断是否需要调度:PD从收集到TiKV集群、TiKV集群在的Regions信息中,每次收到 Region Leader 发来的心跳包时,PD 都会检查这个 Region 是否有待进行的操作,针对一下情况,生成调度策略
- 一个 Region 的副本数量正确,比如TiKV掉线Region副本减少,然后恢复后发现Region副本又多了,或者是管理员修改了max-replicas 的配置,策略就是需要Add/Remove Region Replica
- 一个 Raft Group 中的Leader Region以及Salve Region多个副本不在同一个位置,避免点故障,一般来说PD Server会把Region分配到不同的TiKV节点,但是多个TiKV可能放在一个服务器,策略就是可以根据用户设置的labels、location-labels来保证副本不在同一个TiKV以及同一个服务器。
- Leader Region 在 TiKV 之间的分布均匀分配,复杂均衡,另外就是热点Leader Region的均匀分配
- 每个TiKV的存储空间大致相等,每个 TiKV 启动的时候都会指定一个 Capacity 参数,表明这个 Store 的存储空间上限,PD 在做调度的时候,会考虑节点的存储空间剩余量。
- 控制调度速度,避免影响在线服务,考虑CPU、内存、磁盘IO以及网络带宽等
调度的实现:然后通过心跳包的回复消息,将需要进行的操作返回给 Region Leader,并在后面的心跳包中监测执行结果。注意这里的操作只是给 Region Leader 的建议,并不保证一定能得到执行,具体是否会执行以及什么时候执行,由 Region Leader 根据当前自身状态来定。
TiDB Server
为什么设计无状态,不需要主从模式?
- 无状态服务:由于 TiDB Server 不存储数据,节点之间无需同步状态,因此不需要主从复制。
- 故障恢复:如果某个 TiDB Server 节点故障,其他节点可以立即接管请求,无需等待主节点切换。
- 事务一致性:事务的 ACID 特性由底层存储层(TiKV + PD)通过 Raft 协议和分布式事务机制保证,与 TiDB Server 的架构无关。
解析SQL
无状态,接受客户端请求,解析SQL为kv操作,然后发给存储层TiKV,然后组装TiKV返回的结果返回。
试想最简单的SQL解析方案思路就是按照上面的table到kv的规则,举个例子
比如 select count(*) from user where name = “TiDB” 这样一个 SQL 语句,没有索引,它需要读取表中所有的数据,然后检查 name 字段是否是 TiDB,如果是的话,则返回这一行。具体流程如下:
- 构造出 Key Range:一个表中所有的 RowID 都在 [0, MaxInt64) 这个范围内,使用 0 和 MaxInt64 根据行数据的 Key 编码规则,就能构造出一个 [StartKey, EndKey)的左闭右开区间。
- 扫描 Key Range:根据上面构造出的 Key Range,读取 TiKV 中的数据。
- 过滤数据:对于读到的每一行数据,计算 name = “TiDB” 这个表达式,如果为真,则向上返回这一行,否则丢弃这一行数据。
- 计算 Count():对符合要求的每一行,累计到 Count() 的结果上面。
这样直观是可行的,但是需要考虑到性能问题,
- 每个kv都需要一次RPC:每次调用TiKV存储引擎API的RPC消耗,每一行都需要调用TiKV取数据,每次都需要一次RPC
- 会召回不需要的数据:另外就是先查询TiKV全部数据,在TiDB Server层完成条件过滤,搜索到了一些不必要的数据,其实应该判断不满足条件name = "SCL TIDB"的kv数据应该不用返回。
因此TIDB的实现方案是将过滤条件下推到TiKV存储引擎计算,这样可以避免频繁的RPC调用以及召回不相关的数据,然后count(*)函数也可以被下推到TiKV存储引擎,后面只需要将每个TiKV返回的结果在TIDB Server聚合即可。
关于SQL解析层上述只是一个大体思想,真实情况很复杂,用户的 SQL 请求会直接或者通过 Load Balancer 发送到 TiDB Server,TiDB Server 会解析 MySQL Protocol Packet,获取请求内容,对 SQL 进行语法解析和语义分析,制定和优化查询计划,执行查询计划并获取和处理数据。数据全部存储在 TiKV 集群中,所以在这个过程中 TiDB Server 需要和 TiKV 交互,获取数据。最后 TiDB Server 需要将查询结果返回给用户。
自增主键
TiDB提供两种主键模式
1、AUTO_INCREMENT(类 MySQL 模式,但是MySQL是单机,通常是严格自增不会有空洞)实现原理:
- 每个 TiDB 实例(tidb-server)预先向PD Server申请一段连续 ID 区间(默认步长 30000)。
- TiDB Server实例内存中缓存这些 ID,插入数据时直接分配本地缓存的 ID。
- 当缓存耗尽时,向 PD(Placement Driver)协调节点异步请求新区间。
特点:
- 单调递增但不连续(事务回滚、缓存区间未耗尽时重启实例会导致 ID 空洞)。
- 写入热点风险:顺序递增可能导致新数据集中写入同一 Region(需配合 SHARD_ROW_ID_BITS 分治)。
举例子,假设集群中有 2 个 TiDB Server(实例 A 和 B),3 个 PD Server,3 个 TiKV 节点:
- 初始状态:
- TiDB Server A 和 B 启动时,本地 ID 缓存为空。
- PD 维护全局自增 ID 元信息(当前已分配的最大 ID)。
- 客户端向 TiDB Server A 插入数据:
- TiDB A 发现本地 ID 缓存为空,向 PD 请求分配一个 ID 区间(例如 [1, 30000])。
- PD 记录当前已分配 ID 最大值(30000),并返回该区间给 TiDB A。
- TiDB A 将 [1, 30000] 缓存在内存中,后续插入直接使用本地缓存 ID(无需每次请求 PD)。
- 客户端向 TiDB Server B 插入数据:
- TiDB B 同样缓存为空,向 PD 请求分配 ID 区间。
- PD 返回下一个区间 [30001, 60000]。
- TiDB B 缓存此区间。
- 并发插入:两个 TiDB Server 独立分配 ID,无需互相协调,保证全局唯一。
- TiDB A 使用 ID 1,2,3…(从 [1, 30000] 中分配)
- TiDB B 使用 ID 30001,30002,30003…(从 [30001, 60000] 中分配)。
- 数据写入 TiKV:
- 最终数据(包含自增 ID)通过 Raft 协议,先写入Raft Log,再写入 TiKV 的 Region Leader。
- TiKV 不参与 ID 分配,仅负责存储数据。
注意存在极端情况,若 TiDB Server A 在分配完 ID 1-100 后崩溃,未使用的 101-30000 区间将永久丢失,导致 ID 空洞。这是 TiDB 为性能(减少 PD 交互)与全局唯一性所做的权衡。
2、AUTO_RANDOM(TiDB 特有模式)实现原理:
- 将 64 位整数主键分为三部分: 高位随机位(默认 5bit) | 时间戳(可省略) | 低位自增序列(剩余位) |
- 高位随机位:用于散列到不同 Region,避免写入热点。
- 低位自增:保证同一分片内局部递增(但全局无序)。
特点:
- 天然避免写入热点,适合高并发插入场景。
- 主键全局唯一但无序,且只能用于主键列(类型需为 BIGINT)。
为什么不是 TiKV 或 PD 分配 ID?
主要是考虑并发写入的性能。
- TiKV:若由 TiKV 分配,每个 Region 需要维护独立的 ID 区间,导致跨 Region 协调复杂。写入性能会因跨节点协调而严重下降。
- PD:若每次分配 ID 都请求 PD(单点协调),会成为性能瓶颈。TiDB 的 预分配区间(Batching) 机制大幅减少 PD 交互次数(例如每 30000 个 ID 请求一次 PD)。
如何选择?
高并发插入:优先选择 AUTO_RANDOM。
业务依赖有序性:使用 AUTO_INCREMENT + SHARD_ROW_ID_BITS 分片。
避免显式指定主键值:可能破坏 ID 分配机制,导致冲突或性能下降。
示例:创建表时指定自增类型
CREATE TABLE users (id BIGINT PRIMARY KEY AUTO_RANDOM, -- 避免热点name VARCHAR(64)
);CREATE TABLE orders (id INT PRIMARY KEY AUTO_INCREMENT, -- 单增但需处理热点SHARD_ROW_ID_BITS = 4 -- 分片缓解热点// 另外就是对于非聚簇索引主键或没有主键的表,TiDB 会使用一个隐式的自增 rowid。// 大量执行 INSERT 插入语句时会把数据集中写入单个 Region,造成写入热点。
);
如何让兼容MySQL使用?
TiDB 自增 ID 的缓存大小在早期版本中是对用户透明的。从 v3.1.2、v3.0.14 和 v4.0.rc-2 版本开始,TiDB 引入了 AUTO_ID_CACHE 表选项来允许用户自主设置某个表自增 ID 分配缓存的大小,该模式能确保 ID 严格递增且间隙最小。
CREATE TABLE t(a int AUTO_INCREMENT key) AUTO_ID_CACHE 1;
当 AUTO_ID_CACHE 设置为 1 时,所有 TiDB 实例生成的 ID 严格全局递增,每个 ID 保证全局唯一,相较于默认缓存模式(AUTO_ID_CACHE 0 具有 30000 个缓存值),ID 间隙显著缩小。
但是有回到了RPC性能损耗的问题,当设置为 1 时,每次插入新记录都需要从 PD (Placement Driver) 获取下一个自增 ID。
- 每次插入需要一次 PD 的 RPC 请求,导致高延迟(通常增加 1ms+ 的延迟)。
- 吞吐量急剧下降,无法发挥 TiDB 的分布式写入能力。
- PD 可能成为瓶颈,集群规模较大时可能引发 PD 资源争用。
推荐
- 调整AUTO_ID_CACHE值大一些
- 业务层使用Snowflake生成唯一ID
- 使用AUTO_RANDOM
- 优化TiDB Server和PD Server之间的RPC通信消耗
不连续的原因?
即使兼容MySQL那种方式使用,仍然会造成ID空洞,比如
- 同一个TiDB Server重启:AUTO_INCREMENT 缓存不会持久化,重启会导致缓存值失效,会重新向PD Server申请号段。
- 同一个TiDB Server事务回滚:比如插入行数据,但是name列有唯一索引,导致插入失败事务回滚,这个时候本次申请的自增ID就会跳过,下一个事务插入数据就会向TiDB Server申请新的AUTO_INCREAMENT ID
- 同一个TiDB Server号段池用完:下一个事务插入数据之前,TiDB Server会向PD Server申请新的号段ID区间,然后再为该事务分配自增ID
- 不同的TiDB Server:不同的TiDB Server自增ID区间是不一样的。
聚簇索引和非聚簇索引?
聚簇索引 (clustered index) 是 TiDB 从 v5.0 开始支持的特性,用于控制含有主键的表数据的存储方式。通过使用聚簇索引,TiDB 可以更好地组织数据表,从而提高某些查询的性能。有些数据库管理系统将聚簇索引表称为“索引组织表” (index-organized tables)。
目前 TiDB 中含有主键的表分为以下两类:
<font style="background-color:rgb(245, 248, 250);">NONCLUSTERED</font>
,表示该表的主键为非聚簇索引。在非聚簇索引表中,行数据的键由 TiDB 内部隐式分配的<font style="background-color:rgb(245, 248, 250);">_tidb_rowid</font>
构成,而主键本质上是唯一索引,因此非聚簇索引表存储一行至少需要两个键值对,分别为<font style="background-color:rgb(245, 248, 250);">_tidb_rowid</font>
(k)- 行数据(v)- 主键列数据(k) -
<font style="background-color:rgb(245, 248, 250);">_tidb_rowid</font>
(v)
<font style="background-color:rgb(245, 248, 250);">CLUSTERED</font>
,表示该表的主键为聚簇索引。在聚簇索引表中,行数据的键由用户给定的主键列数据构成,因此聚簇索引表存储一行至少只要一个键值对,即- 主键列数据(k) - 行数据(v)
https://docs.pingcap.com/zh/tidb/stable/clustered-indexes/
TiFlash
https://docs.pingcap.com/zh/tidb/stable/tiflash-overview/#%E4%BD%BF%E7%94%A8-tiflash
是HTAP(混合事务数据分析)的关键组件,是列式存储,TiFlash 暂时无法直接接受数据写入,任何数据必须先写入 TiKV 再同步到 TiFlash,支持表粒度的数据同步。TiFlash 部署完成后并不会自动同步数据,而需要手动指定需要同步的表。
包含组件
- 列式存储引擎组件
- 处理 Multi-Raft 协议通信相关工作的 TiFlash proxy 组件
TiFlash 主要有异步复制、一致性、智能选择、计算加速等几个核心特性,
- 异步复制TiKV的数据:TiFlash 中的副本以特殊角色 (Raft Learner)从TiKV 进行异步的数据复制。这表示当 TiFlash 节点宕机或者网络高延迟等状况发生时,TiKV 的业务仍然能确保正常进行。
- 主从Region一致性:提供与TiKV一样的快照隔离支持,保证读取到最新的数据,请求来到Region Slave,会发起一个数据复制进度的校验到Region Leader,是一个非常轻的RPC请求,只有当主从Region复制进度确保至少所包含读取请求时间戳所覆盖的数据之后才响应读取
- 智能选择:TiDB可以自动选择使用TiFlash、TiKV存储,在同一查询内也是可以混合使用提供最佳查询速度,跟TiDB选择不同索引查询类似,根据统计信息判断读取代价并作出合理的选择。
- 计算加速:包含列存储本身的读取效率以及类似TiKV计算下推分担计算 两种
测试使用
环境搭建demo
本地部署TiDB集群:TiDB 数据库快速上手指南
使用JDBC连接本地TiDB:Spring Boot集成tidb快速入门demo_springboot tidb-CSDN博客
// 启动TiDB
tiup playground// 指定参数
tiup playground v8.5.1 --db 2 --pd 3 --kv 3// 访问 Prometheus 管理界面:http://127.0.0.1:9090。
// 访问 TiDB Dashboard 页面:http://127.0.0.1:2379/dashboard,默认用户名为 root,密码为空。
// 访问 Grafana 界面:http://127.0.0.1:3000,默认用户名和密码都为 admin。// 使用mysql客户端连接TiDB
mysql --host 127.0.0.1 --port 4000 -u root
快速使用TiFlash HTAP 的demo:HTAP 快速上手指南
// 安装数据生成工具
tiup install bench// 生成数据
tiup bench tpch --sf=1 prepare// 查询生成的数据
// 共生成了八张表,最大的一张表数据量有 600 万行(由于数据是工具随机生成,所以实际的数据生成量以 SQL 实际查询到的值为准)。
SELECT CONCAT(table_schema,'.',table_name) AS 'Table Name', table_rows AS 'Number of Rows', CONCAT(ROUND(data_length/(1024*1024*1024),4),'G') AS 'Data Size', CONCAT(ROUND(index_length/(1024*1024*1024),4),'G') AS 'Index Size', CONCAT(ROUND((data_length+index_length)/(1024*1024*1024),4),'G') AS'Total'FROM information_schema.TABLES WHERE table_schema LIKE 'test';// 查询行存数据
USE test;
SELECTl_orderkey,SUM(l_extendedprice * (1 - l_discount)) AS revenue,o_orderdate,o_shippriority
FROMcustomer,orders,lineitem
WHEREc_mktsegment = 'BUILDING'
AND c_custkey = o_custkey
AND l_orderkey = o_orderkey
AND o_orderdate < DATE '1996-01-01'
AND l_shipdate > DATE '1996-02-01'
GROUP BYl_orderkey,o_orderdate,o_shippriority
ORDER BYrevenue DESC,o_orderdate
limit 10;// 将数据同步到TiFlash列存
ALTER TABLE test.customer SET TIFLASH REPLICA 1;
ALTER TABLE test.orders SET TIFLASH REPLICA 1;
ALTER TABLE test.lineitem SET TIFLASH REPLICA 1;// 查询同步状态
SELECT * FROM information_schema.tiflash_replica WHERE TABLE_SCHEMA = 'test' and TABLE_NAME = 'customer';
SELECT * FROM information_schema.tiflash_replica WHERE TABLE_SCHEMA = 'test' and TABLE_NAME = 'orders';
SELECT * FROM information_schema.tiflash_replica WHERE TABLE_SCHEMA = 'test' and TABLE_NAME = 'lineitem';// 使用 EXPLAIN ANALYZE 分析是否使用了TiFlash
// 如果结果中出现 ExchangeSender 和 ExchangeReceiver 算子,表明 MPP 已生效。USE test;
EXPLAIN ANALYZE SELECTl_orderkey,SUM(l_extendedprice * (1 - l_discount)) AS revenue,o_orderdate,o_shippriority
FROMcustomer,orders,lineitem
WHEREc_mktsegment = 'BUILDING'
AND c_custkey = o_custkey
AND l_orderkey = o_orderkey
AND o_orderdate < DATE '1996-01-01'
AND l_shipdate > DATE '1996-02-01'
GROUP BYl_orderkey,o_orderdate,o_shippriority
ORDER BYrevenue DESC,o_orderdate
limit 10;
SQL规则语法差异
主要对比v8.5.1的TiDB和MySQL的部分规则差异,与 MySQL 兼容性对比
- 存储过程函数:TiDB不支持存储过程(Stored Procedures)、自定义函数(UDF)和触发器(Triggers)。示例:CREATE PROCEDURE 会直接报错。仅支持常用的 MySQL 内建函数,有部分函数并未支持。可通过执行 SHOW BUILTINS 语句查看可用的内建函数。
- 外键约束:TiDB不支持外键约束(FOREIGN KEY),DDL 中定义外键会被忽略。示例:FOREIGN KEY (col) REFERENCES tbl(col) 无实际约束效果。
- 修改表字段类型:TiDB仅支持有限修改(如增大 VARCHAR 长度),不支持 ALTER TABLE MODIFY COLUMN 修改类型。MySQL:支持修改列类型。
- 表的自增主键:TiDB自增 ID 不保证严格连续(分布式分配),但可通过 AUTO_ID_CACHE=1 缓解。AUTO_INCREMENT,该模式能确保 ID 严格递增且间隙最小
- 索引支持:TiDB不支持全文索引(FULLTEXT)、空间索引(SPATIAL)。示例:CREATE FULLTEXT INDEX 报错。
- 字符集与排序规则:TiDB支持 utf8mb4,默认就是utf8mb4。但部分排序规则可能与 MySQL 不同(如 utf8mb4_general_ci)。MySQL支持很多字符集,MySQL 5.7 默认:latin1,MySQL 8.0 默认:utf8mb4。
- 事务模型:TiDB默认悲观事务,支持乐观事务,隔离级别为快照隔离(Snapshot Isolation),与 MySQL 的 REPEATABLE-READ 行为不同。
- 当 TiDB 使用悲观锁(自 TiDB v3.0.8 起默认使用)时,TiDB 中 SELECT FOR UPDATE 的行为与 MySQL 中的基本一致。
- 当 TiDB 使用乐观锁时,SELECT FOR UPDATE 不会在事务启动时对数据加锁,而是在提交事务时检查冲突。如果检查出冲突,会回滚待提交的事务。
- 查询计划:TiDB 中,执行计划(EXPLAIN 和 EXPLAIN FOR)在输出格式、内容、权限设置方面与 MySQL 有较大差别。TiDB 执行计划概览
- 日期与时间:日期时间处理的区别,TiDB 采用系统当前安装的所有时区规则进行计算(一般为 tzdata 包),不需要导入时区表数据就能使用所有时区名称,导入时区表数据不会修改计算规则。MySQL 默认使用本地时区,依赖于系统内置的当前的时区规则(例如什么时候开始夏令时等)进行计算;且在未导入时区表数据的情况下不能通过时区名称来指定时区。
SQL语法差异,TiDB技术探索
- DDL的差异:与 MySQL 兼容性对比
- ALTER TABLE 不支持少部分类型的变更。比如,TiDB 不支持从 DECIMAL 到 DATE 的变更。当遇到不支持的类型变更时,TiDB 将会报 Unsupported modify column: type %d not match origin %d 的错误。更多细节,请参考 ALTER TABLE。
- 不支持指定不同类型的索引 (HASH|BTREE|RTREE|FULLTEXT)。若指定了不同类型的索引,TiDB 会解析并忽略这些索引。
- SELECT的差异:
- TiDB的 SELECT … GROUP BY expr 的返回结果与 MySQL 5.7 并不一致。MySQL 5.7 的结果等价于 GROUP BY expr ORDER BY expr,另外就是TiDB支持对GROUP BY 使用别名
- UPDATE的差异:在计算表达式中的列时,TiDB 总使用原始的值,先赋值再运算
- DELETE、REPLACE、INSERT语句与mysql语法完全兼容。
数据迁移工具
数据迁移工具概览
工具 | 适用场景 | 注意事项 |
---|---|---|
Dumpling | 全量数据导出(替代mysqldump) | 支持大表并行导出,默认保存为SQL文件 |
TiDB Lightning | 快速导入全量数据到TiDB | 需禁用外键检查,建议分批次导入大表 |
DM (Data Migration) | 实时增量同步(类似CDC) | 需配置source/destination,处理binlog |
TiDB Data Migration(DM)
TiDB Data Migration (DM) 是一款便捷的数据迁移工具,支持从与 MySQL 协议兼容的数据库(MySQL、MariaDB、Aurora MySQL)到 TiDB 的全量数据迁移(TB级别以下的数据量)和增量数据同步,并且支持分库分表数据的合并迁移。
通常数据量较低时,使用 DM 进行迁移较为简单,可直接完成全量+持续增量迁移工作。但当数据量较大时,DM 较低的数据导入速度 (30~50 GiB/h) 可能令整个迁移周期过长。本文所称“大数据量”通常指 TiB 级别以上。
使用 DM 工具就是在TiDB集群中启动一个DM Master以及多个DM Slave服务节点,有利于简化数据迁移过程,降低数据迁移运维成本。
需要注意MySQL 版本 5.6 ~ 8.0,以及TiDB目前只兼容了MySQL的部分DDL,需要不兼容的DDL会报错,跳过或者手动处理,另外视图的DDL不会同步,DM 在 v5.4.0 之前不支持将 charset=GBK 的表迁移到 TiDB。DM 不支持 MySQL 8.0 的新特性 binlog 事务压缩 Transaction_payload_event。使用 binlog 事务压缩有导致上下游数据不一致的风险。
使用步骤demo
// 安装TiDB DM
curl --proto '=https' --tlsv1.2 -sSf https://tiup-mirrors.pingcap.com/install.sh | sh// 启动TiDB以及DM组件
tiup playground v8.5.1 --dm-master 1 --dm-worker 1 --tiflash 0 --without-monitor// 给待迁移的MySQL数据库创建一个角色
CREATE USER 'tidb-dm'@'%'IDENTIFIED WITH mysql_native_passwordBY 'MyPassw0rd!';GRANT PROCESS, BACKUP_ADMIN, RELOAD, REPLICATION SLAVE, REPLICATION CLIENT, SELECT ON *.* TO 'tidb-dm'@'%';// 通过yaml文件配置TiDB DM数据源
source-id: "mysql-01"
from:host: "127.0.0.1"user: "tidb-dm"password: "MyPassw0rd!" # In production environments, it is recommended to use a password encrypted with dmctl.port: 3306// 创建DM数据源节点
tiup dmctl --master-addr 127.0.0.1:8261 operate-source create mysql-01.yaml// 通过yaml文件配置创建DM数据同步任务
# Task
name: tiup-playground-task
task-mode: "all" # Execute all phases - full data migration and incremental sync.# Source (MySQL)
mysql-instances:- source-id: "mysql-01"## Target (TiDB)
target-database:host: "127.0.0.1"port: 4000user: "root"password: "" # If the password is not empty, it is recommended to use a password encrypted with dmctl.// 创建DB任务完成全量、增量数据同步
tiup dmctl --master-addr 127.0.0.1:8261 start-task tiup-playground-task.yaml
Dumpling和TiDB Lightning
因此,本文档介绍如何使用 Dumpling 和 TiDB Lightning 进行全量数据迁移。TiDB Lightning 物理导入模式的导入速度最高可达每小时 500 GiB,注意实际导入速度受硬件配置、表结构、索引数量等多方面因素的影响。完成全量数据迁移后,再使用 DM 完成增量数据迁移。
使用数据导出工具 Dumpling,你可以把存储在 TiDB 或 MySQL 中的数据导出为 SQL 或 CSV 格式,用于逻辑全量备份。
// 安装
tiup install dumpling// 查看版本
tiup dumpling -V// 导出数据
// 参数filetype:支持sql、csv
// 参数o:导出文件路径
// 参数t:导出任务线程数,过多会对数据库造成压力
// 参数r:用于开启表内并发加速导出。默认值是 0,表示不开启。取值大于 0 表示开启,取值是 INT 类型。
// 参数F:选项用于指定单个文件的最大大小,单位为 MiB,可接受类似 5GiB 或 8KB 的输入。如果你想使用 TiDB Lightning 将该文件加载到 TiDB 实例中,建议将 -F 选项的值保持在 256 MiB 或以下。
tiup dumpling -u root -P 3306 -h 127.0.0.1
--filetype sql
-t 8
-o /Users/sichaolong/Documents/my_projects/my_idea_projects/tidb-demo/springboot-jdbc-tidb-demo/src/main/resources/tidb/data
-r 200000
-F 256MiB// 另外其他参数
// --consistency <consistency level>,可选snapshot表示基于某时间戳的快照导出,lock表示会给待导出的表加锁
// -B test:从 test 数据库导出。
// -f 'test.t[12]':只导出 test.t1 和 test.t2 这两个表。tiup dumpling -u root -p 12345678 -P 3306 -h 127.0.0.1 --filetype sql -t 8 -o /Users/sichaolong/Documents/my_projects/my_idea_projects/tidb-demo/springboot-jdbc-tidb-demo/src/main/resources/tidb/data -F 256MiB
// 会生成表的schema和具体数据的插入sql以及meata文件,这个文件会记录binlog位置信息
仅导出表结构
tiup dumpling -u root -p 12345678 -P 3306 -h 127.0.0.1 \
--filetype sql \
-t 8 \
-o /Users/sichaolong/Documents/my_projects/my_idea_projects/tidb-demo/springboot-jdbc-tidb-demo/src/main/resources/tidb/data \
-F 256MiB \
--where "1=0"
参数说明
参数 | 作用 | 必要性 |
---|---|---|
<font style="color:rgb(51, 51, 51) !important;">--filetype sql</font> | 指定导出为 SQL 格式 | 必要 |
<font style="color:rgb(51, 51, 51) !important;">--where "1=0"</font> | 过滤所有数据行,仅导出表结构 | 必要 |
<font style="color:rgb(51, 51, 51) !important;">-F 256MiB</font> | 控制单个文件大小(不影响 Schema 导出) | 可选 |
<font style="color:rgb(51, 51, 51) !important;">-t 8</font> | 并发线程数(对 Schema 导出影响较小) | 可选 |
-T db.WorkOrder。 | -T 选项只能接受完整的 库名.表名 形式,不支持只指定表名。例:Dumpling 无法识别 | 可选,–filter 选项与 -T 选项不可同时使用。 |
-B employees | 导出 employees 数据库 | 可选 |
仅导出特定表的表结构
tiup dumpling -u xkw -p xxx -P 3306 -h xxx \
--filetype sql \
-t 8 \
-o /Users/sichaolong/Documents/my_projects/my_idea_projects/tidb-demo/springboot-jdbc-tidb-demo/src/main/resources/qbm_dev/dumpling \
-F 256MiB \
-T qbm.kpoints_tagging_scene
--where "1=0"
导入模式
使用TiDB Lightning根据dumpling全部数据导入到TiDBTiDB Lightning 目前支持两种导入方式,通过 backend 配置区分。不同的模式决定 TiDB Lightning 如何将数据导入到目标 TiDB 集群。
- 物理导入模式:TiDB Lightning 首先将数据编码成键值对并排序存储在本地临时目录,然后将这些键值对上传到各个 TiKV 节点,最后调用 TiKV Ingest 接口将数据插入到 TiKV 的 RocksDB 中。如果用于初始化导入,请优先考虑使用物理导入模式,其拥有较高的导入速度。物理导入模式对应的后端模式为 local。
- 逻辑导入模式:TiDB Lightning 先将数据编码成 SQL,然后直接运行这些 SQL 语句进行数据导入。如果需要导入的集群为生产环境线上集群,或需要导入的目标表中已包含有数据,则应使用逻辑导入模式。逻辑导入模式对应的后端模式为 tidb。
许多用户反馈 TiDB Lightning 的部署、配置、维护比较复杂,特别是在处理大数据量并行导入的场景中。
针对此问题,TiDB 逐渐将 TiDB Lightning 的一些功能整合到 IMPORT INTO 语句中。你可以直接通过执行
IMPORT INTO
导入数据,从而提升导入数据的效率。此外,IMPORT INTO
支持某些 TiDB Lightning 不支持的功能,例如自动分布式任务调度和 TiDB 全局排序。与 TiDB Lightning 相比,
<font style="color:rgb(38, 42, 44);">IMPORT INTO</font>
语句可以直接在 TiDB 节点上执行,支持自动化分布式任务调度和 TiDB 全局排序,在部署、资源利用率、任务配置便捷性、调用集成便捷性、高可用性和可扩展性等方面都有很大提升。建议在合适的场景下,使用<font style="color:rgb(38, 42, 44);">IMPORT INTO</font>
代替 TiDB Lightning。
// 安装tidb-lightning
tiup install tidb-lightning// 编写配置文件// 导入
nohup tiup tidb-lightning -config tidb-lightning.toml > nohup.out &
// 导入出现tidb lightning exit successfully表示成功
tidb-lightning.toml配置文件
// 编写导入TiDB配置文件
[lightning]
# 日志
level = "info"
file = "tidb-lightning.log"[tikv-importer]
# 选择使用的导入模式
backend = "local"
# 设置排序的键值对的临时存放地址,目标路径需要是一个空目录
sorted-kv-dir = "/Users/sichaolong/Documents/my_projects/my_idea_projects/tidb-demo/springboot-jdbc-tidb-demo/src/main/resources/tidb/sorted-kv-dir"[mydumper]
# 源数据目录。
data-source-dir = "/Users/sichaolong/Documents/my_projects/my_idea_projects/tidb-demo/springboot-jdbc-tidb-demo/src/main/resources/tidb/data/"# 配置通配符规则,默认规则会过滤 mysql、sys、INFORMATION_SCHEMA、PERFORMANCE_SCHEMA、METRICS_SCHEMA、INSPECTION_SCHEMA 系统数据库下的所有表
# 若不配置该项,导入系统表时会出现“找不到 schema”的异常
filter = ['*.*', '!mysql.*', '!sys.*', '!INFORMATION_SCHEMA.*', '!PERFORMANCE_SCHEMA.*', '!METRICS_SCHEMA.*', '!INSPECTION_SCHEMA.*']
[tidb]
# 目标集群的信息
host = "127.0.0.1"
port = 4000
user = "root"
password = ""
# 表架构信息在从 TiDB 的“状态端口”获取。
status-port = 10080
# 集群 pd 的地址。从 v7.6.0 开始支持设置多个地址。
pd-addr = "127.0.0.1:2379"