20 | 幻读是什么,幻读有什么问题?

主键 id ,索引 c,插入6 行数据。begin;  select * from t  where d=5 for update;  commit;

命中 d=5 行,主键 id=5 行加写锁,两阶段锁协议,commit 释放

d 上没索引,全表扫描,不满足的会不会加锁?ps:InnoDB默认可重复读

一、幻读是什么?

其他行的不加锁的话

图 1 假设只在 id=5 这一行加行锁  

Q3 读到 id=1 “幻读”:一个事务两次查询不一样

1.  可重复读,普通查询是快照读,不会看到别的事务。幻读“当前读”才出现

2.  session B 修改结果,A 后的 select 语句用“当前读”看到,不能称为幻读。幻读仅专指“新插入的行”。

如果只从第 8 篇文章《事务到底是隔离的还是不隔离的?》可见性规则分析,三条 SQL结果都没有问题。

for update都是当前读。B 和 C执行后就提交,跟可见性规则并不矛盾。但还有问题。

二、幻读有什么问题?

语义上:A 在 T1 声明d=5 行锁住,这个语义被破坏。加强说明:往  B 、C 里分别加 SQL 

图 2 假设只在 id=5 这一行加行锁 -- 语义被破坏  

B 第二条update t set c=5 where id=0

T1 时刻,没给 id=0 加上锁。 B 在 T2 时刻,可执行两条。session C同理

2.1数据一致性问题

不止内部数据状态,还包含数据和日志逻辑上一致。A 在 T1 加更新:update t set d=100 where d=5

图 3 假设只在 id=5 这一行加行锁 -- 数据一致性问题  

update 加锁语义和 select …for update 一致

1.   T1 变成 (5,5,100), T6 提交;

2.  T2 ,id=0 (0,5,5);    //B提交,写两条

3.  T4 ,(1,5,5);    //C提交写两

4.  其他不变。

binlog 里

update t set d=5  where id=0; /*(0,0,5)*/

update t set c=5  where id=0; /*(0,5,5)*/


insert into t  values(1,1,5); /*(1,1,5)*/

update t set c=5  where id=1; /*(1,5,5)*/


update t set  d=100 where d=5;/* 所有 d=5 的行,d 改成100*/

用 binlog 克隆变成 (0,5,100)、(1,5,100) 和 (5,5,100)。数据不一致

数据不一致怎么引入的?

“select * from t where d=5 for update 加锁”导致的。碰到的行,都加写锁:

图 4 假设扫描到的行都被加上了行锁

A 提交后, B 才执行。id=0 最终结果(0,5,5)。binlog 执行序列:

insert into t  values(1,1,5); /*(1,1,5)*/  //T4

update t set c=5  where id=1; /*(1,5,5)*/


update t set d=100 where d=5;/* 所有 d=5 的行,d 改成100*/


update t set d=5  where id=0; /*(0,0,5)*/

update t set c=5  where id=0; /*(0,5,5)*/

id=1 数据库(1,5,5), binlog (1,5,100),幻读没解决。加锁时候,id=1 不存在,加不上锁.

所有都加锁,阻止不了新记录,“幻读”被单拿出解决原因。

三、如何解决幻读?

3.1间隙锁 (Gap Lock): 锁两值空隙

插入6 记录,产生 7 间隙。幻读原因:行锁只锁行,新插入要更新的“间隙”。

图 5 表 t 主键索引上的行锁和间隙锁

执行 select * from t where d=5 for update 加7间隙锁。确保无法再插入新记录。

行锁,分读\写锁,冲突关系:是“另外行锁”。

图 6 两种行锁间的冲突关系  

间隙锁存冲突关系:间隙中插入锁之间不冲突

图 7 间隙锁之间不互锁

B 不会被堵住。t 没有 c=7 ,A 是间隙锁 (5,10)。 B 也是。共同目标,之间不冲突

间隙锁和行锁合称 next-key lock,前开后闭区间。初始化后, select * from t for update锁整表,形成7 next-key lock,分别是 (-∞,0]、(0,5]、(5,10]、(10,15]、(15,20]、(20, 25]、(25,+supremum]。(+∞是开区间。每个索引加不存在最大值 supremum

间隙锁为开区间next-key lock 为前开后闭区间

3.2带来困扰

任意锁一行,不存在插入,存在更新

begin; select * from t  where id=N for update;

/* 如果行不存在*/  insert into t  values(N,N,N);

/* 如果行存在*/  update t set d=N  set id=N;

commit;

insert … on  duplicate key update 就能解决吗?多个唯一键时不行

一旦并发,就死锁。操作前 for update 锁,怎么还有死锁?假设 N=9:

图 8 间隙锁导致的死锁

不需要用到后面的 update 语句,已经形成死锁了。

1.  A select … for update  id=9 不存在,加上间隙锁(5,10)

2.  B select … for update 同加上间隙锁 (5,10),锁之间不冲突

3. B 插入 (9,9,9)被A 间隙锁挡住,等待;

4.  A 插入 (9,9,9),B 间隙锁挡住。

互相等,形成死锁。InnoDB 死锁检测让  A 的 insert 返回。

间隙锁引入,导致同样语句锁更大范围,影响并发度

为解决幻读,简单方法

间隙锁:可重复读隔离级别才会生效。读提交解决不一致,binlog_format=row

读提交隔离级别够用,业务不需可重复读,锁范围更小(没有间隙锁),合理。

都用读提交,逻辑备份时,mysqldump 为什么备份线程设置成可重复读呢?(这个我在前面的文章中已经解释过了,你可以再回顾下第 6 篇文章《全局锁和表锁 :给表加个字段怎么有这么多阻碍?》的内容)

备份时,备份线程用的是可重复读,业务线程是读提交同时存在会不会有问题

不同隔离级别现象有什么不一样?为什么“用读提交就够了”?

小结

所有行都加行锁无法解决幻读,引入间隙锁

间隙锁考虑少。会产生因为间隙锁导致的死锁现象

影响系统并发度,增加锁复杂度

思考题

图 9 事务进入锁等待状态  

B、 C 都等待,原因

预习,session C是有点难度的。下一篇说明。

线上 MySQL 配置的是什么隔离级别,为什么这么配?什么场景,必须可重复读?


varchar 加锁规则:判断间隙和int 一样,排好就有间隙。

A 执行完:begin; select * from t  where d=5 for update; /*Q1*/

B 和C假设不堵住,出现问题:推导A 需锁整个表所有行和间隙

评论1

insert into t values(0,0,0),(5,5,5),

(10,10,10),(15,15,15),(20,20,20),(25,25,25);

运行mysql> begin;

Query OK, 0 rows affected (0.00 sec)

mysql> select * from t where c>=15 and c<=20 order by c desc for update;

c 索引最右包含主键值,(0,0) (5,5) (10,10) (15,15) (20,20) (25,25) 上锁范围要匹配主键值

思考题答案:

上限会扫到c索引(20,20) 上一个键,为了防止c为20 主键值小于25 的行插入,需要锁定(20,20) (25,25) 两者的间隙;开启另一会话(26,25,25)可以插入,而(24,25,25)会被堵塞。

下限会扫描到(15,15)的下一个键也就是(10,10),测试语句会继续扫描一个键就是(5,5) ,此时会锁定,(5,5) 到(15,15)的间隙,由于id是主键不可重复所以下限也是闭区间;

在本例的测试数据中添加(21,25,25)后就可以正常插入(24,25,25)

@ 某、人 、@郭江伟 

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

推荐阅读更多精彩内容

  • 专业考题类型管理运行工作负责人一般作业考题内容选项A选项B选项C选项D选项E选项F正确答案 变电单选GYSZ本规程...
    小白兔去钓鱼阅读 8,984评论 0 13
  • 在C语言中,五种基本数据类型存储空间长度的排列顺序是: A)char B)char=int<=float C)ch...
    夏天再来阅读 3,340评论 0 2
  • 龙走九霄 正当霁诗晴被亘古重击负伤之时,御少流也因为担心一时分了神,金蝉见状指点龙蛇,以气化字,每一字凝结极强的天...
    探旅客Yeats阅读 249评论 0 0
  • 昏花阅读 58评论 0 0
  • 股权变化: ◆莱美药业:股东邱炜拟减持不超1%股份 ◆金麒麟:首次回购755万元股份 ◆易明医药:股东嘉泽创投拟减...
    gudestock阅读 215评论 0 0