Mysql InnoDB的锁定机制实例详解


当前第2页 返回上一页

对于 update,insert,delete 语句会自动加排它锁

1.3通过索引更新数据,也是上排他锁,行锁
SessionA                    SessionB
mysql> set autocommit=0;            mysql> set autocommit=0;
Query OK, 0 rows affected (0.02 sec)      Query OK, 0 rows affected (0.02 sec)
mysql> select * from test;           mysql> select * from test;
--------------------------------------------------------------------------------
+----+-------+-------+-------+         +----+-------+-------+-------+              
| id | name | money | level |         | id | name | money | level |
+----+-------+-------+-------+         +----+-------+-------+-------+
| 1 | tom  |  100 |   1 |         | 1 | tom  |  100 |   1 |
| 2 | jack |  200 |   2 |         | 2 | jack |  200 |   2 |
| 3 | lucas |  300 |   3 |         | 3 | lucas |  300 |   3 |
+----+-------+-------+-------+         +----+-------+-------+-------+
3 rows in set (0.00 sec)            3 rows in set (0.00 sec)
--------------------------------------------------------------------------------
更新id=1的行,就给该行上了排它锁,其他事务
无法更新该行
mysql> update test set level=11 where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
--------------------------------------------------------------------------------
                        事务B则不能更新id=1的行,会发生等待
                        mysql> update test set level=21 where id=1;
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
                        
                        MORE:
                        也不能上排它锁
                        mysql> select *from test where id=1 for update;
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
                        
                        也不能上共享锁
                        mysql> select * from test where level=1 lock in share mode;
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
--------------------------------------------------------------------------------
释放排它锁
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
--------------------------------------------------------------------------------
                        事务A释放锁,事务B就可以加排它锁了
                        mysql> select * from test where id=1 for update;
                        +----+------+-------+-------+
                        | id | name | money | level |
                        +----+------+-------+-------+
                        | 1 | tom |  100 |   11|
                        +----+------+-------+-------+
                        1 row in set (0.00 sec)

2.1脏读

//脏读
//2.1脏读
SessionA                    SessionB
mysql> set autocommit=0;            mysql> set autocommit=0;
Query OK, 0 rows affected (0.02 sec)      Query OK, 0 rows affected (0.02 sec)
set session transaction isolation        set session transaction isolation level read uncommitted;
level read uncommitted;             Query OK, 0 rows affected (0.00 sec)
Query OK, 0 rows affected (0.00 sec)      
mysql> select * from test;           mysql> select * from test;
--------------------------------------------------------------------------------
+----+-------+-------+-------+         +----+-------+-------+-------+              
| id | name | money | level |         | id | name | money | level |
+----+-------+-------+-------+         +----+-------+-------+-------+
| 1 | tom  |  100 |   1 |         | 1 | tom  |  100 |   1 |
| 2 | jack |  200 |   2 |         | 2 | jack |  200 |   2 |
| 3 | lucas |  300 |   3 |         | 3 | lucas |  300 |   3 |
+----+-------+-------+-------+         +----+-------+-------+-------+
3 rows in set (0.00 sec)            3 rows in set (0.00 sec)
--------------------------------------------------------------------------------
                        mysql> update test set level=100 where id=1;
                        Query OK, 1 row affected (0.00 sec)
                        Rows matched: 1 Changed: 1 Warnings: 0
--------------------------------------------------------------------------------
//脏读
mysql> select *from test where id=1;
+----+------+-------+-------+
| id | name | money | level |
+----+------+-------+-------+
| 1 | tom |  100 |  100 |
+----+------+-------+-------+
1 row in set (0.00 sec)
--------------------------------------------------------------------------------
                        rollback;
                        Query OK, 0 rows affected (0.01 sec)
                        
                        mysql> select *from test where id=1;
                        +----+------+-------+-------+
                        | id | name | money | level |
                        +----+------+-------+-------+
                        | 1 | tom |  100 |   1 |
                        +----+------+-------+-------+
                        1 row in set (0.00 sec)

2.2不可重复读

2.2不可重复读
//脏读
SessionA                    SessionB
mysql> set autocommit=0;            mysql> set autocommit=0;
Query OK, 0 rows affected (0.02 sec)      Query OK, 0 rows affected (0.02 sec)
set session transaction isolation        set session transaction isolation level read uncommitted;
level read uncommitted;             Query OK, 0 rows affected (0.00 sec)
Query OK, 0 rows affected (0.00 sec)      
mysql> select * from test;           mysql> select * from test;
--------------------------------------------------------------------------------
+----+-------+-------+-------+         +----+-------+-------+-------+              
| id | name | money | level |         | id | name | money | level |
+----+-------+-------+-------+         +----+-------+-------+-------+
| 1 | tom  |  100 |   1 |         | 1 | tom  |  100 |   1 |
| 2 | jack |  200 |   2 |         | 2 | jack |  200 |   2 |
| 3 | lucas |  300 |   3 |         | 3 | lucas |  300 |   3 |
+----+-------+-------+-------+         +----+-------+-------+-------+
3 rows in set (0.00 sec)            3 rows in set (0.00 sec)
--------------------------------------------------------------------------------
                        mysql> update test set level=100 where id=1;
                        Query OK, 1 row affected (0.00 sec)
                        Rows matched: 1 Changed: 1 Warnings: 0
--------------------------------------------------------------------------------
mysql> select *from test where id=1;
+----+------+-------+-------+
| id | name | money | level |
+----+------+-------+-------+
| 1 | tom |  100 |  100 |
+----+------+-------+-------+
1 row in set (0.00 sec)
--------------------------------------------------------------------------------
                        mysql> update test set level=1000 where id=1;
                        Query OK, 1 row affected (0.00 sec)
                        Rows matched: 1 Changed: 1 Warnings: 0
--------------------------------------------------------------------------------
//不可重复读
//读三次,第一次是level是1,第二次是100,第三次是1000
mysql> select *from test where id=1;
+----+------+-------+-------+
| id | name | money | level |
+----+------+-------+-------+
| 1 | tom |  100 |  1000|
+----+------+-------+-------+
1 row in set (0.00 sec)

2.3幻读

//2.3幻读
SessionA                    SessionB
mysql> set autocommit=0;            mysql> set autocommit=0;
Query OK, 0 rows affected (0.02 sec)      Query OK, 0 rows affected (0.02 sec)
set session transaction isolation        set session transaction isolation level read uncommitted;
level read uncommitted;             Query OK, 0 rows affected (0.00 sec)
Query OK, 0 rows affected (0.00 sec)      
mysql> select * from test;           mysql> select * from test;
--------------------------------------------------------------------------------
+----+-------+-------+-------+         +----+-------+-------+-------+              
| id | name | money | level |         | id | name | money | level |
+----+-------+-------+-------+         +----+-------+-------+-------+
| 1 | tom  |  100 |   1 |         | 1 | tom  |  100 |   1 |
| 2 | jack |  200 |   2 |         | 2 | jack |  200 |   2 |
| 3 | lucas |  300 |   3 |         | 3 | lucas |  300 |   3 |
+----+-------+-------+-------+         +----+-------+-------+-------+
3 rows in set (0.00 sec)            3 rows in set (0.00 sec)
--------------------------------------------------------------------------------
                        mysql> update test set level=100 where id=1;
                        Query OK, 1 row affected (0.00 sec)
                        Rows matched: 1 Changed: 1 Warnings: 0
--------------------------------------------------------------------------------
mysql> select *from test where id=1;
+----+------+-------+-------+
| id | name | money | level |
+----+------+-------+-------+
| 1 | tom |  100 |  100 |
+----+------+-------+-------+
1 row in set (0.00 sec)
--------------------------------------------------------------------------------
                        mysql> insert into test (name, money,level) VALUES                             ('tim',250,4);
                        Query OK, 1 row affected (0.01 sec)
--------------------------------------------------------------------------------
//幻读
//读两次,第二次多了tim的数据
//如果是rr级别,需要使用当前读select * from test lock in share mode;否则因为MVCC的缘故,是读不到tim的
mysql> select * from test;
+----+-------+-------+-------+
| id | name | money | level |
+----+-------+-------+-------+
| 1 | tom  |  100 |   1 |
| 2 | jack |  200 |   2 |
| 3 | lucas |  300 |   3 |
| 4 | tim  |  250 |   4 |
+----+-------+-------+-------+
4 row in set (0.00 sec)

3 间隙锁(Net-Key锁)

MVCC使RR级别下,事务当前读,来避免了读情况下的幻读问题,但如果写更新时候呢?在范围更新的同时,往范围内插入新数据,怎么办?

于是就有了间隙锁,在更新某个区间数据时,将会锁定这个区间的所有记录。例如update XXX where id between 1 and 100, 就会锁住id从1到100之间的所有的记录。值得注意的是,在这个区间中假设某条记录并不存在,该条记录也会被锁住,这时,如果另一个事务往这个区间添加数据,就必须等待上一个事务释放锁资源。

使用间隙锁有两个目的,一是防止幻读;二是满足其恢复和赋值的需求。

3.1范围间隙锁,显式左开右闭区间

//间隙锁(Net-Key锁) 范围间隙锁,左开右闭区间
SessionA                    SessionB
mysql> set autocommit=0;            mysql> set autocommit=0;
Query OK, 0 rows affected (0.02 sec)      Query OK, 0 rows affected (0.02 sec)
     
