InnoDB存储引擎的锁

InnoDB存储引擎的锁

锁的类型

锁的类型包括:

1.     共享锁(S lock),允许事务读取一行数据

2.     排他锁(X lock),允许事务删除或更新一行数据

锁的兼容性a

 

X

S

X

不兼容

不兼容

S

不兼容

兼容

S和X都是行锁。

InnoDB支持的是行级别的锁。

InnoDB支持多粒度锁定,这种锁定允许事务在行级上的锁和表级上的锁同时存在。为了支持在不同粒度上进行加锁操作,InnoDB支持一种额外的锁方式:意向锁。

意向锁包括:

意向共享锁(IS lock),事务想要获取一张表中某几行的共享锁

意向排他锁(IX lock),事务想要获取一张表中某几种的排他锁

在对记录r加X锁之前,已经有事务对表1进行了S表锁,那么表1上已存在S锁,之后事务需要对记录r在表1上加上IX,由于不兼容,所以该事物需要等待表锁操作的完成。

锁的兼容性b

 

IS

IX

S

X

IS

兼容

兼容

兼容

不兼容

IX

兼容

兼容

不兼容

不兼容

S

兼容

不兼容

兼容

不兼容

X

不兼容

不兼容

不兼容

不兼容

一致性非锁定读

一致性非锁定读是指InnoDB存储引擎通过行多版本控制的方式来读取当前执行时间数据库中行的数据,不需要等待访问的行上X锁的释放(此时访问的是该行的快照数据)。

行多版本技术:一个行记录可能有不止一个快照数据。

不同的事务隔离级别下,读取的快照数据也会不同。

Read committed隔离级别下:总会读取最新的一份快照数据。

Repeatable read隔离级别下:总会读取事务开始时的行快照数据。

举例来看一致性非锁定读是怎样的情况,以及在不同隔离级别下的特性:(table表只有一个字段id)

Session A

mysql>BEGIN;

mysql>SELECT * FROM table1 WHERE id=1;

在上面回话A中,开启了一个事务,并读取了id=1的记录(假设记录存在表中),但是事务并没有结束。与此同时,再启动另一个回话B

Session B

mysql>BEGIN;

mysql>UPDATE table1 SET id=3 WHERE id=1;

在回话B中更新id=1的值改为3,此时B事务依然没有提交,这样id=1上加了一个X锁。如果此时在A会话中再次读取id=1的记录,根据InnoDB存储引擎的特性,在read committed和repeatable read下会使用一致性非锁定读,此时读取的应该是id=1的记录。

截止在会话B中提交事务。

在B事务提交后,在会话A中再次执行查询,这时候在在read committed和repeatable read下会使用一致性非锁定读,就会得到不一样的查询结果。Read committed下读取的为空,repeatable read下读取的还是id=1的记录。

一致性锁定读

在默认情况下,即事务的隔离级别是repeatable read模式下,InnoDB存储引擎的SELECT操作使用的是一致性非锁定读。但是在某些情况下,用户需要显示的读取数据操作进行加锁保证数据逻辑的一致性。

InnoDB提供了两种方式实现一致性锁定读:

1.     Select … for udpate,对读取的行加了X锁

2.     Select … lock in share mode,对读取的行加了S锁

自增长与锁

自增长字段通常会在表设计时作为主键来用。InnoDB存储引擎对于每张包含自增长字段的表都会有一个自增长计数器维护在内容中。当进行插入操作时,这个计数器会被初始化,插入操作会依据这个自增长的计数器加1赋予自增长列,这个实现方式成为 auto_inc locking。这是一种特殊的表锁机制,为了提高插入性能,所不是在一个事务完成后才释放,而是完成插入操作时就立即释放。

比较新的mysql版本提供了一种轻量级互斥量的自增长机制。

提供属性innodb_autoinc_lock_mode俩控制自增长模式(取值有0、1、2,默认值是1)。

锁的算法

InnoDB存储引擎包含3中行锁的算法:

1.     record lock,单个行记录上的锁

2.     gap lock,锁定一个范围,但不包含本身

3.     next-key lock,等同于gap lock+record lock

若事务T1已经通过next-key locking锁定了如下范围

(10,11]、(11,13]

当插入新的纪录12时,则锁定的范围变为

(10,11] 、(11,12] 、(12,13]

当查询的索引具有唯一特性时,InnoDB存储引擎会对next-key lock进行优化,降级为record lock,即仅锁定本身。

若是辅助索引,则情况会有所不同:

Create table z(a int,b int, primary key(a), key(b));

Insert into z select 1,1

Insert into z select 3,1

Insert into z select 5,3

Insert into z select 7,6

Insert into z select 10,8

该表b是辅助索引,若执行如下sql

Select * from z where b=3 for update

很明显sql会根据辅助索引来查询,使用next-key locking来加锁。并且因为有两个索引,需要分别锁定,对于聚集索引会对a=5的行添加record lock,而对于非聚集索引,回家上next-key lock,会对b列的区间(1,3)锁定,特别注意InnoDB存储引擎还会对辅助索引下一个键值添加上gap lock,即对区间(3,6)锁定。所以对以下sql的执行都会阻塞:

Select * from z where a=5 lock in share mode;

Insert into z select 4,2

Insert into z select 6,5

 

由此可见gap lock是为了防止多个事务把数据插入到同一范围内,而这会导致Phantom problem。

猜你喜欢

转载自www.cnblogs.com/share2perfect/p/9547769.html