Java程序员必备!MySQL:介于普通读和锁定读的加锁方式

Posted 程序员超时空

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java程序员必备!MySQL:介于普通读和锁定读的加锁方式相关的知识,希望对你有一定的参考价值。

mysql> SELECT * FROM hero;
+--------+------------+---------+
| number | name       | country |
+--------+------------+---------+
|      1 | l刘备      | 蜀      |
|      3 | z诸葛亮    | 蜀      |
|      8 | c曹操      | 魏      |
|     15 | x荀彧      | 魏      |
|     20 | s孙权      | 吴      |
+--------+------------+---------+
5 rows in set (0.01 sec)

现象

在小册答疑群里有一位同学提了一个问题:说是在READ COMMITTED隔离级别下发生了一件百思不得其解的事儿。好的,首先构造环境,将当前会话默认的隔离级别设置成READ COMMITTED

mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
Query OK, 0 rows affected (0.00 sec)

事务T1先执行:

# T1中,隔离级别为READ COMMITTED
mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM hero WHERE country = '魏' FOR UPDATE;
+--------+---------+---------+
| number | name    | country |
+--------+---------+---------+
|      8 | c曹操   | 魏      |
|     15 | x荀彧   | 魏      |
+--------+---------+---------+
2 rows in set (0.01 sec)

country列并不是索引列,所以本条语句执行时肯定是使用扫描聚簇索引的全表扫描方式来执行,EXPLAIN语句也证明了我们的想法:

mysql> EXPLAIN SELECT * FROM hero WHERE country = '魏' FOR UPDATE;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | hero  | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    5 |    20.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.02 sec)

我们之前学过MySQL语句的加锁分析,知道在READ COMMITTED隔离级别下,如果采用全表扫描的方式执行查询语句时,InnoDB存储引擎将依次对每条记录加正经记录锁,在server层测试该记录是否符合WHERE条件,如果不符合则将加在该记录上的锁释放掉。本例中使用FOR UPDATE语句,肯定加的是X型正经记录锁。只有两条记录符合WHERE条件,所以最终其实只对这两条符合条件的记录加了X型正经记录锁(就是number列值为815的两条记录)。当然,我们可以使用SHOW ENGINE INNODB STATUS命令证明我们的分析:

mysql> SHOW ENGINE INNODB STATUS\\G
... 省略了很多内容

------------
TRANSACTIONS
------------
Trx id counter 39764
Purge done for trx's n:o < 39763 undo n:o < 0 state: running but idle
History list length 36
Total number of lock structs in row lock hash table 1
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 281479653009568, not started
0 lock struct(s), heap size 1160, 0 row lock(s)
---TRANSACTION 281479653012832, not started
0 lock struct(s), heap size 1160, 0 row lock(s)
---TRANSACTION 39763, ACTIVE 468 sec
2 lock struct(s), heap size 1160, 2 row lock(s)
MySQL thread id 19, OS thread handle 123145470611456, query id 586 localhost 127.0.0.1 root
TABLE LOCK table `xiaohaizi`.`hero` trx id 39763 lock mode IX
RECORD LOCKS space id 287 page no 3 n bits 72 index PRIMARY of table `xiaohaizi`.`hero` trx id 39763 lock_mode X locks rec but not gap
Record lock, heap no 4 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 80000008; asc     ;;
 1: len 6; hex 000000009b4a; asc      J;;
 2: len 7; hex 80000001d3012a; asc       *;;
 3: len 7; hex 63e69bb9e6938d; asc c      ;;
 4: len 3; hex e9ad8f; asc    ;;

Record lock, heap no 5 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 8000000f; asc     ;;
 1: len 6; hex 000000009b4a; asc      J;;
 2: len 7; hex 80000001d30137; asc       7;;
 3: len 7; hex 78e88d80e5bda7; asc x      ;;
 4: len 3; hex e9ad8f; asc    ;;

 ... 省略了很多内容

其中id39763的事务就是指T1,可以看出它为heap no值为45的两条记录加了X型正经记录锁(lock_mode X locks rec but not gap)。

然后再开启一个隔离级别也为READ COMMITTED的事务T2,在其中执行:

# T2中,隔离级别为READ COMMITTED
mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM hero WHERE country = '吴' FOR UPDATE;
(进入阻塞状态)

很显然,这条语句也会采用全表扫描的方式来执行,会依次去获取每一条聚簇索引记录的锁。不过因为number值为8的记录已经被T1加了X型正经记录锁T2想得却得不到,只能眼巴巴的进行阻塞状态,此时的SHOW ENGINE INNODB STATUS也能证明我们的猜想(只截取了一部分):

---TRANSACTION 39764, ACTIVE 34 sec fetching rows
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1160, 1 row lock(s)
MySQL thread id 20, OS thread handle 123145471168512, query id 590 localhost 127.0.0.1 root Sending data
**Java面试核心知识点笔记**

其中囊括了JVM、锁、并发、Java反射、Spring原理、微服务、Zookeeper、数据库、数据结构等大量知识点。

![蚂蚁金服(Java研发岗),26岁小伙斩获三面,收获Offer定级P6](https://img-blog.csdnimg.cn/img_convert/cca8e7893b62cb259275a0395ee569eb.png)

**Java中高级面试高频考点整理**

![蚂蚁金服(Java研发岗),26岁小伙斩获三面,收获Offer定级P6](https://img-blog.csdnimg.cn/img_convert/f66d5e1f5c7d46e5c16860c28c77dc6b.png)

**更多Java进阶知识笔记文档分享,这些对于面试还是学习来说都是一份不错的学习资料**

**[有需要的朋友可以戳这里即可免费领取](https://gitee.com/vip204888/java-p7)**

![蚂蚁金服(Java研发岗),26岁小伙斩获三面,收获Offer定级P6](https://img-blog.csdnimg.cn/img_convert/503c4c46126c8631938c92a2feb767bc.png)

**最后还分享Java进阶学习及面试必备的视频教学**

![蚂蚁金服(Java研发岗),26岁小伙斩获三面,收获Offer定级P6](https://img-blog.csdnimg.cn/img_convert/a41cc37f0d1025a196b2d8c92e185c45.png)

java-p7)**

[外链图片转存中...(img-1lTEFwB4-1628074506906)]

**最后还分享Java进阶学习及面试必备的视频教学**

[外链图片转存中...(img-C0mG7TUc-1628074506908)]

以上是关于Java程序员必备!MySQL:介于普通读和锁定读的加锁方式的主要内容,如果未能解决你的问题,请参考以下文章

MYSQL的隔离机制实现

volatile

MySQL的RR和RC事务隔离级别加锁类型验证

java代码格式规范,架构师必备!

innodb解决幻读

Mysql 锁定 读情况