MySQL锁
概述
不同存储引擎的锁机制:
- MyISAM采用的是表级锁。
- InnoDB既支持行级锁,也支持表级锁。InnoDB行锁是通过给索引项加锁来实现的,只有通过索引条件检索数据才会加行锁,在没有索引的情况下使用表锁。
表锁行锁特点:
- 表锁:开销小,加锁快,不会出现死锁,锁粒度大,发生锁冲突的概率大,并发度低,适合查询场景。
- 行锁:开销大,加锁慢,会出现死锁,发生锁冲突几率低,并发度高,适合大部分应用场景。
MyISAM
- 表共享读锁(Table Read Lock):不会阻塞其他事务对同一表的读请求,但会阻塞对同一表的写请求;
- 表独占写锁(Table Write Lock):会阻塞其他事务对同一表的读和写操作;
MyISAM在执行查询语句前,会自动给涉及的所有表加读锁,在执行更新操作前,会自动给涉及的表加写锁。
MyISAM支持并发插入,以减少给定表的读和写操作之间的争用:如果MyISAM表在数据文件中没有空闲块,则行始终插入数据文件的末尾。在这种情况下,可以自由混合并发使用MyISAM表的insert和select语句而不需要加锁—你可以在其他线程进行读操作的时候,同时将行插入到MyISAM表中。文件中的空闲块可能是从表格中间删除或者更新的行而产生的。如果文件中间有空闲块,则并发插入会被禁用。
InnoDB
- 共享锁(S):允许一个事物去读一行,阻止其他事物获得相同数据集的排他锁。即多个事务可以同时读取同一个资源,但是不允许其他事务修改。
- 排他锁(X):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的读锁和写锁。写锁是排他的,写锁会阻塞其他的写锁和读锁。
为了允许行锁和表锁共存,实现多粒度机制,InnoDB还有两种内部使用的意向锁:
-
意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须取得该表的IS锁。
-
意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。
共享锁(S) 排他锁(X) 意向共享锁(IS) 意向排他锁(IX) 共享锁(s) 兼容 冲突 兼容 冲突 排他锁(X) 冲突 冲突 冲突 冲突 意向共享锁(IS) 兼容 冲突 兼容 兼容 意向排他锁(IX) 冲突 冲突 兼容 兼容 注意:上表的S,X都是表级的
意向锁是InnoDB自动加的,不需要用户干预。
使用意向锁的目的:https://www.zhihu.com/question/51513268
InnoDB的update,delete,insert都会自动给涉及到的数据加上排他锁,select语句默认不会加任何类型锁。
间隙锁
当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做"间隙(GAP)",InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁。
很显然,在使用范围条件检索并锁定记录时,InnoDB这种加锁机制会阻塞符合条件范围内键值的并发插入,这往往会造成严重的锁等待。因此,在实际应用开发中,尤其是并发插入比较多的应用,我们要尽量优化业务逻辑,尽量使用相等条件来访问更新数据,避免使用范围条件。
InnoDB使用间隙锁的目的:
- 防止幻读
- 满足恢复和复制的需要
乐观锁与悲观锁
乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。
悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。
MVCC
MVCC多版本并发控制,它是行级锁的变种。MVCC最大的优点是读不加锁,因此读写不冲突,并发性能好。InnoDB实现MVCC,多个版本数据可以共存,主要基于以下技术及数据结构:
- 隐藏列:InnoDB中每行数据都有隐藏列,隐藏列中包含了本行数据的事务id、指向undolog的指针等。
- 基于undolog的版本链:前面说到每行数据的隐藏列中包含了指向undolog的指针,而每条undolog也会指向更早版本的undo log从而形成一条版本链。
- ReadView:通过隐藏列和版本链,MySQL可以将数据恢复到指定版本;但是具体要恢复到哪个版本,则需要根据ReadView来确定。所谓ReadView,是指事务在某一时刻给整个事务系统打快照(trx_sys),之后再进行读操作是,会将读取到的事务中的事务id与trx_sys快照比较,从而判断数据对该ReadView是否可见。
简单说MVCC会给每行加创建版本号和删除版本号的字段,而每一个事务在开启的时候,都有一个唯一的递增的版本号。
-
SELECT
- 读取创建版本小于或等于当前事务版本号,并且 删除版本 为空或大于当前事务版本号的记录。这样可以保证在读取之前记录是存在的
-
INSERT
- 将当前事务的版本号保存至行的创建版本号
-
UPDATE
- 新插入一行,并以当前事务的版本号作为新行的创建版本号,同时将原记录行的删除版本号设置为当前事务版本号
-
DELETE
- 将当前事务的版本号保存至行的删除版本号
MVCC:https://blog.csdn.net/qq_33330687/article/details/89004462
- 将当前事务的版本号保存至行的删除版本号