本文接着上篇,是对数据库锁机制的总结。对数据库锁的了解,往往也是区分Java程序员中高级的一个重要标志。日常开发中,锁也是我们容易踩坑的地方。尤其当你的系统遇到高并发大访问量操作数据库时,稍有不慎可能造成线上事故。因此,掌握好锁机制的重要性不言而喻。

六、 InnoDB锁的类型七、 InnoDB锁几种算法

六、 InnoDB锁的类型

  1. InnoDB锁的类型

共享锁(s),允许事务读一行事务。

排他锁(x),允许事务删除或更新一行锁。

表级意向锁(IS,IX),为了支持在不同粒度上的加锁操作,innodb存储引擎支持一种额外的方式。意向锁希望在更细粒度上进行加锁。

IS,IX锁是分别在select 或是在update等操作时加的读意向锁或者是写意向锁和表级别的s锁或表级的x锁冲突,但是和行级别锁的s或x锁基本不发生冲突。

总的来说,InnoDB的锁定机制和Oracle数据库有不少相似之处。InnoDB的行级锁定同样分为两种类型,共享锁排他锁,而在锁定机制的实现过程中为了让行级锁定和表级锁定共存,InnoDB也同样使用了意向锁(表级锁定)的概念,也就有了意向共享锁和意向排他锁这两种。

  1. 事务的锁机制详解

当一个事务需要给自己需要的某个资源加锁的时候:

(1)如果遇到一个共享锁正锁定着自己需要的资源的时候,自己可以再加一个共享锁,不过不能加排他锁;

(2)但是,如果遇到自己需要锁定的资源已经被一个排他锁占有之后,则只能等待该锁定释放资源之后自己才能获取锁定资源并添加自己的锁定。

而意向锁的作用就是当一个事务在需要获取资源锁定的时候,如果遇到自己需要的资源已经被排他锁占用的时候,该事务可以需要锁定行的表上面添加一个合适的意向锁。

如果自己需要一个共享锁,那么就在表上面添加一个意向共享锁。而如果自己需要的是某行(或者某些行)上面添加一个排他锁的话,则先在表上面添加一个意向排他锁。意向共享锁可以同时并存多个,但是意向排他锁同时只能有一个存在。

所以,可以说InnoDB的锁定模式实际上可以分为四种:共享锁(S),排他锁(X),意向共享锁(IS)和意向排他锁(IX),我们可以通过以下表格来总结上面这四种锁的共存逻辑关系:




mysql innodb引擎数据库有哪些操作会锁表 mysql innodb锁有哪几种_数据

innodb 存储引擎中锁的兼容性



如果一个事务请求的锁模式与当前的锁兼容,InnoDB就将请求的锁授予该事务;反之,如果两者不兼容,该事务就要等待锁释放。

意向锁是InnoDB自动加的,不需用户干预。

对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);对于普通SELECT语句,InnoDB不会加任何锁;

事务可以通过以下语句显式给记录集加共享锁或排他锁。

共享锁(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE排他锁(X):SELECT * FROM table_name WHERE ... FOR UPDATE

用SELECT ... IN SHARE MODE获得共享锁,主要用在需要数据依存关系时来确认某行记录是否存在,并确保没有人对这个记录进行UPDATE或者DELETE操作。

但是如果当前事务也需要对该记录进行更新操作,则很有可能造成死锁,对于锁定行记录后需要进行更新操作的应用,应该使用SELECT... FOR UPDATE方式获得排他锁。

七、 InnoDB锁几种算法(针对可重复读级别)

  1. 锁的算法概要

1.1. record lock:单个行上的锁。

1.2. gap lock 间隙锁:锁定一个范围。

1.3. Next-Key 锁:前两种锁的组合,锁定一个范围并且锁定记录本身。

1.4. Insert Intention 插入意图锁(或叫意向锁)

Record lock 总是会锁住索引记录,如果在建立表的时候没有设置索引,那么会使用隐式的主键来进行锁定。gap lock锁定一个范围。 Next-Key是结合了gap锁以及record锁的一种算法。

(1) 主键索引,查询条件存在数据的情况下,锁住一行记录

(2) 唯一索引,查询条件存在数据的情况下,锁住一行记录

(3) 普通索引,查询条件存在数据的情况下,锁住该行记录以及该行范围

(4) 无索引,查询条件存在数据的情况下,聚簇索引上所有的记录,都被加上了X锁。




mysql innodb引擎数据库有哪些操作会锁表 mysql innodb锁有哪几种_数据_02

上图是查询条件存在的情况下,测试版本为mysql 5.6.30-debug



2.InnoDB行锁的实现方式

InnoDB行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁。

在实际应用中,要特别注意InnoDB行锁的这一特性,不然的话,可能导致大量的锁冲突,从而影响并发性能。

下面通过实例来加以说明。

(1)在不通过索引条件查询的时候,InnoDB确实使用的是表锁,而不是行锁。

(2)由于MySQL的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的。

(3)当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行,另外,不论是使用主键索引、唯一索引或普通索引,InnoDB都会使用行锁来对数据加锁。

(4)即便在条件中使用了索引字段,但是否使用索引来检索数据是由MySQL通过判断不同执行计划的代价来决定的,如果MySQL认为全表扫描效率更高,比如对一些很小的表,它就不会使用索引,这种情况下InnoDB将使用表锁,而不是行锁。因此,在分析锁冲突时,别忘了explain SQL的执行计划,以确认是否真正使用了索引。

3.间隙锁(Next-Key锁)

当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP),InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(Next-Key锁)。

例:假如emp表中只有101条记录,其empid的值分别是 1,2,...,100,101,下面的SQL:

mysql> select * from emp where empid > 100 for update;

是一个范围条件的检索,InnoDB不仅会对符合条件的empid值为101的记录加锁,也会对empid大于101(这些记录并不存在)的“间隙”加锁。

InnoDB使用间隙锁的目的何在?

(1)防止幻读,以满足相关隔离级别的要求。对于上面的例子,要是不使用间隙锁,如果其他事务插入了empid大于100的任何记录,那么本事务如果再次执行上述语句,就会发生幻读;

(2)为了满足其恢复和复制的需要

很显然,在使用范围条件检索并锁定记录时,即使某些不存在的键值也会被无辜的锁定,而造成在锁定的时候无法插入锁定键值范围内的任何数据。在某些场景下这可能会对性能造成很大的危害。

除了间隙锁给InnoDB带来性能的负面影响之外,通过索引实现锁定的方式还存在其他几个较大的性能隐患:

(1)当Query无法利用索引的时候,InnoDB会放弃使用行级别锁定而改用表级别的锁定,造成并发性能的降低;

(2)当Query使用的索引并不包含所有过滤条件的时候,数据检索使用到的索引键所只想的数据可能有部分并不属于该Query的结果集的行列,但是也会被锁定,因为间隙锁锁定的是一个范围,而不是具体的索引键;

(3)当Query在使用索引定位数据的时候,如果使用的索引键一样但访问的数据行不相同的时候(索引只是过滤条件的一部分),一样会被锁定。

因此,在实际应用开发中,尤其是并发插入比较多的应用,我们要尽量优化业务逻辑,尽量使用相等条件来访问更新数据,避免使用范围条件。

还要特别说明的是,InnoDB除了通过范围条件加锁时使用间隙锁外,如果使用相等条件请求给一个不存在的记录加锁,InnoDB也会使用间隙锁。


读到此文的您,觉得本文对你有用,欢迎点赞和转发

也欢迎大家关注我,我会坚持不断输出有价值的技术文章。