SQL 大表加索引的 concurrent build 与锁等待最小化路径

2次阅读

大表加索引应优先使用mysql的algorithm=inplace+lock=none或postgresql的create index concurrently实现近无锁操作,辅以低峰期执行、实时监控锁等待及从库预验证等通用策略确保安全。

SQL 大表加索引的 concurrent build 与锁等待最小化路径

大表加索引时,避免长时间锁表、减少业务阻塞,核心在于绕过 DML 锁竞争和事务阻塞。MySQL 5.6+ 支持 ALGORITHM=INPLACELOCK=NONE,PostgreSQL 支持 CONCURRENTLY,这是最小化锁等待的起点。

MySQL:用 INPLACE + LOCK=NONE 实现近乎无锁建索引

只要满足条件(如非主键、非全文索引、不涉及列类型变更),MySQL 可在不重建表、不阻塞写入的前提下在线加索引:

  • 语法示例:ALTER table orders ADD INDEX idx_user_id (user_id) ALGORITHM=INPLACE, LOCK=NONE;
  • 必须确认执行前 select @@innodb_online_alter_log_max_size; 足够(默认128MB),否则 DDL 可能退化为 copy 算法
  • 通过 SHOW PROCESSLISTperformance_schema.threads 观察状态,若出现 copy to tmp table,说明触发了锁表降级
  • 建议搭配 pt-online-schema-change 做兜底:它用影子表+触发器+重放机制,彻底规避长事务锁,适合超大表或不满足 INPLACE 条件的场景

PostgreSQL:用 CREATE INDEX CONCURRENTLY 避免排他锁

该命令不会获取 access EXCLUSIVE 锁,允许 DML 并发执行,但有关键约束:

  • 不能在事务块中执行(会报错),必须单独运行
  • 建索引期间若发生唯一约束冲突或死锁,索引会进入 INVALID 状态,需手动 DROP INDEX 后重试
  • 首次构建完成后,建议立即 VACUUM ANALYZE 表名 更新统计信息,避免查询计划劣化
  • 如需加速,可提前调大 maintenance_work_mem(例如设为 2GB),显著缩短构建时间

通用策略:分阶段 + 低峰期 + 监控闭环

技术参数只是基础,真正落地要靠节奏控制和可观测性:

  • 优先选业务低峰期操作(如凌晨 2–4 点),并预留 2–3 倍预估耗时作为窗口
  • 建索引前查清表的活跃事务:SELECT * FROM pg_stat_activity WHERE state = 'active' AND backend_type = 'client backend';(PG)或 SELECT * FROM information_schema.INNODB_TRX;(MySQL)
  • 实时监控锁等待:sys.innodb_lock_waits(MySQL)、pg_locks + pg_stat_activity(PG),发现阻塞及时 kill 长事务
  • 对 TB 级表,考虑先在从库上建索引,验证性能影响后再切主库,降低风险面

不复杂但容易忽略的是:没有银弹。CONCURRENTLY 和 INPLACE 都依赖存储引擎能力和当前负载状态,务必在测试环境完整复现生产数据量与并发模型再上线。

text=ZqhQzanResources