【精】MySql语法(6)— ON DUPLICATE KEY UPDATE不存在插入,存在即更新(死锁|性能杀手,慎用)

1. ON DUPLICATE KEY UPDATE语法

duplicate:美 [ˈduːplɪkeɪt , ˈduːplɪkət] 完全一样的。

mysql表结构:

CREATE TABLE `t_school` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(30) NOT NULL,
  `address` varchar(50) DEFAULT NULL,
  `stu_num` int(11) DEFAULT NULL COMMENT '学生数量',
  PRIMARY KEY (`id`),
  UNIQUE KEY `name_index` (`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

其中:id是逻辑主键、name是唯一索引。

业务场景中:若某条记录存在,那么更新,否则插入。

mysql语法:

INSERT INTO t_school(`name`,address,stu_num) VALUES('海淀中学','北京市',2000) ON DUPLICATE KEY UPDATE address = '北京市海淀区';

ON DUPLICATE KEY UPDATE语法的目的是为了解决重复性,当数据库存在某个记录时,执行这条语句会更新它,而不存在这条记录时,会插入它。

如何判断记录是否存在

如果插入的记录存在主键或唯一索引(例如:上例中name便是唯一索引),且表中存在该记录,那么就会认为该条记录存在,则便是更新语句。

2. ON DUPLICATE KEY UPDATE语法的坑

2.1 导致主键不连续自增

ON DUPLICATE KEY UPDATE可能会导致主键不连续自增。

MySQL性能(4)—AUTO_INCRMENT锁机制(innodb_autoinc_lock_mode参数配置)

在Mysql5.1.2-Mysql8.0中innodb_autoinc_lock_mode的默认配置。即对于Mixed inserts(混合插入):直接分析语句,获取最坏情况下需要插入的数量,然后一次性分配足够的auto increment id,只会将整个分配过程锁住。

INSERT … ON DUPLICATE KEY UPDATE属于Mixed inserts。该语句插入时并不会锁表操作,而是会分配足够的自增主键。但若是最终触发了UPDATE操作,那么便会导致主键的跳跃。

2.2 binlog主从复制不一致

影响的mysql的版本:5.0.91,5.1.50,5.6.0

相关bug地址:https://bugs.mysql.com/bug.php?id=50413

原因:当存在多个唯一键时,binlog选择statement模式,会导致主从不一致。

复现:

create table t1(
  f1 int auto_increment primary key,
  f2 int unique key,
  f3 int
);

表中有一条记录:(1, 10, 100)

con1 con2
begin; begin;
insert into t1 values(2, 10, 200) on duplicate key update f3 = 120;
insert into t1 values(2, 20, 300) on duplicate key update f3 = 500;
commit;
commit;
  1. con1在con2开始之前执行了整个插入,则con1会在f2=10上看到冲突,因此将唯一行更新为(1,10,120);
  2. con2看不到冲突,于是插入(2,20,300);
  3. 最终主库的状态为:
f1 f2 f3
1 10 120
2 20 300

因为binlog若是statement模式的场景下:语句执行的顺序是commit的先后顺序,而con2是con1之前进行commit的,于是从库顺序为:

  1. con2插入不会看到冲突,然后插入(2,20,300);
  2. con1会看见冲突,但这次是在f1=2上(与master相反,主库是在f2=10上看到的),因此它将冲突行改为(2,20,120);
  3. 最终从库的状态为:
f1 f2 f3
1 10 100
2 20 120

出现bug了...

mysql做出的改变是加更多的锁(gap lock),来避免这种场景。但是加锁又会引发死锁,造成性能损耗。详见下文。

2.3 存在间隙锁,影响性能

加了间隙锁,导致死锁,影响性能。

影响的mysql版本:5,7,26 8.0.15

问题来源:https://bugs.mysql.com/bug.php?id=98324

在2.2中我们说到了ON DUPLICATE KEY UPDATE加了更多的锁,来解决多个唯一索引场景下主从不一致的情况。

那么为什么mysql会选择间隙锁作为处理方案?

image.png

我们对这里处理的困难在于:如果有多个约束,那么违反哪个特定的约束(f1 或 f2)非常重要,因为这会影响ON DUPLICATE KEY UPDATE字句将更新哪一行(f1有冲突还是f2有冲突?)

为了提供可序列化性(binlog主从一致性),我们需要“锁定我们看到的一切以做出决定”【要加间隙锁】

ON DUPLICATE KEY UPDATE的流程:
在主库中,con1通过首先观察到没有f1=2的行,做出冲突发生在f2(而不是f1)上的决定。
它是如何观察到的?通过主索引临时创建一条f1=2的行,做出了冲突发生在f2(而不是f1)上的决定。
直到后来,当它发现f2上的冲突并决定删除该记录时。
通过删除记录,con1删除了它在f1上没有看到冲突的“证明”的一部分。
这就是con2能够插入f1=2行的原因,这在以后会导致复制问题。

“保留证据”的正确做法是确保f1=2的间隙保持锁定,直到con1提交。

这样可以通过在临时行上创建显式锁来实现,然后让它在删除时被继承。
这种显式锁可以通过所谓的隐式到显式转换来创建。
con1 已经对记录进行了隐式锁定,因为它的 TRX_ID 作为写入它的 trx 的 id 在行的标题中。
但是这个隐式锁会在我们物理删除记录后立即消失,因此需要将其作为显式锁保存在内存中。

3. 优化方案:

分析系统插入操作多还是更新操作多。若是更新操作多,可以先进行update操作,若更新失败(影响的行数为0),那么在进行插入操作。

推荐阅读

关于MYSQL 的 AUTO-INC Locks

mysql官网升级信息文档

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342