replace into 用法

  1. replace into t(id, update_time) values(1, now());
  2. replace into t(id, update_time) select 1, now();

insert on duplicate 用法

单独在SQL或Mybatis中指定:

  1. <insert id="insertOnDuplicateUpdate" parameterType="User">
  2. insert into user (user_id, create_time,
  3. update_time, deleted)
  4. values (#{userId,jdbcType=BIGINT}, #{createTime,jdbcType=TIMESTAMP},
  5. #{updateTime,jdbcType=TIMESTAMP}, #{deleted,jdbcType=BIGINT})
  6. ON DUPLICATE KEY UPDATE update_time = values(update_time)
  7. </insert>

insert on duplicate 问题

Insert on Duplicate Key Update 在并发情况下容易出现死锁,某些版本上,例如5.7.
https://bugs.mysql.com/bug.php?id=52020

当表中既有主键又有唯一键时,当执行insert on duplicate key update 最终在DB上执行的是insert 时,在主键索引树添加insert intention lock,同时也在 unique index tree 添加GAP lock。

对比

Replace Into 分两步
1.Try insert on the table (1 rows affected )
2. If 1 fails, delete row and insert new row (2 rows affected)
replace 执行替换时,主表实际是执行了delete+insert两个操作,如果此时关联了外键,会在有约束的情况下删除这些外联数据。(在一些特点情况下可能需要删除外联数据之后又恢复主表数据)
replace执行完替换后auto_increment键会自动增加
Insert on Duplicate Key Update 分两步
1.Try insert on table (1 rows affected )
2.If 1 fails, update row (2 or ? rows affected )
INSERT ON DUPLICATE KEY UPDATE 执行完更新后当前自增id不变,但是 auto_increment值会增加。
INSERT ON DUPLICATE KEY UPDATE 属于 Mixed-mode inserts,只分析SQL无法知道需要几个自增id,在 innodb_autoinc_lock_mode 为 1(默认值),这类 insert 不走 AUTO_INC 表级锁,而是用一个轻量级的 mutex,一次性分配最坏情况下所需要的自增id,至于用不用的完就不管了。上锁的只是分配id的过程,不会锁整个sql语句,这样一来提高了并发度,但代价是和后续insert分配的自增id之间可能存在空洞。具体到INSERT ON DUPLICATE KEY UPDATE,即使最终执行了 update,自增ID也是会增长的,不过这一般不是问题。

结论

业务逻辑尽量不要依赖MySQL机制实现!
replace into / Insert on Duplicate Key Update 这种操作尽量不要使用!