为什么批量插入时加索引反而变慢?
因为 MySQL 在执行
INSERT时,每插入一行都会触发索引 B+ 树的维护(分裂、合并、回写),索引越多,写放大越严重。尤其当表已有大量数据,且目标字段存在二级索引时,
INSERT ... VALUES (...), (...), ...的吞吐会明显下降。 单条插入带 3 个二级索引:约 4 次磁盘随机写(1 行记录 + 3 索引项) 批量插入 1000 行:若逐行提交,就是 4000 次随机写;若用事务包住,能减少日志刷盘次数,但索引结构更新仍无法避免 真正有效的优化是:**先删索引 → 批量插入 → 再建索引**,前提是业务允许短时间不可用或离线操作
INSERT ON DUPLICATE KEY UPDATE 怎么避免唯一键冲突导致的锁升级?
当使用
INSERT ... ON DUPLICATE KEY UPDATE更新主键或唯一索引字段时,MySQL 会为每一行匹配的唯一键值加
next-key lock,高并发下极易出现锁等待甚至死锁。这不是语法问题,而是 InnoDB 的悲观锁定机制决定的。 确认是否真的需要“存在则更新”:如果只是补漏,可改用
INSERT IGNORE+ 单独的
UPDATE语句分两阶段处理 确保
ON DUPLICATE KEY UPDATE中的
UPDATE子句只修改非索引列,否则可能触发额外的唯一约束检查 批量时拆成更小批次(如 500 行/批),降低单次锁持有范围和时间 在
WHERE条件中显式指定主键或唯一索引列,避免全表扫描引发的间隙锁扩散
REPLACE INTO 和 INSERT ... ON DUPLICATE KEY UPDATE 的性能差异在哪?
REPLACE INTO本质是
DELETE + INSERT,会触发两次索引变更(先删旧索引项,再插新索引项),还可能导致自增 ID 跳变、外键级联动作重复执行。而
INSERT ... ON DUPLICATE KEY UPDATE是原地更新,仅修改聚簇索引记录和受影响的二级索引字段。
INSERT INTO t (id, name, version) VALUES (1, 'a', 1) ON DUPLICATE KEY UPDATE name = VALUES(name), version = version + 1;如果
version不在任何索引中:只更新聚簇索引页,二级索引完全不动 如果
name是二级索引前缀:需更新该二级索引对应项(但不删除重建)
REPLACE INTO即使只改一个字段,也会让所有二级索引项先被标记删除、再重建,开销翻倍
大批量更新时如何防止索引拖慢 UPDATE 速度?
执行
UPDATE时,只要 SET 子句中修改了任何被索引的列(包括主键、唯一索引、普通索引),MySQL 就必须同步更新对应索引结构。对百万级以上表,这常常成为瓶颈。 优先考虑是否可以绕过索引列更新:比如把状态码存进
tinyint字段并建索引,而不是用
varchar(20)存 “processing”、“done” 等字符串再索引 临时禁用非必要索引(仅限支持
ALTER TABLE ... DISABLE KEYS的 MyISAM;InnoDB 不支持,需改用
DROP INDEX+
ADD INDEX) 用
UPDATE ... JOIN替代子查询,避免因隐式临时表导致索引失效 确认 WHERE 条件是否命中索引:没走索引的
UPDATE会全表扫描+逐行加锁,比索引更新本身更耗时 索引不是越多越好,也不是任何时候都该开着——批量写入场景下,索引是成本中心,不是加速器。最常被忽略的一点是:很多人在 ETL 脚本里全程保持索引开启,却没意识到,关掉两个非关键二级索引,插入耗时可能直接降 60%。
