美文网首页
MySql InnoDB 锁(lock)

MySql InnoDB 锁(lock)

作者: jyhnp | 来源:发表于2020-06-01 16:07 被阅读0次

文章是通过《Mysql技术内幕 InnoDB存储引擎》这本书概括的,主要是锁的这一章,包括共享锁、排它锁、意向锁、一致性非锁定读、一致性锁定读、自增长与锁这些知识点,稍微有点长,耐心看下来,定会有收获。

测试表结构:

DROP TABLE IF EXISTS t;
CREATE TABLE t (
id int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

BEGIN;
INSERT INTO t VALUES (1);
INSERT INTO t VALUES (2);
COMMIT;

开发多用户、数据库驱动应用时,最难的一个点就是:一方面要最大程度利用数据库的并发访问,另一方面还要确保每个用户能以一致的方式读取和修改数据,为此就有了锁机制。锁是为了支持资源进行并发访问,提供数据的完整性和一致性,Lock的对象是事务,用来锁定数据库中的对象,如表、页、行,事务仅在commit或rollback后释放。

InnoDB存储引擎实现两种标准的行级锁:

共享锁(S Lock):允许事务读一行数据
排它锁(X Lock):允许事务删除或更新一行数据

如果事务T1获取了行row1的共享锁,另一个事物T2同时也能获取row1的共享锁,这种情况成为锁兼容,但此时事务T3想获取row1的排它锁,则必须等事务T1、T2释放了row1的共享锁,这种称为锁不兼容。如图:


Mysql技术内幕.png

此外,InnoDB存储引擎支持多粒度(granular)锁定,这种锁允许事务在行级别和表级别上的锁同时存在,称之为意向锁。

意向共享锁(IS Lock): 事务获取一张表中某几行的共享锁
意向排它锁(IX Lock): 事务获取一张表中某几行的排它锁
Mysql技术内幕.png

举例来说:如果已经有事务对表1加了IS表锁,之后事务对记录row1操作在表1上加IX锁,由于锁不兼容,需要等表1的IS锁操作完成后。


Mysql技术内幕.png

一致性非锁定读:

如果读取的行正在执行DELETE或UPDATE操作(X锁),这时读操作不会因此去等待行上的锁释放, Innodb存储引擎会去读取一个快照数据。快照数据是指该行之前版本的数据,读快照数据不需要上锁,因为没有事务要对历史的数据进行修改。
如图:


Mysql技术内幕.png

在事务隔离级别READ COMMITED(读已提交)和REPEATBLE READ(可重复读,InnoDB默认)下使用一致性非锁定读;READ COMMIT事务隔离级别下,非一致性锁读总是读取最新的快照;REPEATABLE READ隔离级别会去读取锁定行的上一次快照。

来看一个例子说明两种隔离级别不同的一致性非锁读的效果,打开两个数据库命令行(A和B):
Session A:

mysql> use test;
Database changed

mysql> set session transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t where id = 1;
+----+
| id |
+----+
| 1 |
+----+
1 row in set (0.00 sec)

会话A设置事务隔离级别为REPEATABLE READ,并且执行命令Begin开启了一个事务,查询了t表id等于1的记录,但事务并没有结束,与此同时在会话B继续操作

session B:

mysql> begin;
Query OK, 0 rows affected (0.01 sec)

mysql> update t set id = 10 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql>
mysql> commit;
Query OK, 0 rows affected (0.00 sec)

会话B开启一个事物,修改id等于1的记录,commit提交会话B事务,再去会话A查看

Session A:

mysql> select * from t where id = 1;
+----+
| id |
+----+
| 1 |
+----+
1 row in set (0.00 sec)
REPEATABLE READ隔离级别下,一致性非锁定读查询的是当前事务锁定行的上一次快照,并不是最新的快照,我们再来看看READ COMMIT事务隔离级别的一致性非锁读效果

Session A:

mysql> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t where id = 2;
+----+
| id |
+----+
| 2 |
+----+
1 row in set (0.00 sec)

会话A设置事务隔离级别为COMMIT READ,查询id为2的记录,切到会话B

Session B:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> update t set id = 20 where id = 2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

会话B开启一个事务修改id为2的值为20,并提交事务,再去会话A

Session A:

mysql> select * from t where id = 2;
Empty set

mysql> select * from t where id = 20;
+----+
| id |
+----+
| 20 |
+----+
1 row in set (0.00 sec)

效果已经出来了,COMMIT READ隔离级别下,一致性非锁定读获取的是最新的快照,之前id为2的快照不是最新的了,所以查询不到了,id为20的为最新快照。从数据库理论来看,其违反了事务ACID的I特性,及隔离性。

一致性锁定读

Innodb默认配置下,使用REPEATABLE READ隔离级别,Select操作会使用一致性非锁定读。但是在某些情况下,需要加锁保证数据逻辑的一致性,就要用到一致性锁定读。

InnoDB存储引擎对于SELECT语句支持两种一致性锁定读(locking read)操作:
1.SELECT *** FOR UPDATE(排它锁)
2.SELECT *** LOCK IN SHARE MODE(共享锁)

注意:使用FOR UPDATE时查询条件有索引列(主键也行,主键列默认有索引)的话,锁的是当前行,否则锁全表,update语句的where条件也是一个道理

两种操作必须在事务中,当事务提交了,锁也就释放了,因此在使用完两种锁定SELECT语句时,要使用BEGIN,START TRANSACTION或者AUTOCOMMIT=0来开启事务。

自增长与锁

InnoDB存储引擎中,每个含有自增的表进行插入操作时都会有一个自增长计数器(auto-increment counter),当对含有自增长计数器表进行插入时,计数器会被初始化,执行如下语句得到计数器的值:

SELECT MAX(auto_inc_col) from t FOR UPDATE

这种自增实际采用表锁机制,通过FOR UPDATE(X锁)排它锁来保证唯一,为了提高性能,锁不是在一个事务完成后释放,而是在完成自增长插入后的SQL后立即释放,这种实现方式称为AUTO-INC Locking。

由于AUTO-INC Locking的并发性能较差,事务必须等待前一个插入的完成(虽然不用等待事务的完成),对于大量数据插入会影响插入性能。从MySQL5.1.22版本开始,InnoDB存储引擎提供一种轻量级互斥量(mutex)的自增长机制,对内存中的计数器进行累加操作,大大提升列并发插入的性能。此版本开始,InnoDB提供了一个参数innodb_autoinc_lock_mode来控制自增长模式,该参数默认值是1。如图所示插入类型和innodb_autoinc_lock_mode值说明:

Mysql技术内幕.png Mysql技术内.png

死锁

死锁的概念:死锁是指两个或两个一上的事务在执行过程中,因争夺资源而造成
的一种互相等待的现象。

解决死锁最简单的就是超时机制,当两个事务互相等待时,当一个等待时间超过了阈值,此事务就进行回滚,另一个事务就能继续进行了,超时机制虽然简单,若超时的事务占比较大,比如更新了很多行,占用了较多的undo log,这个事务回滚的时间相对另一个事务所占的时间可能会很多。当前数据库普遍采用wait-for graph(等待图)的方式 进行死锁检测。

Mysql技术内幕.png

wait-for graph是一种主动的死锁监测机制,在每个事务请求锁并发等待时都会判断是否存在回路,若存在回路则有死锁(如图t1和t2事务存在回路),InnoDB 存储引擎选择回滚undo log量最小的事务。

死锁示例(注意查询条件列要带有索引,不然会锁全表):


Mysql技术内幕.png

结束语:如果有写的不好或者不太懂得地方可以在下方评论,感觉不错可以点个赞哦。

相关文章

  • MySql InnoDB 锁(lock)

    文章是通过《Mysql技术内幕 InnoDB存储引擎》这本书概括的,主要是锁的这一章,包括共享锁、排它锁、意向锁、...

  • SQL语句加了哪些锁?

    InnoDB的锁 InnoDB 三种行锁: Record Lock(记录锁):锁住某一行记录 Gap Lock(间...

  • MySql InnoDB 锁机制

    MySQL InnoDB支持三种行锁定方式: l 行锁(Record Lock):锁直接加在索引记录上面,锁住...

  • MySQL innodb锁

    MySQL自旋锁-spin lock 一篇算是介绍innodb锁比较有条理的文章 https://blog.csd...

  • mysql 排它锁之行锁、间隙锁、后码锁

    MySQL InnoDB支持三种行锁定 行锁(Record Lock):锁直接加在索引记录上面,锁住的是key。 ...

  • MySQL-Innodb锁

    表锁 InnoDB的表级别锁包含五种锁模式:LOCK_IS、LOCK_IX、LOCK_X、LOCK_S以及LOCK...

  • mysql

    1. 数据库优化 2.mysql锁 2.1 InnoDB行锁类型 共享锁(S-Lock) 允许多个事务对于同一数据...

  • 淘宝MySQL文档整理

    MySQL · 引擎特性 · InnoDB 事务锁系统简介 MySQL · 引擎特性 · Innodb 锁子系统浅...

  • MySQL InnoDB的3种行锁定方式

    首先众所周知,InnoDB 三种行锁: Record Lock(记录锁):锁住某一行记录 Gap Lock(间隙锁...

  • Innodb的锁

    Innodb的锁是行级锁 mysql delete是否会锁表 MySQL的InnoDB存储引擎支持行级锁,Inno...

网友评论

      本文标题:MySql InnoDB 锁(lock)

      本文链接:https://www.haomeiwen.com/subject/bjvjzhtx.html