前言TiDB 是一款优秀的分布式数据库,定位于满足OLTP和OLAP的融合型数据库产品,实现了一键水平伸缩、强一致性的多副本数据安全、分布式事务、实时 OLAP 等重要特性。同时兼容 MySQL 协议和生态,迁移便捷,运维成本低。

TiDB 现已被近 1000 家不同行业的领先企业应用在实际生产环境,例如美团、小米、知乎、微众银行等,还包括一些海外客户(如日本的PayPay、东南亚的Shopee和ZaloPay)。个人认为TiDB有可能是未来与老美的基础软件竞争中,实现国产数据库弯道超车的一个机会。

4ded4412b7165c23fe5246ddeeeeb472.png

TiDB诞生至今还不到5年,是很年轻的一个数据库,目前仍在快速的迭代更新中,本文主要是从主键方面谈一谈个人在使用TiDB过程中遇到的问题和思考。


TiDB的主键

TiDB在默认情况下,一旦一张表创建成功,其主键就不能再改变。如果需要添加/删除主键,需要在 TiDB 配置文件中将 alter-primary-key 设置为 true,并重启 TiDB 实例使之生效。而对于在未开启该功能时创建的整数类型的主键的表,即使开启添加/删除主键功能,也不能删除其主键约束。不管日后是否会变更主键,建议在建表之前将alter-primary-key 设置为 true。但一般不建议变更主键,因为这是个重量级的操作。

几个踩坑点

1. 无主键影响数据同步

在TiDB给下游的TiDB/MySQL同步数据时,若需要同步的表中存在没有主键且没有唯一索引的表,则可能会导致binlog性能下降,容易导致数据同步较慢。

因此,一般情况下,对于TiDB中的表,建议都要加上主键或唯一索引。

2. 自增主键非全局单调

TiDB的自增主键(AUTO_INCREMENT)在原理实现上与MySQL有一些差异:每个 TiDB 节点在分配 主键ID 时,都申请一段ID 作为缓存,用完之后再去取下一段(下一批),而不是每次分配都向存储节点申请。

这意味着,TiDB的AUTO_INCREMENT自增值在单节点上能保证单调递增,但在多个节点下则可能会存在剧烈跳跃。

6d3cf5d7d52301ec3823ce872fefd083.png

从上图可看出,虽然整体上大部分主键值是单调有序的,但由于会出现部分跳跃较大、非单调有序的自增值,因此,在多节点下,TiDB的AUTO_INCREMENT自增值从全局来看,并非绝对单调递增的,也即并非绝对有序的

TiDB是分布式数据库,在线上环境一般也会部署多个节点,而每个节点都是使用各自缓存的 AUTO_INCREMENT自增值,使用完后才会再去申请下一批值,而不同节点对于各自缓存中的AUTO_INCREMENT自增值的消耗速率有可能是不均匀的,这是TiDB AUTO_INCREMENT自增值的顺序不具有全局单调性的一个重要原因

3. 自增主键易导致热点

前面提到使用TiDB默认的自增主键时,虽然从全局来看,并非绝对的顺序递增,但大部分是顺序递增的,只有少部分会跳跃或非单调有序,这会造成在大量insert 时形成表的写入热点,即短时间内大量的insert大部分都集中写入到某少数几个节点,甚至少数几个Region上。

一般可考虑从DB层或应用层来解决:

由DB层解决

可使用TiDB提供的AUTO_RANDOM方式,将由TiDB 生成随机分布且空间耗尽前不重复的主键,达到离散写入、打散写入热点的目的。该方式适用于代替自增主键,解决自增主键带来的写入热点(此时TiDB 生成的主键不再是自增的主键)。

将建表语句中的 AUTO_INCREMENT改为 AUTO_RANDOM 即可使用该功能,适用于主键只需要保证唯一,不包含业务意义的场景。示例如下:

CREATE TABLE t (a BIGINT PRIMARY KEY AUTO_RANDOM, b varchar(255));INSERT INTO t (b) VALUES ("foo");SELECT * FROM t;

注:也可以通过设置SHARD_ROW_ID_BITS,把 rowid 打散写入多个不同的 Region,缓解写入热点问题。但是设置的过大会造成 RPC 请求数放大,增加 CPU和网络开销,且打散热点的效果不如 AUTO_RANDOM。

由应用层解决

即不依赖于DB,而是由业务应用自己生成无序离散的唯一ID,思路也很简单,先生成分布式唯一ID,再将之倒转逆序即可。

目前业内大部分的分布式唯一ID都是基于雪花算法实现,常见的分布式唯一ID可分为时间戳、机器号、序列号等几部分,一般来说序列号都是排在最后,序列号之前的部分都是单调递增的,而序列号部分则是随机无序的,此时该分布式唯一ID从整体上坎依旧是单调递增的,需要将之反转倒序,即将序列号部分放在前面,剩余部分则放在后面,则此时该分布式唯一ID总体上看就变成了相对离散随机、无序、非单调,能较好地打散热点写入,让各个节点的写入相对均匀。


最后的话

其实不光是TiDB,对于大部分的分布式数据库而言,主键ID都尽量不要设计为单调递增(包括DB的自增主键和单调递增的分布式唯一ID),否则在高并发写入下,很容易造成写入热点,拖慢整个数据库的读写性能,典型的如HBse的rowkey若设计不当,同样也会造成写入热点。

对于单机关系型数据库(如MySQL),从长远看,也不太推荐使用自增主键,自增主键非常不利于数据库双活,数据同步时易造成主键冲突。虽然可以设置不同的步长来规避同步时主键冲突的问题,但一旦数据库实例数有变化,则步长也必须变化,维护复杂度高,扩展性较差。综上,出于性能和扩展性考虑,无论是分布式数据库,还是单机关系型数据库,尽量不要使用单调递增的主键,而是尽量使用相对随机离散无序、非单调递增的唯一ID作为主键。- 往期回顾 -MySQL可重复读的实际意义热点账户系列-概述热点账户系列-账户拆分方案热点账户系列-异步入账方案浅谈对账谈谈通用对账平台的业务架构设计
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