mysql 事务隔离级别和相关的锁及锁的类型

本文详细介绍了MySQL的四种事务隔离级别:READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ和SERIALIZABLE,以及它们针对并发问题的解决方案。同时,文章探讨了锁的概念,包括基本锁(共享锁和排它锁)、意向锁、行锁(记录锁、Gap锁和Next-Key锁)以及自增锁(AUTO-INC Locks)。重点解析了各种锁在并发控制中的作用和应用场景。

隔离级别

mysql 的事务隔离级别有4种,由高到低分别是:READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE。其中REPEATABLE READ是系统mysql的默认的。

下面具体说明下:

Read uncommitted 读未提交

READ UNCOMMITTED是限制性最弱的隔离级别。由于该级别忽略其它事务放置的锁。使用READ UNCOMMITTED级别运行的事务,能够读取还未由其它事务提交的改动后的数据值,这些行为称为“脏”读。我们所说的脏读,例如:两个并发的事务,“事务A:小明网银行卡上存钱”、“事务B:小红查询小明工资账户”,事务B读取了事务A尚未提交的数据。比方,事务1改动一行,事务2在事务1提交之前读取了这一行。

假设事务1回滚,事务2就读取了一行没有提交的数据。这种数据我们认为是不存在的。

Read committed 读提交

该级别通过指定语句不能读取其它事务已改动可是尚未提交的数据值。禁止运行脏读。在当前事务中的各个语句运行之间,其它事务仍能够改动、插入或删除数据。从而产生无法反复的读操作。或“影子”数据。比方,事务1读取了一行。事务2改动或者删除这一行而且提交。假设事务1想再一次读取这一行,它将获得改动后的数据或者发现这一样已经被删除。因此事务的第二次读取结果与第一次读取结果不同,因此也叫不可反复读。

大多数数据库的默认级别就是Read committed。比方Sql Server , Oracle。

Repeatable read 反复读

REPEATABLE READ是比READ COMMITTED限制性更强的隔离级别。

该级别包含READ COMMITTED,而且另外指定了在当前事务提交之前。其它不论什么事务均不能够改动或删除当前事务已读取的数据。并发性低于 READ COMMITTED。由于已读数据的共享锁在整个事务期间持有,而不是在每一个语句结束时释放。这个隔离级别仅仅是说,不可以改动和删除,可是并没有强制不能插入新的满足条件查询的数据行。此可以得出结论:REPEATABLE READ隔离级别保证了在同样的查询条件下,同一个事务中的两个查询。第二次读取的内容肯定包换第一次读到的内容。注:Mysql的默认隔离级别就是Repeatable read。

Serializable 序列化

SERIALIZABLE 是限制性最强的隔离级别,由于该级别锁定整个范围的键。并一直持有锁,直到事务完毕。该级别包含REPEATABLE READ。并添加了在事务完毕之前,其它事务不能向事务已读取的范围插入新行的限制。比方,事务1读取了一系列满足搜索条件的行。事务2在运行SQL statement产生一行或者多行满足事务1搜索条件的行时会冲突。则事务2回滚。这时事务1再次读取了一系列满足同样搜索条件的行。第二次读取的结果和第一次读取的结果同样。

事务的并发问题

(1)脏读:事务A读取了事务B更新的数据,然后事务B执行了回滚操作,那么事务A读取到的数据就是脏数据,称之为脏读。

(2)不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,读取到的结果不一致,此种情况称之为不可重复读。

(3)幻读:例如系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。

小结:不可重复读的和幻读不容易区分,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行:行锁,解决幻读需要锁表:表锁。

事务隔离级别脏读不可重复读幻读
读未提交(read-uncommitted)
不可重复读(read-committed)
可重复读(repeatable-read)
串行化(serializable)

锁及类型

一、基本锁

基本锁包括有:共享锁和排它锁

共享锁(Shared Locks:S锁)与排他锁(Exclusive Locks:X锁)

mysql允许持有S锁的事务读一行,允许持有X锁的事务更新或删除一行。
加了S锁的记录,允许其他事务再加S锁,不允许其他事务再加X锁;
加了X锁的记录,不允许其他事务再加S锁或者X锁。

mysql 加这两种锁的语法如下:
加S锁:select…lock in share mode
加X锁:select…for update

二、意向锁

InnoDB为了支持多粒度(表锁与行锁)的锁并存,引入意向锁。
意向锁是表级锁,可分为意向共享锁(IS锁)和意向排他锁(IX锁)。
事务在请求S锁和X锁前,需要先获得对应的IS、IX锁。
意向锁产生的主要目的是为了处理行锁和表锁之间的冲突,用于表明“某个事务正在某一行上持有了锁,或者准备去持有锁”,提高获取锁的效率。

共享锁、排他锁与意向锁的兼容性如下表:

XIXS
X冲突冲突冲突冲突
IX冲突兼容冲突兼容
S冲突冲突兼容兼容
IS冲突兼容兼容兼容

三、行锁

(1)记录锁

仅仅锁住索引记录的一行。
单条索引记录上加锁,record lock锁住的永远是索引,而非记录本身,即使该表上没有任何索引,那么innodb会在后台创建一个隐藏的聚集主键索引,那么锁住的就是这个隐藏的聚集主键索引。所以说当一条sql没有走主键或唯一索引时,那么将会在每一条聚集索引后面加X锁,这个类似于表锁,但原理上和表锁应该是完全不同的。
参见官方文档:

