# 使用场景
在实际应用中,经常碰到导入数据的功能,当导入的数据不存在时则进行添加,有修改时则进行更新
在 MySQL 数据库中,如果在 insert 语句后面带上 ON DUPLICATE KEY UPDATE 子句,而要插入的行与表中现有记录的惟一索引或主键中产生重复值,那么就会发生旧行的更新;如果插入的行数据与现有表中记录的唯一索引或者主键不重复,则执行新纪录插入操作。
说通俗点就是数据库中存在某个记录时,执行这个语句会更新,而不存在这条记录时,就会插入。
# 用法
该语句是基于唯一索引或主键使用,比如一个字段 user_id 被加上了 unique index,并且表中已经存在了一条记录值为 1, 则会更新记录 1 的 balance 字段
ON DUPLICATE KEY UPDATE 后面可以放多个字段,用英文逗号分割
insert into dtm_busi.user_account (user_id, balance)
values (1, 10000),
(2, 10000) on DUPLICATE KEY
UPDATE balance =
values (balance);
# 注意点
因为这是个插入语句,所以不能加 where 条件。如果是插入操作,受到影响行的值为 1;如果更新操作,受到影响行的值为 2;如果更新的数据和已有的数据一样(就相当于没变,所有值保持不变),受到影响的行的值为 0。
on duplicate key update 有一个特性就是,每次是更新的情况下 id 也是会自增加 1 的,比如说现在 id 最大值的 5, 然后进行了一次更新操作,再进行一次插入操作时,id 的值就变成了 7 而不是 6.
为了解决这个问题,有两种方式,第一种是修改
innodb_autoinc_lock_mode
中的模式,第二种是将语句修拆分为更新和操作 2 个动作第一种方式:innodb_autoinc_lock_mode 中有 3 中模式,0,1 和 2,mysql5 的默认配置是 1,
0 是每次分配自增 id 的时候都会锁表. 有死锁可能
1 只有在
bulk insert
的时候才会锁表,简单insert
的时候只会使用一个 light-weight mutex, 比 0 的并发性能高2
simple insert
语句能保证 ID 是连续的,但是bulk insert
的 ID 则可能有空洞。
主从复制的同一张表下的同一行 id 有可能不一样数据库默认是 1 的情况下,就会发生上面的那种现象,每次使用
insert into .. on duplicate key update
的时候都会把简单自增 id 增加,不管是发生了insert
还是update
# 产生死锁的原因
insert ... on duplicate key 在执行时,innodb 引擎会先判断插入的行是否产生重复 key 错误,如果存在,在对该现有的行加上 S(共享锁)锁,如果返回该行数据给 mysql, 然后 mysql 执行完 duplicate 后的 update 操作,然后对该记录加上 X(排他锁),最后进行 update 写入。
如果有两个事务并发的执行同样的语句,那么就会产生 death lock,如: