3分钟了解InnoDB锁机制与事务隔离

锁机制

共享排他锁

InnoDB实现了两种标准行级锁

  • 共享锁(S)
    持有S锁的事务可以读取行
  • 排他锁(X)
    持有X锁的事务可以修改或者删除行

共享锁与排他锁的兼容性:

S X
S 兼容 互斥
X 互斥 互斥

即:

  1. 多个事务可以同时获取S锁,读读可以并行
  2. 只有一个事务能够获取X锁,写写不能并行
  3. 一条记录要么是S锁要么是X锁,读写不能并行

意向锁

意向锁是表级锁,它表明一个事务需要在这个表的行上添加的锁类型。

意向锁有两种:

  • 意向共享锁(IS)
    表明一个事务有意向表中的行添加S锁,加锁方式SELECT …… LOCK IN SHARE MODE
  • 意向排他锁(IX)
    表明一个事务有意向表中的行添加X锁,加锁方式 SELECT …… FOR UPDATE

意向锁的协议:

  • 一个事务获取S锁之前,必须获取IS锁
  • 一个事务获取X锁之前,必须获取IX锁

意向锁与共享排他锁的兼容性:

S IS X IX
S 兼容 兼容 互斥 互斥
IS 兼容 兼容 互斥 兼容
X 互斥 互斥 互斥 互斥
IX 互斥 兼容 互斥 兼容

记录锁、间隙锁、临键锁

假设有一张表:

id name no
1 dxy1 10
2 dxy2 20
3 dxy3 30

PK:id
Key:name
Key:no

  • 记录锁
    记录锁是对记录行加锁,只对主键或者唯一索引生效,并且必须是精确匹配
    例如:
    事务A先执行,不提交:
    select * from t where id = 1 for update;
    事务B后执行:
    select * from t where id = 1 for update;
    如图,事务B阻塞,因为id=1的记录已经有了记录锁
  • 间隙锁
    间隙锁是一种开区间锁,用来解决插入的问题
    例如:
    事务A先执行,不提交:
    select * from t where id between 1 and 10 for update;
    事务B后执行:
    insert into t values(8,'dxy8',8);

如图:事务B阻塞,因为区间(1,10)已经加锁


  • 临键锁
    临键锁是由记录锁以及索引记录之前的间隙组成
    例如:
    事务A先执行,不提交:
    select * from t where no = 20 for update;
    事务B后执行:
    insert into t values(19,'dxy19',19);
    如图:事务B被阻塞,因为区间(10,20]已经加锁

需要注意的是

  1. 如果no是唯一索引,那么事务A支持有id=20的记录锁
  2. 如果no是普通索引,那么会持有(10,20]的临键锁和(20,30)的间隙锁

插入意向锁

插入意向锁是一种特殊的间隙锁,由插入操作设置
多个事务插入记录,如果在同一区间内插入位置不冲突,那么不会相互阻塞

例如:

id name no
1 dxy1 10
2 dxy2 20
3 dxy3 30

事务A先执行,不提交:
insert into t values(11,'dxy11',11);
事务B后执行:
insert into t values(12,'dxy12',12);
事务B不会被阻塞

但是如果事务B执行:
insert into t values(11,'dxy11',11);
如图:事务B依然会被阻塞,因为事务A占有记录锁

自增锁

自增锁是一种特殊的表级锁,是由向包含自增列的表中插入数据的事务获取的。
注意:自增锁的生命周期就是一条语句,而不是事务
例如:
事务A先执行,不提交:
insert into t(name,no) values('dxy11',11),('dxy12',12);
事务B后执行,不提交:
insert into t(name,no) values('dxy13',13);
事务A后执行,不提交:
insert into t(name,no) values('dxy14',14);

如图:事务A两条语句插入的记录,id是不连续的

可以通过innodb_autoinc_lock_mode设置自增锁模式:

  1. innodb_autoinc_lock_mode=0(传统锁模型)
  2. innodb_autoinc_lock_mode=1(连续锁模型)
  3. innodb_autoinc_lock_mode=2(交叉锁模型)
    更多关于自增锁模式的描述,参考官网文档

事务隔离性

我们知道事务有ACID四大特性,其中I就是代表的隔离性(isotation)
什么是事务的隔离性?
隔离性是指多个事务并发访问同一个表数据时,一个事务不会受到另一个事务对数据操作的影响。

并发事务之间的干扰存在哪些问题?
假设表t有数据:

id name no
1 dxy1 10
  • 场景1

事务A先执行,不提交:
insert into t values(4,'dxy11',11);

事务B后执行,不提交:
select * from t;

id name no
1 dxy1 10
4 dxy11 11

最后事务A回滚

此时,事务B就读取并处理了一条不存在的数据,这种情况就是脏读

  • 场景2

事务A先执行,不提交:
select * from t where id >= 1;

id name no
1 dxy1 10

事务B后执行,并提交:
update t set name = 'wuming' where id = 1;

事务A再次执行:
select * from t where id >= 1;

id name no
1 wuming 10

此时,事务A先后两次查询的记录数不一致,这种情况就是不可重复读

  • 场景3

事务A先执行,不提交:
select * from t where id = 1;

id name no
1 dxy1 10

事务B后执行,并提交:
insert into t values(4,'dxy11',11);

事务A再次执行:
select * from t where id = 1;

id name no
1 dxy1 10
4 dxy11 11

此时,事务A先后两次查询的记录数不一致,这种情况就是幻读

因此,并发事务可能导致

  1. 脏读:一个事务读取了另一个事务未提交的数据
  2. 不可重复读:一个事务读取了另一个事务修改并提交的数据
  3. 幻读:一个事务读取了另一个事务增加或删除并提交的数据

为了平衡并发事务的性能和数据一致性、可靠性、再现性,InnoDB支持了SQL:1992表准中全部四种隔离级别

  1. 读未提交(Read Uncommitted)
    Select语句不加锁,事务可能读取其他事务未提交的数据,即脏读。因此,这种级别的并发性最好,一致性最差

  2. 读已提交(Read Committed)

    • 对于非加锁读,每次都读取最新的快照
    • 对于加锁读、更新、删除,只对记录加锁而不对间隙加锁,即这种级别不支持间隙锁,因此会导致幻读

    因此,RC的隔离级别会出现不可重复读和幻读

  3. 可重复读(Repeatable Read)(默认级别)

    • 对于非加锁读,同一个事务只在第一次查询时生成快照。
    • 对于加锁读、更新、删除存在两种情况
      1. 唯一索引精确匹配:只对数据加记录锁
      2. 范围查询或非唯一索引:使用间隙锁和临键锁

    因此,RR级别下可以完全避免幻影行和不可重复读

  4. 序列化(Serializable)
    最严格的隔离级别,所有select全部转换成 select …… lock in share mode。这就导致,如果其他事务修改记录,那么select会被阻塞。

全表扫描在RC和RR级别下的加锁策略是怎样的呢?
假设有一张表t:

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

推荐阅读更多精彩内容