TiDB 热点问题详解

对于分布式数据库来说,热点和事务冲突是两个需要避免的场景,在很多客户测试的案例中,经常出现热点引起的性能未达预期的情况。本文借近期遇到的几个客户场景,对热点问题在 TiDB 中的表现形式和影响,以及如何应对做一个记录。

何为热点

热点可以理解为热点数据,或者说热点 region,TiDB 自带的 grafana 监控指标中也有 hot region write / read 的 metrics。但是我的理解热点问题更准确的表现形式,其实是 某(几)个tikv 节点的 corprocessor / scheduler (负责读/写模块的线程)消耗资源过高,而剩下的 kv 节点资源白白闲置。对于分布式系统来说,优点突出但是可能存在木桶效应,某一个组件/服务器资源瓶颈,会影响到整个集群的表现。

判断热点现象也很简单,查看 tikv 监控页面的 thread CPU 监控项,如果发现 corprocessor 或 scheduler 中各 kv 实例的 CPU 消耗十分不均匀,那大概率就是碰到热点现象了。

产生热点的原因

产生热点现象的原因有多种,大致总结可分为以下:

1、部分小表例如配置表的频繁访问引起热点

2、MySQL 中自增主键的高并发写入

3、非自增,但时间相关的顺序插入

4、无主键,或主键为非 int 类型

5、时间相关字段的索引

6、业务逻辑/数据分布产生的热点读写

7、执行计划不合理引起的非必要全表/错误的索引扫描

如何规避

热点的解决思路有两种,一是加快单次处理的速度,二是将频繁请求的数据分散到不同的 region,然后通过 pd 调度或手工的方式,将 region 的 leader 调度到多个kv 实例中。

针对上面的情况,逐一分析。

第一种小表频繁访问的场景,因为数据量少,而 TiKV 默认的 region 大小为64M,基本上这些数据都会分在一个 region,如果业务高并发访问,势必会引起热点。这种主要是通过业务手段来规避,比较常见的做法是将配置表数据放到缓存中。从数据库角度优化,可以通过 pd-ctl 找到热点 region,确认对应的配置表后,可以手动将热点 region split 为多个,后续 pd 就可以通过调度算法将这几个不同的 region leader 调度到不同的 kv 节点,缓解热点情况。

第二~四种场景也是比较常见的,一般 MySQL 中都会建议采用 auto_increment 字段作为主键,或者有些业务例如订单系统虽然没有用自增主键,但是基于时间戳来生成一个业务 ID 作为主键,这种对于TiDB 来说跟自增的场景也比较类似。另外还有些时候,客户会选择非 int 类型的字段作为主键,例如手机号码存为 varchar 等。对于这种非 int 类型的主键,TiDB 内部会做一个转换,添加一个自增的 bigint 类型作为主键。所以这几个场景如果出现高并发的大量写入,目前2.0/2.1版本中,基本上单表 TPS 超过1W 就有可能会产生明显的热点效应了。如果想解决可以对表结构做一些改造,将原主键设为 Unique Key,这样 TiDB 内部会添加一个自增的伪列 _tidb_rowid。我们可以通过 alter table t shard_row_id_bits = 4; 的语句来将数据打散。此语法只对没有显示指定 PK 或 PK 为非整数类型时才有效,打散后插入效率可以大大提升,但是会带来一定的副作用就是进行范围 scan 的时候,打散前可能只需要扫一个 region,打散后可能需要扫多个 region。

第五种时间字段的索引,在目前2.0版本中,并没有太好的解决办法。未来版本中即将开放的 partition table 这个新的特性,对于这种场景会有一定的缓解。但是在范围分区表中,就不能以时间作为分区键了,可能需要找另外一个字段作为分区键,这样才能够将基于时间的顺序写入切分为多张表来操作以缓解热点情况。

但是这可能会有两个问题,一是这样就不能利用到时间范围分区的最大便利之一的快速归档功能,二是如果基于时间的范围查找,需要将所有分表都通过索引 scan 一遍再 union 之后返回结果。

其实可以考虑类似 Oracle 的组合分区功能,先按照时间范围 partition,在每个 partition 里再 hash partition 一下,这样基于时间的范围查找仍然能够定位到大的分区,大分区下面的所有 hash 子分区必须是要全部 scan 了。

第六种需要结合具体的业务场景来分析,例如某些交易系统中对公账户可能会成为热点账户,这时在业务侧进行拆分,将一个对公账户拆分为10个账户。业务访问热点账户时,可以随机选其中一个账户进行操作,这样可以有效避免热点情况的产生,但是统计的时候需要将所有账户进行归并。另外在对热点数据进行操作的时候,可以考虑在业务层进行排序/合并,降低对热点数据的访问频率。

对于第七种场景,就是上面所提到的要通过提升单次请求的效率来缓解热点问题,主要还是通过优化慢 SQL 的手段。

热点 Case 记录

case1:某平台业务压测写入瓶颈

该用户在 TiDB 上进行业务 PoC 测试,这是目前线上写入量最大的业务单元,峰值 TPS 接近4W,由于还未采用分库分表,单个 MySQL 实例几乎无法满足这么大的写入量,所以目前是通过 Hbase 的方式来做。

