表设计不能消除排队但能大幅降低锁竞争与索引开销;自增主键比UUID写入更快,因避免页分裂和随机I/O;二级索引过多会拖慢写入;低基数字段不宜建单列索引;日增超50万且有热点时应分表;Buffer Pool大小不足会放大I/O等待。

核心结论:表设计本身不能“消除”排队,但能大幅降低锁竞争密度和索引维护开销——关键在主键、索引数量与字段选择。
为什么自增主键比 UUID 更适合高并发写入
InnoDB 表数据按主键顺序物理存储。自增 AUTO_INCREMENT 保证插入位置连续,页分裂极少,Buffer Pool 命中率高;而 UUID 是随机值,每次插入都可能触发 B+ 树页分裂 + 随机磁盘 I/O,写入吞吐直接腰斩。
- 实测对比:10 万行批量插入,
BIGINT AUTO_INCREMENT比CHAR(36)UUID 快 3~5 倍,innodb_row_lock_time_avg低一个数量级 - 别被“分布式 ID”带偏:Snowflake 等时间有序 ID 可接受,但纯随机字符串(如
UUID_SHORT()不加改造)仍属高危 - 如果业务强依赖全局唯一,可在应用层生成有序 ID(如
timestamp + machine_id + seq),再作为主键
哪些索引会悄悄拖慢写入速度
每多一个二级索引,INSERT/UPDATE 就要额外维护一棵 B+ 树。真正卡住写入的,往往不是锁,而是索引页分裂和刷脏压力。
- 用
SHOW INDEX FROM table_name查看Cardinality,对Cardinality极低(如status只有 3 个值)的字段,单列索引基本无效,还加重写入负担 - 把
(status, created_at)这类高频组合查询字段建为联合索引,而不是分别建idx_status和idx_created_at - 删除写多读少字段的索引(如
extra_info JSON上的idx_extra),宁可用应用层过滤,也别让它参与每次 INSERT 的索引更新 - 警惕隐式依赖:某些
ORDER BY或DISTINCT语句会偷偷走索引加速,删前务必用EXPLAIN FORMAT=TREE验证执行计划是否退化
什么时候该考虑分表而非优化单表
当单表日增写入超 50 万行,且存在明显热点(如订单表里 80% 写入集中在最近 2 小时的数据),再怎么调索引和主键也难缓解,此时分表是更现实的选择。
- 按时间分表(如
order_202610,order_202611)最易落地,写入天然分散,归档也方便 - 按业务维度分表(如
order_user_001~order_user_999)可打散热点,但要求路由逻辑稳定、查询尽量带分片键 - 避免“逻辑分表+中间件”一步到位:先手动分物理表 + 应用层路由,验证效果后再引入 ShardingSphere 等组件
- 分表后仍需注意:每个子表仍要遵循自增主键 + 精简索引原则,否则只是把排队从一张表搬到多张表
最容易被忽略的一点:innodb_buffer_pool_size 设得太小,会导致索引页频繁换入换出,放大所有写入操作的 I/O 等待——哪怕表结构再合理,内存撑不住,照样排队。

















