ON DUPLICATE KEY UPDATE 是 MySQL 中一种处理 “存在则更新、不存在则插入” 的语法,它的性能相对单纯的 INSERT 或 UPDATE 会有所差异,因为它包含了更多的逻辑控制。下面我们详细分析性能差距及影响因素。


性能对比:ON DUPLICATE KEY UPDATE vs INSERT vs UPDATE

1. INSERT 的性能

  • 单纯的 INSERT 是最轻量的操作,只需向表中插入一条新记录。
  • MySQL 的开销主要集中在写入操作和索引更新:
    • 如果表有主键或唯一索引,MySQL会检查是否存在冲突。
    • 如果没有冲突,数据会直接插入,且非唯一索引会更新。
    • 如果发生冲突(如主键或唯一索引冲突),INSERT 直接失败,返回错误,结束操作。

性能特点:

  • 插入速度通常与数据表的索引数量和行锁机制相关。
  • 没有触发冲突检查时,性能最优。

2. UPDATE 的性能

  • UPDATE 是用于修改现有记录的,在执行时需要先 找到匹配的记录,然后更新指定的字段值。
  • 一般来说,UPDATE 的性能受以下因素影响:
    • 是否有索引支持。如果没有索引,MySQL需要执行全表扫描以找到匹配的记录。
    • 更新的是索引字段还是普通字段。如果更新了索引字段,需要额外操作更新索引结构。
    • 更新操作是否触发行锁(如是事务操作中的主键或唯一索引)。

性能特点:

  • UPDATE 的性能通常优于 ON DUPLICATE KEY UPDATE,因为它不需要检查是否发生插入行为,只需要处理已有记录。

3. ON DUPLICATE KEY UPDATE 的性能

  • ON DUPLICATE KEY UPDATE 结合了插入和更新两种操作逻辑:
    • 插入流程:首先尝试插入新的记录。
    • 冲突检查:如果发生主键或唯一索引冲突,会执行更新操作。
  • 因为它需要进行冲突检查,它的运行效率在总体上比单纯的 INSERT 或 UPDATE 更复杂。

性能开销来源:

  1. 冲突检查
    • ON DUPLICATE KEY UPDATE 依赖主键或唯一索引来判断记录的唯一性。
    • 如果索引字段数量多或索引复杂度较高,冲突判定会增加处理开销。
  2. 行锁与事务冲突
    • 如果并发事务较多(大量同时写),频繁的冲突检查和行锁竞争可能导致性能下降。
  3. 双路径处理
    • 插入路径:如果记录不存在,走插入逻辑。
    • 更新路径:如果记录存在并引发冲突,走更新逻辑,增加了判断和操作成本。

性能差距估算

1. 理论分析

  • 单纯的 INSERT 是性能最快的,因为它没有冲突检查(如果没有索引冲突),且只需执行一次写操作。
  • 单纯的 UPDATE 会更慢一些,因为它需要先定位记录再更新,但只涉及一次写操作。
  • ON DUPLICATE KEY UPDATE 性能通常最慢,因为:
    1. 它需要进行一次插入尝试。
    2. 如果插入失败,还需要进入更新逻辑,相当于执行了两次操作。
    3. 它的性能开销受冲突率影响。如果大多数情况下记录存在并需要触发更新,性能明显偏低。

2. 实际测试
实际场景中,性能差距很大程度上取决于以下因素:

  • 数据量:表的数据越多,冲突检查和更新性能越低。
  • 索引设计:主键和唯一索引的复杂度影响冲突检查效率。
  • 并发写入:大量并发写可能导致行锁竞争加剧。

根据经验,当数据量较大且冲突率较高时,ON DUPLICATE KEY UPDATE 的性能可能会比单独的 INSERT 或 UPDATE 慢数倍。例如:

  • 如果冲突率接近 0(大多数情况下是插入逻辑),ON DUPLICATE KEY UPDATE 的性能接近单纯的 INSERT
  • 如果冲突率接近 100%(大多数情况下是更新操作),ON DUPLICATE KEY UPDATE 可能比直接 UPDATE 慢 50% 到 100%。

高效场景选择

如何选择最优策略取决于以下场景:

  1. 冲突率较低(大多数插入操作)
    • 使用 ON DUPLICATE KEY UPDATE 是合理的选择,因为它可以处理少量冲突,同时代码逻辑简单。
    • 如果只有极少数记录会触发冲突,性能差距可以忽略不计。
  2. 冲突率较高(大多数更新操作)
    • 使用单独的 SELECT + UPDATE 操作可能更高效:
      1. 通过 SELECT 检查是否存在记录。
      2. 如果记录存在,则执行 UPDATE
      3. 如果记录不存在,则执行 INSERT
    示例逻辑:SQL1SELECT COUNT(*) FROM user_assets WHERE user_id = ? AND asset_type = ?; 2IF EXISTS THEN 3 UPDATE user_assets SET amount = ? WHERE user_id = ? AND asset_type = ?; 4ELSE 5 INSERT INTO user_assets(user_id, asset_type, amount) VALUES (?, ?, ?); 6END IF;
  3. 高并发场景
    • 如果是高并发写入场景,ON DUPLICATE KEY UPDATE 的行锁竞争问题可能导致性能瓶颈。
    • 考虑通过分库分表、逻辑分区等方式来减少冲突。

优化建议

  1. 索引设计
    • 确保主键或唯一索引设计合理,索引字段的选择要能够准确标识记录,避免不必要的冲突。
    • 避免字段过多的联合索引增加冲突检查复杂度。
  2. 批量写入
    • 如果数据量较大,可以使用批量插入或批量更新方式来减少执行次数,提升性能。例如:SQL1INSERT INTO table_name (...) VALUES (...) 2ON DUPLICATE KEY UPDATE ...;
  3. 分布式锁
    • 在高并发场景下,使用分布式锁机制(如 Redis 锁)来减少行锁竞争。
  4. 合理选择操作方式
    • 针对冲突较高的场景,使用 SELECT 检查存在性后分别处理 INSERT 或 UPDATE
    • 针对混合场景,使用 ON DUPLICATE KEY UPDATE 可用作通用处理策略。

总结

  • ON DUPLICATE KEY UPDATE 的性能通常介于单纯 INSERT 和 UPDATE 之间,且性能开销取决于冲突率和表索引设计。
  • 在数据量大、冲突率高的场景,ON DUPLICATE KEY UPDATE 的性能可能显著低于分别使用 SELECT + INSERT/UPDATE 的方式。
  • 选择高效的解决方案需要结合具体场景,比如数据规模、冲突频率、并发写入量。


ON DUPLICATE KEY UPDATE 对比单纯的insert或update性能差距多大插图

关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台

除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接

本文链接:https://www.choupangxia.com/2025/07/12/on-duplicate-key-update-insert-update/