做了几年后端经常被人问起TiDB到底是个什么东西。这问题听着简单真要讲清楚却不容易。如果你去搜TiDB会看到一堆官方说法开源分布式关系型数据库、兼容MySQL协议、支持水平扩展、金融级高可用……这些都对但对一个只想评估我能不能用或者我该怎么用的人来讲还是太抽象。我在生产环境里断断续续折腾过两年TiDB也从MySQL迁过业务过来这篇文章想用我的实际经历把这个数据库的架构逻辑、适合场景、部署要点、迁移过程中容易踩的坑尽量讲得接地气一点。无论你是刚开始了解分布式数据库还是已经在做选型对比这篇都应该能给你一些参考。1. 先把架构说清楚TiDB不是一个数据库而是一套组件很多人第一次看TiDB的架构图就懵了因为它跟MySQL、PostgreSQL这种单体数据库长得完全不一样。MySQL你装一个服务连上3306端口就能用所有东西都在一个进程里。TiDB不是这样它天生就是分布式架构由好几个组件配合工作而且每个组件都可以单独缩扩。1.1 三个核心组件各自扮演的角色TiDB这套东西最核心的是三个组件我用大白话给你捋一遍TiDB Server这是入口层或者说计算层。它负责接收客户端发过来的SQL做解析、生成执行计划、执行计算然后把结果返回给客户端。这一层跟你平时用的MySQL服务端角色很像但它本身不存数据它是无状态的。这意味着你可以随便多挂几个TiDB Server实例前面加一层负载均衡承担更大的查询并发。我做压测的时候第一次体会到这个设计的好处查得慢加一个TiDB Server节点就行不用动存储层。PDPlacement Driver这是整个集群的大脑和调度中心。它负责管理整个集群的元数据给每个数据分片Region分配存储位置生成全局单调递增的事务时间戳TSO还要做Leader调度、存储节点的负载均衡这些事。PD同时也是集群的仪表盘你用PD可以查集群里有多少Region、每个TiKV节点的状态是否健康。PD通常建议部署奇数个节点比如3个或5个它们之间自己会通过选举机制保证高可用。TiKV这是真正的数据存储层。它是分布式Key-Value存储引擎但对外表现得像一棵巨大的、有序的Map。所有数据都会按照Key范围切成一片片Region默认每个Region大约96MB然后每一片Region在多个TiKV节点上存多份副本通过Raft协议保证共识。TiKV才是TiDB跟传统单机数据库拉开差距的地方——数据不是存在一块盘上而是打散到几十台甚至上千台机器的盘上写入和存储能力随节点数量近乎线性增长。除了这三个还有TiFlash这个列存引擎。它不是必装的但如果你有分析类查询最好配上。TiFlash通过特殊的Raft Learner角色异步从TiKV同步数据把行存数据转成列存格式专门跑OLAP类的大查询。这样你在同一套集群里跑事务和跑分析两边各用各的引擎互不干扰。我实际用下来TiFlash在几千万行的大表聚合查询上提速基本是两位数级别的。1.2 Region分片与Raft协议数据如何被拆散又保证一致要理解TiDB的存储关键要理解Region。TiDB把一张表的所有数据按主键或者隐藏的_rowid的字典序排列这个有序的Key空间会被切分成一系列连续的区间每个区间就是一片Region。当某个Region的数据量超过大小限制默认96MB它会自动分裂成两个当集群里有些Region特别热比如被人疯狂读写PD会把它的Leader副本调度到其他节点上去分摊压力。这套机制用一句话概括就是不需要你手动分库分表数据自己在底层就分好了。我见过很多业务方早期在MySQL上做分库分表分片键一旦定错后面数据倾斜、跨分片查询都是大麻烦。在TiDB上数据分片这件事是自动的应用层基本无感知。那怎么保证副本之间数据一致呢这里靠Raft协议。每一片Region默认有三个副本当然你也可以配置为五副本这三个副本分别落在不同的TiKV节点、最好是不同的物理机上。写入的时候Leader负责接收请求然后把写入日志同步给Follower只有大多数副本比如三副本中的两个都确认写入了这次写入才算成功。这个过程保证了即使某台机器突然断电或者宕机集群依然能正常对外服务也不会丢数据。我对Raft的理解是它用少数服从多数的机制把分布式系统里最头疼的一致性问题变成了一个清晰可验证的流程。1.3 一条SELECT语句在TiDB里经历了什么从用户角度看你连上TiDB执行一条SQL跟连MySQL没多大区别但底层路径其实很长。我画个走查流程你就明白了客户端把SQL发给任意一个TiDB Server。TiDB Server做词法解析、语法验证生成逻辑执行计划然后基于统计信息做优化生成物理执行计划。执行计划里有TableReader、IndexLookUp之类的算子TiDB Server会把需要扫数据的任务下推给对应Region所在的TiKV节点这就是所谓计算下推。多个TiKV节点并行扫描各自负责的Region返回数据给TiDB Server。TiDB Server把各节点返回的数据做最后的聚合、排序、Join最终把结果集返回给客户端。整个过程对你来说就是一条正常的SQL但对TiDB来说它已经把本地计算拆成了真正的分布式并行计算。理解了这条路径后面很多调优思路就顺了比如为什么TiDB特别看重统计信息因为优化器生成计划全靠它为什么大表Join有时候慢因为数据要跨节点传输。2. 选型之前TiDB适合什么业务不适合什么业务TiDB不是万能数据库甚至可以说它适用于的场景是有明确边界的。我在社区里见过不少上线前吹得天花乱坠上线后发现问题一堆的案例核心原因就是选型时没想清楚。这里我把自己实际见过的情况总结一下。2.1 我见过的典型适合场景增长型的互联网业务先说适合的。第一类就是业务增长速度快、数据量可能很快就上亿甚至几十亿行的场景。在MySQL里单表超过几千万行之后即使有索引写入和查询都会明显变慢你要不断去做归档、分表这些事情每件事都很费人。在TiDB上数据量增长带来的是Region不断分裂、自动分布到更多节点你只需要保证存储节点数量跟得上就行。第二类是高并发写入比较突出的业务比如订单系统、交易流水、IoT设备上报、监控数据采集这些。TiDB的写入可以分散到多个TiKV节点的多个Leader上不像单机数据库只有一个主节点扛写入。我在压测的时候三节点集群跑普通混合读写峰值能到几万TPS这还只是默认配置没有做大规模调优。第三类是原来用了分库分表中间件业务已经被拆得很难受的项目。分库分表下跨分片的COUNT、JOIN、事务都很难处理。TiDB对这种业务几乎是降维打击你不需要分片键了也不用关心数据落在哪个库哪张表里SQL怎么写都行分布式事务由数据库自己搞定。我们当时把一个订单分表业务迁到TiDB应用层删掉了一堆路由和聚合逻辑代码量减少非常明显。2.2 不建议用的场景别被兼容MySQL误导再说说不适合的。TiDB虽然兼容MySQL协议但千万不要以为它跟MySQL是完全等价的东西。第一如果你的事务里包含非常复杂的跨行操作、而且对延迟极度敏感TiDB可能不是最优解。分布式事务在底层需要多节点协调天然比单机事务的开销大。你跑一个单行UPDATE在MySQL上可能0.5毫秒完成在TiDB上可能要到1到2毫秒甚至更多。如果你的业务大量依赖这样的短事务而且对单次请求延迟非常苛刻我建议你仔细评估。第二极端复杂的SQL比如超深度的子查询嵌套、非常复杂的存储过程逻辑在TiDB上的优化能力目前还没法和成熟的单机数据库比。TiDB对复杂的分析查询可以通过TiFlash的MPP能力解决但那种高复杂度的PL/SQL类业务迁过来可能会发现有些写法不支持或者执行效率不高。第三数据量其实很小、并发也不高的场景真的没必要用TiDB。三节点的集群最低配置也得几台机器运维复杂度也高于单机数据库数据量要是连千万行都没有完全是杀鸡用牛刀。2.3 横向对比面对分库分表和NewSQL怎么取舍我经常碰到的一个问题是我们有MySQL分库分表方案也有TiDB方案怎么选我的建议是分情况。如果你的团队有很强的中间件研发能力分库分表方案比如ShardingSphere也用了好几年业务上的分片键设计得很稳SQL也被限制得很规范那继续用分库分表没有太大问题。分库分表的优势是底层还是单机数据库运维经验成熟生态丰富。但短板也很明显跨分片操作难、扩缩容要迁移数据、分片键改起来等于重构。TiDB的优势是不分片胜似分片应用层不需要知道数据分布细节扩容就是加节点官方工具自动搬迁数据。它更适合那种不想把大量精力投入到底层数据路由、希望数据库自己能弹性扩展的团队。顾此失彼的点是硬件和运维要求更高很多内核参数你没办法像MySQL那样用一个配置文件都管明白。另外TiDB也经常跟其他NewSQL产品对比比如OceanBase、CockroachDB这些。OceanBase在金融行业里落地很重强调强一致和Oracle兼容性CockroachDB更偏全球多活部署。TiDB的突出优势在于MySQL生态兼容度高、社区文档中文资料丰富、开源版本功能也基本完整。单说上手难度这一块我用TiDB时的体验是明显更平滑的。3. 实操用TiUP在3台机器上搭一个最小集群TiDB早期部署是真的麻烦要自己处理各种配置和组件间的连接关系。但后来官方出了TiUP这个运维工具之后部署和运维的复杂度大幅下降。我现在搭环境基本都是这路子给团队演示也这么干整个过程半小时内搞定。3.1 安装环境和TiUP初始化TiUI推荐的最低配置是单节点8核16G起步如果你想跑一个功能完整的实验集群最少也需要3台机器用于PD和三副本TiKV。我自己搭测试集群时用的是3台4核8G云主机拿来演示和跑Demo足够但要压测就有点吃力。拿到机器后第一件事是在其中一台一般是中控机装TiUP。前提是这台机器能SSH免密登录到所有目标节点而且目标节点上有具备sudo权限的普通用户。命令很简单curl --proto https --tlsv1.2 -sSf https://tiup-mirrors.pingcap.com/install.sh | sh装完之后要让TiUP的路径临时生效。然后初始化一个最小集群名字我通常取eshop或者test看项目心情tiup cluster deploy test v7.8.0 ./topology.yaml --user tidb -p其中v7.8.0是集群版本号topology.yaml是拓扑文件--user指定用tidb这个用户执行部署。这个命令会自动把二进制分发到各目标机器并启动所有组件第一次执行会等比较久。3.2 最小拓扑文件怎么写拓扑文件是整个部署流程的核心TiDB所有组件该部署在哪些机器、目录是什么、端口是什么都在这里定义。我最简的测试拓扑大概是这样的global: user: tidb ssh_port: 22 deploy_dir: /tidb-deploy data_dir: /tidb-data pd_servers: - host: 10.0.1.1 - host: 10.0.1.2 - host: 10.0.1.3 tidb_servers: - host: 10.0.1.1 tikv_servers: - host: 10.0.1.1 - host: 10.0.1.2 - host: 10.0.1.3 tiflash_servers: - host: 10.0.1.2 - host: 10.0.1.3 monitoring_servers: - host: 10.0.1.1 grafana_servers: - host: 10.0.1.1 alertmanager_servers: - host: 10.0.1.1你可以看到PD是三节点TiKV也是三节点TiDB Server只放了一台。生产环境里TiDB Server一般至少两台PD和TiKV建议严格独立部署不要混部。测试环境混一混没关系生产这么搞会互相抢资源。部署成功之后启动集群tiup cluster start test再装一个MySQL协议的命令行客户端就能通过4000端口连上TiDB了mysql -h 10.0.1.1 -P 4000 -u root3.3 部署完先跑一遍这些验证动作集群起来之后我建议不要急着写业务代码先做几个基础验证确认架构没问题。第一用tiup cluster display test查看所有节点状态确保所有TiKV和PD都是Up状态。第二在MySQL客户端里执行select tidb_version();和select * from information_schema.cluster_info;确认版本和集群组件信息。第三试着建一张测试表灌一点数据再执行admin show ddl;看看有没有DDL任务在跑。最后打开TiDB Dashboard看一眼。Dashboard在TiDB Server的HTTP端口上默认访问地址是http://10.0.1.1:2379/dashboard如果PD和TiDB混部的话。这里面能直观看到集群QPS、延迟、Top SQL语句、异常Region等一大堆信息。我现在排查问题第一站就是Dashboard。4. 从MySQL迁过来工具链和最容易踩的坑有不少人来问TiDB是因为MySQL已经扛不住了想找个平滑迁移方案。TiDB在这方面的工具链做得算是比较成熟的但迁移过程远没有官方文档写的那么云淡风轻。我把实际迁移经验拆成工具和坑两个部分讲。4.1 迁移工具全家桶Dumpling、Lightning、DM迁移MySQL数据到TiDB最常用的是三条工具链Dumpling是数据导出工具可以理解为MySQLmysqldump的分布式版本。它能把MySQL中的数据导出成SQL文件或者CSV格式而且支持一致性快照导出不会影响源库线上业务。它有别于mysqldump的一点是对TiDB的大表会自动分区域table-range并发导出效率高很多。TiDB Lightning是数据导入工具用来把导出的数据快速灌进TiDB。注意它的两种导入模式物理导入模式Physical Import Mode会直接生成SST文件让TiKV直接加载速度极快我们曾经用它在40分钟左右导入了约2亿行数据逻辑导入模式Logical Import Mode则走正常SQL写入速度慢一些但可以在不停集群的情况下跑。生产环境里如果TiDB是新建的空集群强烈建议用物理导入模式能省几个小时。**DMData Migration**是持续同步工具可以把你MySQL上的增量binlog实时同步到TiDB。通常的迁移节奏是先用Dumpling导出存量数据、Lightning导入然后用DM追增量最后在某一刻把写流量切到TiDB上。这套流程官方叫全量增量迁移是生产环境最通用的方案。# 导出MySQL全量数据 dumpling -h 127.0.0.1 -P 3306 -u root -p password \ --filter my_db.* -o /backup/my_db # 物理导入到TiDB需要先保证TiDB集群已关闭相关region调度限制 tiup tidb-lightning -config lightning.tomllightning.toml里最重要的配置是数据源目录和目标集群地址。第一次跑的时候建议先导一个小库试试验证整个链路通不通。4.2 DDL、事务和隔离级别上的兼容性陷阱这块是我最想提醒的。TiDB跟MySQL的兼容性是协议级的不代表行为级的完全一致。**DDL的差异就非常典型。**MySQL 5.7之前的DDL很多会锁表TiDB的所有DDL都是在线执行这个倒是优势。但要注意TiDB的DDL是异步执行的你执行ALTER TABLE ... ADD INDEX之后命令可能已经返回但索引还在后台慢慢建而且同一时间只能有一个DDL任务在执行排在后面的等前一任务完成。我第一次加索引时发现没生效查了ADMIN SHOW DDL才发现任务还在队列里。事务隔离级别方面TiDB默认是REPEATABLE-READ但它实现的其实是快照隔离Snapshot Isolation跟MySQL的RR底层机制不一样。最直观的体验差别是TiDB的RR下没有幻读问题但也因此不支持SELECT ... FOR UPDATE在RR下的某些行为。如果你在MySQL里依赖RR下的间隙锁来防并发迁到TiDB后必须重新审视这段逻辑否则可能出现并发数据不一致。事务大小限制也是一个容易撞上的坑。TiDB默认限制单个事务总写入大小约为100MB实际上根据版本不同有差异最稳妥的说法是TiDB里要避免超大事务。我在早期迁移时遇到过一个大事务超过限制直接报错的情况解决办法是把批量更新拆成多个小批次执行比如每批500条或者1000条。4.3 自增主键、字符集、时区细节决定体验这些看起来都是小事迁移时却最容易出问题。自增主键是第一个需要注意的。TiDB的AUTO_INCREMENT保证唯一但不保证连续而且单机版的连续自增值对分布式数据库来说就是写入热点——所有写入都集中到最后那个Region上。如果表没有明确的主键TiDB默认加一个_tidb_rowid作为主键这个隐式主键也是自增的照样有热点风险。解决方式是用AUTO_RANDOM替代自增主键让主键值随机散列到各个Region上CREATE TABLE t ( id BIGINT PRIMARY KEY AUTO_RANDOM, name VARCHAR(64) );AUTO_RANDOM生成的ID像随机数一样分布写入压力就被分散了。但要注意应用层如果依赖自增ID大小来做业务判断比如ID小的是早期用户换成AUTO_RANDOM就不合适了需要改用其它字段。字符集和排序规则也很烦。TiDB早期版本的排序规则支持不完整如果你在MySQL里用utf8mb4_unicode_ci之类的规则迁移到TiDB后可能发现大小写敏感了或者反过来。建议迁移前先查一下目标TiDB版本对collation的支持范围尽量在源库就把表结构里的排序规则统一成TiDB兼容的。时区也是个隐蔽问题。TiDB每个连接有自己的time_zone会话变量如果客户端连接时没设置默认用的可能是系统时区跟业务服务器不一致最终导致写入时间偏移几个小时。我们迁移时专门在连接串里显式配置了时区参数才避免了一波时间错乱事故。5. 上线之后的日常监控指标、热点治理与运维节奏数据库上线不是终点而是运维的起点。TiDB的运维和MySQL完全是两套思路。MySQL出问题你看慢查询日志、看show processlist基本能定位一半。TiDB因为分布式你得学会同时看监控面板和分布式日志。5.1 Dashboard上必须盯住的几个面板TiDB Dashboard自带了不少监控面板但我建议重点盯这几个指标集群QPS和延迟这个不稀奇主要看有没有明显的尖刺和周期性抖动。Top SQL语句Dashboard会根据SQL指纹自动聚合耗时最长的SQL这个是排查性能问题的第一入口。我很多次优化都是从Top SQL列表里捞出来的罪魁祸首。热点RegionDashboard里有专门的热点分析页面能定位哪些Region的读或写流量异常高。这个页面在数据倾斜排查时是神器。TiKV节点的CPU和存储使用率如果某个TiKV节点CPU明显高于其他节点大概率是热点或者数据倾斜了。如果是老手还可以直接看Grafana上的TiKV面板重点看raftstore、apply、gRPC的耗时。这几个指标直接反映了TiKV底层处理Raft日志和应用写入的效率。5.2 读写热点怎么定位和拆解热点问题在TiDB里几乎是每天都要面对的。常见情况有几种写入热点最典型的就是自增主键/自增rowid导致的单Region热点以及某张业务表的数据天然集中在某个Key范围比如按时间序列写入的日志表新数据永远落在最新Region。定位方法是在Dashboard热点页面里看写流量是否集中在某个节点或某几个Region。拆解思路也明确换成AUTO_RANDOM、对主键做哈希散列、或者把表按业务维度加一个前缀字段把数据打散。读热点某一行数据被高频读取比如商品详情、活动配置导致一个Region撑不住。拆解法有几种一是对这条数据做缓存Redis挡掉一部分读压力二是如果读取可以通过TiFlash承担把分析类查询挪过去三是考虑用TiDB本身的缓存能力比如把热点小表的数据直接放到PD调度里让多个副本分散读流量。小表还容易出另一个问题Region长期只有一个或几个读并发全怼上去了TiDB有split table之类的操作可以手动把小表的Region切得更碎但一般建议从业务上先想办法做缓存。5.3 那些不能乱调的参数GC、Region合并、线程池TiDB有很多内核参数可以直接修改但我不建议上来就改。说几个踩过的教训tidb_gc_life_time是控制历史数据保留时间的默认10分钟。它决定了MVCC版本里旧版本数据的存活时间如果你要跑一个长时间运行的查询比如跨小时的大分析而GC一直在清理旧版本查询会报cannot read之类的错误。这时候可以临时把这个参数调大但千万不要永久调大因为旧版本数据堆积会让TiKV存储占用飙升、查询性能下降。我见过有同事把它调成一小时然后忘记改回来结果存储量几天涨了一倍。Region合并也是一个容易被忽视的点。Region不是越大越好也不是越多越好。如果Region太小太多PD的调度压力会增大集群整体性能可能反而下降。TiDB默认有自动合并机制但当你的表写入了大量数据、又删除了一部分会产生很多空的或者很小的Region这时候就等着PD慢慢合并。一般不用管但如果你发现Region数量异常多且持续不降可以考虑用pd-ctl手动对某张表做scatter region或者调整合并相关的参数。线程池参数、内存参数这些建议走先测后调、小步迭代的路线。每一版TiDB的内核默认值都是经过大规模测试验证的多数情况下默认值比你的拍脑袋改法靠谱。真正要优先做的是把磁盘从HDD换成SSD、把网络从千兆升级到万兆这些外部环境的影响比参数调优大多了。6. 关于TiDB性能调优我的一些心得TiDB性能调优这事网上文章很多但大多局限在加索引和改参数两层。我实际用下来的感受是TiDB的调优思路和MySQL有很大区别因为它多出了分布式执行计划和数据分布这两个维度。6.1 慢查询分析的完整路径碰到一条SQL很慢我的排查路径基本固定先到Dashboard的Top SQL里把慢SQL捞出来看它的执行计划。在MySQL里你习惯用EXPLAIN在TiDB里也一样但TiDB的EXPLAIN输出里有很多特有的字段比如task列会标出这个算子是在TiDB Serverroot上执行还是在TiKV/TiFlashcop上执行。如果一个扫描算子是cop[tikv]说明数据扫描是下推到存储层并行执行的这是好事如果某些算子只能在root上处理比如不合适的Join、子查询展开那就要想办法改写SQL。拿到执行计划后第一步看有没有全表扫。TiDB里一个大表全表扫即使并行代价也很高。第二步看估算行数rows和实际返回行数是否差距很大。如果差距大通常是统计信息没过新执行ANALYZE TABLE刷新一下优化器可能马上就选出正确的索引。第三步看Join的顺序和算法是不是可以用INFORMATION_SCHEMA.TIDB_HOT_REGIONS之类的表去核查数据分布辅助判断是不是数据倾斜影响了速度。6.2 Join、子查询、窗口函数的优化思路TiDB执行Join时最怕的是大表之间做Hash Join因为要HAVING跨节点搬运数据网络开销很大。优先的优化方向是用索引来走Index Join前提是你得保证驱动表足够小并且被驱动表上有能用的索引。子查询也是要小心的地方。TiDB通常会把子查询改写成Join来执行但有些写法比如IN后面跟一个大子查询可能会导致执行计划退化。如果遇到慢查询我一般会手动把子查询拆出来跑一遍看看或者改成EXISTS、改成临时表Join对比哪个执行计划更优。窗口函数在TiDB上执行时排序和分组都是在TiDB Server内存里做的。如果数据量巨大可能出现内存占用过高的问题。优化办法是尽量减少窗口函数处理的粒度可以先在SQL里用子查询把数据量压小再套窗口函数。另外TiDB对MPP架构支持之后TiFlash上跑窗口函数会更快但前提是把这些查询路由到TiFlash可以建列存副本用优化器hint指定。6.3 别忽视客户端连接与TiDB Server的交互最后说说一个容易翻车的点客户端连接池和TiDB Server之间的交互方式对性能的影响往往比SQL本身还大。TiDB是多TiDB Server架构客户端如果只连接其中一个节点那这个节点就成了新的单点瓶颈。所以我强调过生产环境里客户端一定要通过负载均衡如LVS、HAProxy、或者应用层连接池的多地址配置把请求分散到各个TiDB Server上。同时连接池的大小也要克制TiDB每个连接都会有对应的内存开销连接数设得过大比如单机几百上千内存直接吃满TiDB Server的稳定性就会出问题。我们线上TiDB Server的内存曾经被超大的连接池撑爆过后来把最大连接数控制在一个合理范围状况才稳定下来。另外要留意max_prepared_stmt_count这个变量如果应用大量使用预处理语句超过上限就会报错。这些交互层面的东西官方文档通常不会花大篇幅强调但生产环境里它们往往才是决定体验的细节。7. 写在最后我对TiDB的一些真实体会前前后后折腾了这么久我最大的体会是TiDB是一个设计哲学很鲜明的数据库它用自动分片和Raft共识把分布式系统的复杂度尽量收拢起来让你可以像用单机数据库一样去操作一个水平扩展的集群。但这也意味着它的底子跟MySQL终究不是一回事你可以在心智上把它当MySQL用但遇到问题如果还用MySQL那一套老经验去硬套一定会碰壁。如果让我给正在评估TiDB的人一个建议那就是先小范围跑一个真实业务的影子测试不要直接拿核心库迁移。把Dumpling导一份数据、Lightning导入、DM同步跑起来用真实查询跑一周你很快就会知道你的业务适不适合TiDB。第二件事就是一定要重视部署架构测试环境可以随便混部生产环境请把PD、TiDB Server、TiKV分开部署存储用SSD这比之后调任何参数都有价值。我自己就是从线上一次CPU争用事故里学到的这个道理——混部节省的那点机器成本远不够抵一次故障排查的时间成本。