【TiDB 插入性能优化实战:从 5 秒到毫秒级的跨越】

TiDB 插入性能优化实战:从 5 秒到毫秒级的跨越

背景与问题现象

前提条件

  • 数据库:TiDB(分布式关系型数据库)
  • 技术栈:JDK8 + MyBatis-Plus
  • 表结构:11 个字段,数据量约 200 万条

异常现象

线上环境发现某张表的 insert 操作异常缓慢,单次插入耗时高达 5 秒,远超业务预期(正常应在 100ms 以内)。从 TiDB 慢日志来看,所有慢 SQL 均为简单插入语句(如 insert into table (col1, col2...) values (...)),且无复杂逻辑或大字段。

排查过程:一步步定位根因

排查 1:排除连接池资源瓶颈

首先检查数据库连接池配置:最大连接数 max-active=100,通过监控发现同一秒内执行的 SQL 记录不足 80 条,远未达到连接池上限。因此,连接数不足并非问题原因

排查 2:聚焦 TiDB 事务执行链路

通过 TiDB 慢日志的"执行链路详情"发现,耗时几乎全部集中在 Prewrite 阶段(分布式事务两阶段提交的第一阶段)。

补充:Prewrite 阶段是 TiDB 分布式事务的核心环节,需向事务涉及的所有 TiKV Region 的 Leader 发送请求,写入数据、记录锁信息并验证冲突。该阶段耗时直接取决于涉及的 Region 数量和 TiKV 处理能力。

排查 3:发现 ID 生成策略的矛盾

检查表结构定义,发现主键字段明确配置了 AUTO_INCREMENT(自增主键),但查询数据库中已插入的 ID 时,却发现其毫无自增规律(数值为 16 位以上的大整数,且跳跃性极大)。

这是一个明显的异常点:表定义的自增策略与实际 ID 分布不匹配

排查 4:定位代码层的 ID 生成配置

查看项目代码的 MyBatis-Plus 全局配置,发现:

yaml 复制代码
mybatis-plus:
  global-config:
    db-config:
      id-type: ASSIGN_ID  # 全局使用雪花算法生成 ID

同时,实体类 Entity 的主键字段 id 未单独指定 @TableId 注解(即未覆盖全局配置)。

结论 :代码中全局启用了雪花算法生成 ID,覆盖了 TiDB 表的 AUTO_INCREMENT 配置,导致实际插入的 ID 为雪花 ID(跳跃性大),而非预期的连续自增 ID。

解决方案:统一 ID 生成策略

针对排查结果,通过代码调整统一 ID 生成策略:

  1. 实体类指定自增策略:在主键字段添加注解,覆盖全局雪花算法配置:

    java 复制代码
    public class TargetEntity {
        // 指定使用数据库自增策略,覆盖全局的 ASSIGN_ID
        @TableId(type = IdType.AUTO)  
        private Long id;
        // 其他字段...
    }
  2. 验证效果 :重新部署后,新插入的数据 ID 按 1,2,3... 连续递增,符合 TiDB AUTO_INCREMENT 预期。慢日志显示 insert 耗时从 5 秒降至 50ms 以内,Prewrite 阶段耗时显著减少。

深度分析:五个关键问题解答

问 1:为什么雪花算法会导致插入变慢?

核心原因是 ID 分布分散导致 Region 数量激增

  • 雪花算法生成的 ID 由"时间戳+机器 ID+序列号"组成,看似递增但存在大范围跳跃(如多实例部署时,不同机器 ID 生成的 ID 差距极大)。
  • TiDB 按主键范围分裂 Region(默认 64MB/Region),跳跃的 ID 会让 200 万条数据散落在数百个小 Region 中。
  • Prewrite 阶段需要向每个涉及的 Region Leader 发送请求,Region 数量越多,总耗时越长(5 秒本质是数百个 Region 的交互延迟叠加)。

问 2:历史 200 万数据是否需要优化?如何优化?

是否需要优化:取决于历史数据的 Region 分布和操作性能:

  • 若历史数据的 Region 数量超过正常范围(200 万数据应集中在 2-3 个 Region),或涉及历史数据的查询/更新耗时高,则需要优化。

