Mysql体系——锁

2020-10-06  本文已影响0人  chase_lwf

内容

一 锁种类

根据加锁的范围来说,锁可以分为:全局锁、表级锁、行级锁

1 全局锁(FTWRL)

2 表级锁

3 行级别

一 锁类型

概括性来说,mysql innodb存储引擎有两种锁类型,分别是行级别的行锁和表级别的意向锁;
行锁有如下两种类型:

二 一致性非锁定读

一个事务想要读取一行记录,需要先获取这行数据的行共享锁,如果此时另外一个事务先获取该行的行排他锁,根据行锁的兼容性,后来的事务变不能获取行共享,只能等待行排他锁释放,这无疑是不得行了,Innodb通过MVCC机制实现了写不阻塞读,读取的数据如果有其他事务正在进行更新或是删除操作,则读取该行数据的上一个事务版本号的数据,读取的是一个快照数据,这个数据是通过undo日志来获取的,undo日志也即是innodb mvcc机制实现的根本;

三 一致性锁定读

在默认的可重复读隔离级别,innodb读取数据是一致性非锁定读,读不加锁,写不阻塞读;但是有些业务场景需要我们读取数据时手动上锁以保证数据的一致性,有两种方式手动上锁实现一致性锁定读

四 锁算法

innodb有三种锁算法,分别是:

规则:

创建表t2
CREATE TABLE `t2` (
  `id` int(11) DEFAULT NULL,
  `name` varchar(20) DEFAULT NULL,
  KEY `id` (`id`)
) ENGINE=InnoDB

开启两个事务tx1和tx2,先在tx1事务中查询

mysql> begin;
mysql> select * from t2 where id>5;
+------+------+
| id   | name |
+------+------+
|    6 | dd   |
|    8 | dd   |
|    7 | dd   |
|    9 | rr   |
|   10 | re   |
+------+------+
5 rows in set (0.00 sec)

然后事务2中,插入一行记录,并提交

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

mysql> insert into t2 values(12,"rr");
Query OK, 1 row affected (0.00 sec)

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

结论:
再在事务一查询相同语句,查询不到12这条记录,因为普通的select是快照读,快照读根据MVCC机制不会读到大于当前事务版本的数据,所以MVCC可以解决快照读的幻读问题

2 MVCC不能解决当前读的幻读问题,当前读是读取最新的数据,更新删除等就是当前读
例子2:

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

mysql> select * from t2 where id>5 for update;
+------+------+
| id   | name |
+------+------+
|    6 | dd   |
|    8 | dd   |
|    7 | dd   |
|    9 | rr   |
|   10 | re   |
|   12 | rr   |
|   13 | rr   |
+------+------+
7 rows in set (0.00 sec)
mysql> begin;
Query OK, 0 rows affected (0.01 sec)

mysql> insert into t2 values(14,"rr");

结论:
发现事务2进行锁等待,不能插入,那是因为事务1中for update对加了next-key lock,对区间(5,+∞)进行了加锁,导致这个区间的插入操作都等待锁,所以next-key lock能解决到一些幻读问题

例子3
先在事务1执行下面操作

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

mysql> select * from t2;
+------+------+
| id   | name |
+------+------+
|    1 | dd3  |
|    2 | dd3  |
|    3 | dd   |
|    4 | dd   |
|    6 | dd   |
|    8 | dd   |
|    7 | dd   |
|    9 | rr   |
|   10 | re   |
|   12 | rr   |
|   13 | rr   |
+------+------+
11 rows in set (0.00 sec)

此时事务一种查询到11条数据,事务一不提交,然后开启事务2,在事务2中执行一个插入操作,并commit

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

mysql> insert into t2 values(14, "22");
Query OK, 1 row affected (0.00 sec)

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

然后我们再在事务1中执行更新操作,然后相同的查询

mysql> update t2 set name='aa';
Query OK, 12 rows affected (11.66 sec)
Rows matched: 12  Changed: 12  Warnings: 0

mysql> select * from t2;
+------+------+
| id   | name |
+------+------+
|    1 | aa   |
|    2 | aa   |
|    3 | aa   |
|    4 | aa   |
|    6 | aa   |
|    8 | aa   |
|    7 | aa   |
|    9 | aa   |
|   10 | aa   |
|   12 | aa   |
|   13 | aa   |
|   14 | aa   |
+------+------+
12 rows in set (0.00 sec)

结论:
我们发现在事务一中读取到了事务二插入的数据,在同一个事务中两次读取的数据不一致,发生了幻读,原因在于发生了一次当前读(更新操作),所以此时MVCC没有解决到幻读问题
通过例子1 2 3我们得出最终结论:

引用:
《MYSQL技术内幕 Innodb存储引擎》
《丁奇45讲》

上一篇 下一篇

猜你喜欢

热点阅读