mysql> select * from test;           mysql> select * from test;
--------------------------------------------------------------------------------
+----+-------+-------+-------+         +----+-------+-------+-------+              
| id | name | money | level |         | id | name | money | level |
+----+-------+-------+-------+         +----+-------+-------+-------+
| 1 | tom  |  100 |   1 |         | 1 | tom  |  100 |   1 |
| 2 | jack |  200 |   2 |         | 2 | jack |  200 |   2 |
| 3 | lucas |  300 |   3 |         | 3 | lucas |  300 |   3 |
+----+-------+-------+-------+         +----+-------+-------+-------+
3 rows in set (0.00 sec)            3 rows in set (0.00 sec)
--------------------------------------------------------------------------------
mysql> update test set level=0
where money between 0 and 200;
Query OK, 2 rows affected (0.02 sec)
Rows matched: 2 Changed: 2 Warnings: 0
理论上应该锁定[0,300)这个区间
--------------------------------------------------------------------------------
                        插入money=0等待
                        mysql> insert into test (name, money,level) VALUES ('tim',0,0);
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
                        
                        插入money=90等待
                        mysql> insert into test (name, money,level) VALUES ('tim',90,0);
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
                        
                        插入money=100等待
                        mysql> insert into test (name, money,level) VALUES ('tim',100,0);
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
                        
                        插入money=299等待
                        mysql> insert into test (name, money,level) VALUES ('tim',299,0);
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
                        
                        插入money=300 ok
                        mysql> insert into test (name, money,level) VALUES ('tim',300,0);
                        Query OK, 1 row affected (0.00 sec)

3.2单个间隙锁 隐式区间

上小节是指定update某个区间,那如果说是只update一个值呢?还会有间隙锁么?

//间隙锁(Net-Key锁) 单个间隙锁,左开右闭区间
SessionA                    SessionB
mysql> set autocommit=0;            mysql> set autocommit=0;
Query OK, 0 rows affected (0.02 sec)      Query OK, 0 rows affected (0.02 sec)
     
mysql> select * from test;           mysql> select * from test;
--------------------------------------------------------------------------------
+----+-------+-------+-------+         +----+-------+-------+-------+              
| id | name | money | level |         | id | name | money | level |
+----+-------+-------+-------+         +----+-------+-------+-------+
| 1 | tom  |  100 |   1 |         | 1 | tom  |  100 |   1 |
| 2 | jack |  200 |   2 |         | 2 | jack |  200 |   2 |
| 3 | lucas |  300 |   3 |         | 3 | lucas |  300 |   3 |
+----+-------+-------+-------+         +----+-------+-------+-------+
3 rows in set (0.00 sec)            3 rows in set (0.00 sec)
--------------------------------------------------------------------------------
mysql> update test set level=0
where money = 200;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
理论上应该锁定[0,300)这个区间
--------------------------------------------------------------------------------
                        插入money=0 ok
                        mysql> insert into test (name, money,level) VALUES ('tim',0,0);
                        Query OK, 1 row affected (0.00 sec)
                        
                        插入money=90 ok
                        mysql> insert into test (name, money,level) VALUES ('tim',90,0);
                        Query OK, 1 row affected (0.00 sec)
                        
                        插入money=100等待
                        mysql> insert into test (name, money,level) VALUES ('tim',100,0);
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
                        
                        插入money=150等待
                        mysql> insert into test (name, money,level) VALUES ('tim',150,0);
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
                        
                        插入money=200等待
                        mysql> insert into test (name, money,level) VALUES ('tim',200,0);
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
                        
                        插入money=240等待
                        mysql> insert into test (name, money,level) VALUES ('tim',240,0);
                        ERROR 1205 (HY000): Lock wait timeout exceeded;
                        try restarting transaction
                        
                        插入money=300 ok
                        mysql> insert into test (name, money,level) VALUES ('tim',300,0);
                        Query OK, 1 row affected (0.00 sec)

当不指定区间时,隐式的区间为索引B+数前后两个节点的值所确定的区间,也是左开右闭,对于上述例子,就是[0,300)这个区间。

总结

到此这篇关于Mysql InnoDB锁定机制的文章就介绍到这了,更多相关Mysql InnoDB锁定机制内容请搜索

更多相关Mysql内容来自木庄网络博客


标签:Mysql

返回前面的内容

相关阅读 >>

mysql中锁的必要性及分类介绍

navicat远程连接mysql连不上

mysql数据库导出数据字典文档word或者html的3个工具介绍

mysql中关于char和varchar的区别

如何使用mysql完成excel中的数据生成

mysql死锁与日志分析

mysql服务器中主从配置介绍

mysql如何连接数据库

mysql如何添加主键?

mysql循环语句之while循环测试

更多相关阅读请进入《mysql》频道 >>


数据库系统概念 第6版
书籍

数据库系统概念 第6版

机械工业出版社

本书主要讲述了数据模型、基于对象的数据库和XML、数据存储和查询、事务管理、体系结构等方面的内容。



打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...