MySQL的innoDB锁机制以及死锁处理(2)

那么加锁区间就是(9,positive infinity),别的事务无法在此区间插入新数据同时也不允许更新已有的数据到这个区间,也就是 update liuhe set age=19 where id=1是不允许的(因为这也类似于新增)。

整个举例1说明:

行锁防止别的事务修改或删除,GAP锁防止别的事务新增(防止新增包括insert和update已有数据到这个范围中),行锁和GAP锁结合形成的的Next-Key锁共同解决了RR级别在写数据时的部分幻读问题,一定注意只是部分幻读问题;

举例2:

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

Select * from emp where empid > 100 for update;

是一个范围条件的检索,InnoDB不仅会对符合条件的empid值为101的记录加锁,也会对empid大于101(这些记录并不存在)的“间隙”加锁,这样其他事务就不能在empid > 100范围insert数据了。

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

举例3

假如emp表中只有101条记录,其empid的值分别是 1,5,7,9,10,19,那么下面的sql:

select * from emp where empid >2 and empid <16 for update ;

那么InnoDB不仅会对符合条件的empid值为5,7,9,10的记录加锁,也会对(2,16)这个区间加“间隙”加锁,这样其他事务就不能在(2,16)范围insert数据了,并且也不允许更新已有的数据到这个区间;

三:关于innodb锁机制需要注意的是:

1)InnoDB行锁是通过给索引项加锁实现的,如果没有索引,InnoDB会通过隐藏的聚簇索引来对记录加锁。也就是说:如果不通过索引条件检索数据,那么InnoDB将对表中所有数据加锁,实际效果跟表锁一样。

2)由于MySQL的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的。说白了就是,where id=1 for update 会锁定所有id=1的数据行,如果是where id=1 and for update,这样会把所有 id=1以及所有name='liuwenhe'的行都上排它锁;

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

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

四:查看innodb的相关锁;

1)查询相关的锁:

information_schema 库中增加了三个关于锁的表:

innodb_trx        ## 当前运行的所有事务 ,还有具体的语句,

innodb_locks      ## 当前出现的锁,只有

innodb_lock_waits  ## 锁等待的对应关系

看一下表结构:

root@127.0.0.1 : information_schema 13:28:38> desc innodb_locks;

+————-+———————+——+—–+———+——-+

| Field      | Type                | Null | Key | Default | Extra |

+————-+———————+——+—–+———+——-+

| lock_id    | varchar(81)        | NO  |    |        |      |#锁ID

| lock_trx_id | varchar(18)        | NO  |    |        |      |#拥有锁的事务ID

| lock_mode  | varchar(32)        | NO  |    |        |      |#锁模式

| lock_type  | varchar(32)        | NO  |    |        |      |#锁类型

| lock_table  | varchar(1024)      | NO  |    |        |      |#被锁的表

| lock_index  | varchar(1024)      | YES  |    | NULL    |      |#被锁的索引

| lock_space  | bigint(21) unsigned | YES  |    | NULL    |      |#被锁的表空间号

| lock_page  | bigint(21) unsigned | YES  |    | NULL    |      |#被锁的页号

| lock_rec    | bigint(21) unsigned | YES  |    | NULL    |      |#被锁的记录号

| lock_data  | varchar(8192)      | YES  |    | NULL    |      |#被锁的数据

+————-+———————+——+—–+———+——-+

10 rows in set (0.00 sec)

root@127.0.0.1 : information_schema 13:28:56> desc innodb_lock_waits;

+——————-+————-+——+—–+———+——-+

| Field            | Type        | Null | Key | Default | Extra |

+——————-+————-+——+—–+———+——-+

| requesting_trx_id | varchar(18) | NO  |    |        |      |#请求锁的事务ID(也就是等待锁的id)

| requested_lock_id | varchar(81) | NO  |    |        |      |#请求锁的锁ID

| blocking_trx_id  | varchar(18) | NO  |    |        |      |#当前拥有锁的事务ID

| blocking_lock_id  | varchar(81) | NO  |    |        |      |#当前拥有锁的锁ID

+——————-+————-+——+—–+———+——-+

4 rows in set (0.00 sec)

root@127.0.0.1 : information_schema 13:29:05> desc innodb_trx ;

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/2ebc27f633bb4a9889eac9ae72f10830.html