在测试 TiDB 的过程中,发现在3个 tikv 实例的集群中,写入量能达到4W,由于咱们是支持写扩展的,加到6个 tikv 实例,但是发现写入量还是只有4W。在另外一套12个 kv 节点的集群上进行测试,写入也只有4W,无法提升。虽然满足目前线上峰值的需求,但是写入的水平扩展能力并没有得到验证。

通过观察 grafana 监控,发现负责写入的 scheduler 线程有明显的热点情况。确认客户的表结构,是一个业务无关的自增 ID 作为主键, 通过上面shard 的方式打散表后测试写入,在6个 tikv 实例的时候写入能够达到7W,12个 tikv 实例的时候写入能够稳定在11W+,不仅写入能力大大超出客户预期,水平扩展能力也得到验证。

case2:某平台业务压测写入瓶颈

用户在 TiDB 上测试单张业务表(超过20亿数据)的写入性能,由于表结构较为复杂,平均长度达到了2kb,在使用自增 ID 的情况下,写入瓶颈在2.5W QPS。

使用上述方式修改表结构为 shard 模式后,发现仍然存在热点现象,后确认表结构存在两个时间字段的索引,上面提到索引也可能会造成热点。先去掉时间索引进行测试,发现 TPS 还是上不去,热点现象仍然存在。

跟用户确认具体的压测逻辑,业务代码自己维护了一张类似计数器的配置表,多线程并发写入数据的时候,每个线程每次取步长1000,而压测共采用了40个客户端模拟,并发量达到了2W,这样热点情况没有出现在业务表,反而出现在了负责计数器功能的配置表上。建议客户调整步长后,经过多次测试,在步长为500W 时,能够有效避免热点效应,QPS 能够达到10W。

这个 case 后面还发生了一些有意思的现象。

调整表结构为 shard,步长为500W后,虽然写入 QPS 能够达到10W,但是在稳定性测试时,发生过几分钟之后 QPS 会降到3W,热点现象又出现了。

通过分析,每次热点的现象都出现在固定的几个 kv 节点。在高强度的写入过程中region 需要不断的分裂并将 leader 调度到其他节点以平衡各 kv 节点的资源消耗。但是通过分析日志发现,每次调度 leader 的时候都会失败,失败的原因是其它副本的数据写入进度远低于该副本,于是无法调度成功。最后我们发现这几个 kv 节点的 SSD 磁盘写入性能要明显优于其它节点。通过调整集群拓扑,将写入性能差距太大的几台服务器调整为 tidb-server,热点情况消失。研发也提供了两个途径来应对这种场景,一是加快分裂的效率,二是优化切换 leader 时校验数据 gap 的策略。

测完写后,继续压测读请求,很不幸又遇到热点了。压测逻辑是在 ID 最大值和最小值之间随机取数发起读请求,按照常理推断,这种压测方式是不应该产生热点的。通过分析推断,造测试数据的时候可能出现了数据倾斜,通过 select count where id > and id < 的方式很容易得到验证。通过过滤筛选,发现在10亿至20亿之间的数据较多,修改测试代码,随机 ID 选在这个范围之内进行测试,发现热点情况消失,QPS 达到35W。

case3:某券商读业务响应慢

查看 grafana 监控,一共3个 tikv 实例,其中一个 kv 的 corprocessor 消耗 CPU 1000%+,另外两台 CPU 确一直闲置,又是一个明显的热点情况。

通过分析业务 SQL,业务表有一个基于 A+B+C 的联合主键,同时也有一个 A 的单列索引,SQL 中 where 条件是基于 A = ? and B = ? and C = ?,2.0.4版本的优化器可能存在一些缺陷,错误的选择了基于 A 的单列索引。

通过分析表发现,A 的数据分布倾斜十分严重,某些条件下需要扫描5W+ 数据,更合理的执行计划应该是选择A、B、C 的联合主键。通过 hint 方式临时规避这个问题,同时在2.0.6版本已经解决这个优化器的 BUG,建议客户升级到新版本后问题解决。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容

  • 摘要: 本文为今年年初 PingCAP 商业产品团队负责人刘寅在 TiDB DevCon2018 上分享的 《 T...
    nightwish夜愿阅读 6,767评论 0 11
  • “在这么多衣服里边儿,选衣搭配,也算是项工程了。”杨二小姐一边不停地在衣柜翻选衣服,一边念叨。 杨二小姐,不太漂亮...
    羽宙儿阅读 152评论 0 0
  • 国庆档《湄公河行动》口碑票房双丰收的时候,我就猜到了彭于晏(Eddie)必将成为又一枚深受人爱的国民老公。 继胡歌...
    皮皮呀阅读 2,055评论 19 40
  • 俗话说家家都有一本难念的经,是的,我不知道有钱人烦恼是什么。我慢慢的观察到,穷人的烦恼一定跟钱脱不了关系。可能...
    云南啊帅阅读 210评论 0 1
  • 1 2003年,那年我大四,是一位毕业在即的大学生。和所有要走出校门的学生一样,那时候的我除了担忧未来的前程,SA...
    凡子妤阅读 437评论 2 1