mysql中使用索引优化批量插入与更新操作

来源:这里教程网 时间:2026-02-28 20:48:16 作者:

为什么批量插入时加索引反而变慢?

因为 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%。

相关推荐