【六】MySQL之锁机制 总结(InnoDB)

版权声明:转载注明出处 https://blog.csdn.net/jy02268879/article/details/85109568

本文仅涉及的InnoDB存储引擎
.MySQL不同的存储引擎支持不同的锁机制。

一,Spring Transaction的事务传播行为

1.PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务。

2.PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。

3.PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。

4.PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。

5.PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

6.PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。

7.PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

二,事务的隔离级别

READ_UNCOMMITTED这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。
READ_COMMITTED保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。
REPEATABLE_READ这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了不可重复读。
SERIALIZABLE这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读.

InnoDB 默认事务隔离级别为可重复读(重复读取,RR)

三,事务的三级封锁协议

1.一级封锁协议:事务Ť中如果对数据ř有写操作,必须在这个事务中对 - [R的第一次读操作前对它加X锁,直到事务结束才释放事务结束包括正常结束(COMMIT)和非正常结束(ROLLBACK)。

2.二级封锁协议:一级封锁协议加上事务Ť在读取数据ř之前必须先对其加小号锁,读完后方可释放小号锁。

3.三级封锁协议:一级封锁协议加上事务Ť在读取数据ř之前必须先对其加小号锁,直到事务结束才释放。

满足高级锁则一定满足低级锁。但有个非常致命的地方,一级锁协议就要在第一次读加X锁,直到事务结束。几乎就要在整个事务加写锁了,效率非常低。

三级封锁协议只是一个理论上的东西,实际数据库常用另一套方法来解决事务并发问题。

四,锁粒度分类

按照锁的粒度划分时,MySQL的有3种锁:1行锁2表锁3页面锁。

五,InnoDB的中的7种锁

1.共享/排他锁

粒度:行锁

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

不同的数据行,只要访问的是同一个索引(不同的数据行,但索引值相同)也会被锁。

共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。

排他锁(X):允许获取排他锁的事务更新数据,阻止其他事务取得相同的数据集共享读锁和排他写锁。

  • 事务拿到某一行记录的共享S锁,才可以读取这一行;

  • 事务拿到某一行记录的排它X锁,才可以修改或者删除这一行;

  • 多个事务可以拿到一把S锁,读读可以并行;

  • 而只有一个事务可以拿到X锁,写写,读写串行;

目的: 

1.共享锁:提高读读并发
2.排他锁:保证数据的一致性

2.意向锁(Intention Locks)

粒度:表锁

意向共享锁(IS):事务打算给数据行共享锁,事务在给一个数据行加共享锁S前必须先取得该表的IS锁。

意向排他锁(IX):事务打算给数据行加排他锁,事务在给一个数据行加排他锁X前必须先取得该表的IX锁。

意向锁仅仅表明意向,它是比较弱的锁,意向锁之间并不相互互斥,而是可以并行的。

兼容

兼容 X IX S IS
X(行)
IX
S(行)
IS

目的:

1.IX,IS是表级锁,不会和行级的X,S锁发生冲突。只会和表级的X,S发生冲突
2.意向锁是在添加行锁之前添加
0.3。如果没有意向锁,当向一个表添加表级X锁时,就需要遍历整张表来判断是否存行锁,以免发生冲突
4.如果有了意向锁,只需要判断该意向锁与表级锁是否兼容即可。 

3.插入意向锁(插入意图锁)

粒度:行锁

插入意向锁,是间隙锁(Gap Locks)的一种(所以,也是实施在索引上的),它是专门针对插入操作的。

多个事务,在同一个索引,同一个范围区间插入记录时,如果插入的位置不冲突,不会阻塞彼此。

目的:提高写写并发

ID name
1 S
8 a
16 C
事务1开启 事务2开启
insert t value(2,'Q') insert t value(5,'H')
select * from t; select * from t;

1 s

2 q

8 a

16 c

1 s

5 h

8 a

16 c

事务1提交 事务2提交

事物一和事物二都是对表的同一索引范围进行插入,使用的插入意向锁,由于插入的记录并不冲突,所以并不会阻塞事物二。

如果事物二插入的记录与事物一冲突,会被X锁阻塞。

4.间隙锁(Gap Locks)

粒度:行锁

封锁记录中的间隔,防止间隔中被其他事务插入。

间隙锁基于非唯一索引,它锁定一段范围内(开区间)的索引记录。

锁住的是一个区间,不仅仅是而这个区间中的每一条数据。

SELECT * FROM table WHERE id BETWEN 1 AND 10 FOR UPDATE;

即在所有(1,10)区间内的记录行都会被锁住,所有ID为2,3,4,5,6,7,8,9的数据行的插入会被阻塞,但是1和10两条记录行并不会被锁住。 

例如学生表中只有5条记录,ID的值分别是1,2,3,4,5

SELECT * FROM student WHERE id > 4 FOR UPDATE

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

当用范围条件而不是相等条件检索数据时,InnoDB的会给符合条件的已有数据的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”时, InnoDB中也会对这个“间隙”加锁。

在使用范围条件检索并锁定记录时,InnoDB的的这种加锁机制会阻塞符合条件范围内键值的并发插入,会造成严重的锁等待。因此,在实际开发中,尤其是并发插入比较多的应用,我们要尽量优化业务逻辑,尽量使用相等条件来访问更新数据,避免使用范围条件。

如果把事务的隔离级别降级为读提交(Read Committed,RC),间隙锁则会自动失效。

5.临键锁(Next-Key Locks)

粒度:行锁

一种特殊的间隙锁,解决幻读。

每个数据行上的非唯一索引列上都会存在一把临键锁,当某个事务持有该数据行的临键锁时,会锁住一段左开闭向左向右区间的数据。需要强调的一点是,InnoDB 中行级锁的英文基于索引实现的,在唯一索引列(包括主键列)上不存在临键锁。

如果把事务的隔离级别降级为RC,临键锁会失效。

6.自增锁(Auto-inc Locks)

粒度:表锁

专门针对事务插入AUTO_INCREMENT类型的列

如果一个事务正在往表中插入记录,所有其他事务的插入必须等待,以便第一个事务插入的行,是连续的主键值。

show variables like 'innodb_autoinc_lock_mode';​​​​​​​

 mysql> show variables like 'innodb_autoinc_lock_mode';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_autoinc_lock_mode | 1     |
+--------------------------+-------+

innodb_autoinc_lock_mode有3种配置模式:0、1、2,分别对应”传统模式”, “连续模式”, “交错模式”。
传统模式:涉及auto-increment列的插入语句加的表级AUTO-INC锁,只有插入执行结束后才会释放锁。这是一种兼容MySQL 5.1之前版本的策略。
连续模式:可以事先确定插入行数的语句(包括单行和多行插入),分配连续的确定的auto-increment值;对于插入行数不确定的插入语句,仍加表锁。这种模式下,事务回滚,auto-increment值不会回滚,换句话说,自增列内容会不连续。
交错模式:同一时刻多条SQL语句产生交错的auto-increment值。 

7.记录锁(记录锁)

粒度:行锁

记录锁存在于包括主键索引在内的唯一索引中,锁定单条索引记录。

例如:select * from t where id = 1 for update;

需要注意的是:id 列必须为唯一索引列主键列,否则上述语句加的锁就会变成临键锁

它会在ID = 1的索引记录上加锁,以阻止其他事务插入,更新,删除ID = 1的这一行。

说明:select * from t,其中id = 1; 则是快照读(SnapShot Read),它并不加锁
 

六,加锁方式

1.自动

表锁

SQL语句不命中索引,则InnoDB的使用表锁

意向锁是InnoDB的自动加的,不需显示声明。

行锁

对于更新,删除和INSERT语句时,InnoDB会自动给涉及数据集加排他锁(X);对于普通SELECT语句时,InnoDB不会加任何锁。

InnoDB的行锁是通过索引上的索引项来实现的,这一点的MySQL与甲骨文不同,后者是通过在数据中对相应。只有通过索引条件检索数据,InnoDB的才会使用行级锁,否则,InnoDB的将使用表锁。

不同的数据行,只要访问的是同一个索引(不同的数据行,但索引值相同)也会被锁。

2.显示声明

表锁

    1.使用LOCK TALBES虽然可以给InnoDB加表级锁,但表锁不是由InnoDB存储引擎层管理的,而是由其上一层MySQL Server负责的,仅当autocommit = 0,innodb_table_lock = 1(默认设置)时,InnoDB的层才能知道的MySQL加的表锁时,MySQL服务器才能感知的InnoDB加的行锁,这种情况下,InnoDB的才能自动识别涉及表级锁的死锁;否则,InnoDB的将无法自动检测并处理这种死锁。

    2.在用LOCK TABLES对InnoDB锁时要注意,要将AUTOCOMMIT设为0,否则MySQL不会给表加锁 ;事务结束前,不要用UNLOCAK TABLES释放表锁,因为UNLOCK TABLES会隐含地提交事务; COMMIT或ROLLBACK时不能释放用LOCK TABLES加的表级锁,必须用UNLOCK TABLES显示释放表锁。

SET autocommit=0;

LOCK TABLES t1 WRITE, t2 READ, ...;

do something with tables t1 and t2 here ...

COMMIT;

UNLOCK TABLES

行锁

共享锁(S):

SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE

排他锁(X):

SELECT * FROM table_name WHERE ... FOR UPDATE

如果对同一行记录先进行查询再进行修改,为了防止当前事务出现死锁,应该直接加排他锁。

七,查询行锁竞争情况

show status like 'innodb_row_lock%';

innodb_row_lock_current_waits当前正在等待锁的数量

innodb_row_lock_time从系统启动到现在,锁定总时长

innodb_row_lock_time_avg每次等待所花平均时间

innodb_row_lock_time_max从系统启动到现在,最长的一次等待所花时间

innodb_row_lock_waits从系统启动到现在,总共等待次数

猜你喜欢

转载自blog.csdn.net/jy02268879/article/details/85109568
今日推荐