优化方法

  1. 无锁重组织 Region (推荐):

    通过 TiDB 的 REORGANIZE PARTITION 按历史 ID 范围合并分散的 Region:

    sql 复制代码
    -- 按历史雪花 ID 的实际范围合并(替换为实际 ID 区间)
    ALTER TABLE your_table 
    REORGANIZE PARTITION 
    BETWEEN (1658555588888888888) AND (1658555588888988888) 
    INTO (PARTITION p_history VALUES LESS THAN (1658555588888988889));
  2. 重建表 (适合非核心业务):

    低峰期通过临时表按 ID 排序重写数据,强制数据集中分布:

    sql 复制代码
    CREATE TABLE temp_table LIKE your_table;
    INSERT INTO temp_table SELECT * FROM your_table ORDER BY id;  -- 按 ID 排序插入
    RENAME TABLE your_table TO old_table, temp_table TO your_table;

问 3:如何判断 Region 数量和分散程度?

1. 查看 Region 数量
  • TiDB Dashboard :登录 http://{tidb-ip}:2379/dashboard → "数据分布" → "表分布",搜索目标表查看"Region 数量"。

  • SQL 查询

    sql 复制代码
    SELECT COUNT(DISTINCT region_id) AS region_count
    FROM information_schema.tikv_region_status
    WHERE table_name = 'your_table';

    正常范围:数据量(MB)/64(默认 Region 大小),200 万数据(约 100MB)应在 2-3 个。

2. 判断 Region 是否分散
  • TiDB Dashboard:查看表的"Region 列表",观察"范围起始(Start Key)"和"范围结束(End Key)":

    • 连续分布:Region 范围依次衔接(如 [1,10000)[10000,20000))。
    • 分散分布:范围跳跃极大(如 [1658...888, 1658...900)[1658...950, 1658...999))。
  • SQL 验证:查询 Region 范围并检查连续性:

    sql 复制代码
    SELECT region_id, hex(range_start) AS start_key, hex(range_end) AS end_key
    FROM information_schema.tikv_region_status
    WHERE table_name = 'your_table'
    ORDER BY range_start;

问 4:若沿用雪花算法,200 万数据会慢吗?

大概率会慢

雪花算法的 ID 跳跃性会导致 200 万数据分散在数百个 Region 中,引发:

  • 插入时 Prewrite 阶段需与大量 Region 交互,耗时随 Region 数量线性增加;
  • 查询/更新时需扫描多个分散 Region,Coprocessor 处理耗时飙升;
  • 大量小 Region 消耗 TiKV 内存和 PD 调度资源,间接降低集群性能。

仅在"单实例部署+ID 严格连续+无范围查询"的极端场景下,性能问题可能不明显,但分布式系统中几乎不满足。

问 5:雪花算法与主键自增的适用场景对比

维度 雪花算法 主键自增(如 TiDB AUTO_INCREMENT)
全局唯一性 支持(跨库/跨服务) 单库内唯一,TiDB 分布式自增支持全局唯一
ID 特征 整体递增,可能跳跃 严格连续(趋势)
数据分布 可能分散(依赖 ID 生成规则) 集中(按 ID 范围存储)
适用场景 1. 跨库/跨服务全局唯一 ID 需求; 2. 不依赖数据库生成 ID; 3. 需要通过 ID 反推时间戳 1. 单集群内业务,无跨库唯一需求; 2. 频繁范围查询/排序; 3. 依赖 ID 连续性(如订单号); 4. 基于 TiDB 等分布式数据库优化性能

总结

本次案例的核心教训是:分布式数据库的 ID 生成策略需与存储特性匹配。TiDB 等分布式数据库依赖主键范围实现数据集中存储,雪花算法的 ID 跳跃性会破坏这一特性,导致性能问题。

实际开发中,应根据业务场景选择 ID 策略:需全局唯一时用雪花算法(但需优化 ID 分布),单集群内优先用数据库自增(尤其是 TiDB 的分布式自增),才能充分发挥分布式数据库的性能优势。

相关推荐
小马爱打代码2 天前
避坑指南:MySQL 迁移到 TiDB
数据库·mysql·tidb
5***79002 天前
JavaScript生成器函数
hive·tidb·consul
北i13 天前
TiDB 关联子查询去关联优化实战案例与原理深度解析
java·数据库·sql·tidb
Lucifer三思而后行14 天前
使用 BR 备份 TiDB 到 AWS S3 存储
数据库·tidb·aws
Lucifer三思而后行16 天前
使用 BR 备份 TiDB 到阿里云 OSS 存储
阿里云·云计算·tidb
落叶的悲哀17 天前
mysql tidb like查询有换行符内容问题解决
数据库·mysql·tidb
得物技术18 天前
得物TiDB升级实践
数据库·性能优化·tidb
言之。19 天前
【数据库】TiDB 技术选型与架构分析报告
数据库·架构·tidb
熙客20 天前
TiDB:分布式关系型数据库
java·数据库·分布式·tidb