If the table has no PRIMARY KEY or suitable UNIQUE index, InnoDB internally generates a hidden clustered index on a synthetic column containing row ID values. The rows are ordered by the ID that InnoDB assigns to the rows in such a table. The row ID is a 6-byte field that increases monotonically as new rows are inserted. Thus, the rows ordered by the row ID are physically in insertion order【如果表没有主键或合适的唯一索引,InnoDB会在包含行ID值的合成列上内部生成一个隐藏的聚集索引。这些行是按InnoDB分配给这样一个表中的行的ID排序的。行ID是一个6字节的字段,随着新行的插入而单调增加。因此,按行ID排序的行在物理上是按插入顺序排列的】

(2)Gap 锁

区间锁, 仅仅锁住一个索引区间(开区间)。
在索引记录之间的间隙中加锁,或者是在某一条索引记录之前或者之后加锁,并不包括该索引记录本身。
只在REPEATABLE READ或以上的隔离级别下的特定操作才会取得gap lock或nextkey lock。UPDATE和DELETE时,除了对唯一索引的唯一搜索外都会获取gap锁或next-key锁。即锁住其扫描的范围。

参见官方文档:

For consistent reads, there is an important difference from the READ COMMITTED isolation level:
All consistent reads within the same transaction read the snapshot established by the first read. 
For locking reads (SELECT with FOR UPDATE or LOCK IN SHARE MODE), UPDATE, and DELETE statements,locking depends on whether the statement uses a unique index with a unique search condition,or a range-type search condition. For a unique index with a unique search condition,InnoDB locks only the index record found, not the gap before it. For other search conditions,InnoDB locks the index range scanned, using gap locks or next-key locks to block insertions by other sessions into the gaps covered by the range.【对于一致读取,与读取提交隔离级别有一个重要区别:同一事务中的所有一致读取都读取由第一次读取建立的快照;对于锁定读取(选择with For UPDATE或LOCK IN SHARE MODE)、UPDATE和DELETE语句,锁定取决于语句是否使用具有唯一搜索条件的唯一索引,或范围类型搜索条件。对于具有唯一搜索条件的唯一索引,InnoDB只锁定找到的索引记录,而不锁定它之前的间隔。对于其他搜索条件,InnoDB锁定扫描的索引范围,使用gap锁或next key锁阻止插入,通过其他环节进入空白所涵盖的范围。】

作用是防止幻读。通过锁阻止特定条件的新记录的插入,因为插入时也要获取gap锁(Insert Intention Locks)。

查看:

 nnodb_locks_unsafe_for_binlog:默认值为0,即启用gap lock。
最主要的作用就是控制innodb是否对gap加锁。
(3)Next-key 锁(next-key lock

record lock + gap lock, 左开右闭区间。
默认情况下,innodb使用next-key locks来锁定记录。
但当查询的索引含有唯一属性的时候,Next-Key Lock 会进行优化,将其降级为Record Lock,即仅锁住索引本身,不是范围。

Infimun+Supremum Records:InnoDB 存储引擎中,每个数据页中有两个虚拟的行记录,用
来限定记录的边界。
Infimun 记录是比该页中任何主键都要小的值,Supermum 指比任何可能大的值还要大的值。
这两个值在页创建时被建立,并且在任何情况下不会被删除
如果 SESSION 1 的表扫描没有用到索引,那么 gap next-key 锁住的范围是整个表,即任何
值都不能插入。
官网文档:
By default, InnoDB operates in REPEATABLE READ transaction isolation level. In this case, InnoDB
uses next-key locks for searches and index scans, which prevents phantom rows (see Section 0
214.7.4, Phantom Rows”).【默认情况下,InnoDB在可重复读取事务隔离级别下运行。在这种情况下,InnoDB使用next键锁进行搜索和索引扫描,从而防止虚行(参见0 214.7.4节,“虚行”)。】
(4)插入意向锁(Insert Intention Locks)

插入意向锁(Insert Intention Lock),在insert操作时候产生。在多个事务同时写入不同数据至同一索引间隙的时候,并不需要等待其他事务完成,不会发生锁等待。
假设有一个记录索引包含键值3和6,不同的事务分别插入4和5,每个事务都会产生一个加在3-6之间的插入意向锁,获取在插入行上的排它锁,但是不会被互相锁住,因为数据行并不冲突。插入意向锁并非意向锁,而是一种特殊的间隙锁

GapInsert IntentionRecord
Gap兼容兼容兼容兼容
Insert Intention冲突兼容兼容冲突
Record兼容兼容冲突冲突
Next-Key兼容兼容冲突冲突

四、自增锁(AUTO-INC Locks)

AUTO-INC锁是当向使用含有AUTO_INCREMENT列的表中插入数据时需要获取的一种特殊的表级锁 ;在最简单的情况下,如果一个事务正在向表中插入值,则任何其他事务必须等待对该表执行自己的插入操作,以便第一个事务插入的行的值是连续的。 
innodb_autoinc_lock_mode配置选项控制用于自动增量锁定的算法。 它允许您选择如何在可预测的自动递增值序列和插入操作的最大并发性之间进行权衡。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值