读锁:只能读不允许写
写锁:不能读也不能写,只允许自己写
但是允许其他事务进行普通的select操作
对于普通 SELECT 语句,InnoDB 不会加任何锁
select … lock in share mode
将查找到的数据加上一个S锁,允许其他事务继续获取这些记录的S锁,不能获取这些记录的X锁(会阻塞)
使用场景:读出数据后,其他事务不能修改,但是自己也不一定能修改,因为其他事务也可以使用select … lock in share mode 继续加读锁。
select … for update
将查找到的数据加上一个X锁,不允许其他事务获取这些记录的S锁和X锁。
使用场景:读出数据后,其他事务即不能写,也不能加读锁,那么就导致只有自己可以修改数据
隐式锁:一个事务插入一条记录后,还未提交,这条记录会保存本次事务id,而其他事务如果想来读取这个记录会发现事务id不对应,所以相当于在插入一条记录时,隐式的给这条记录加了一把隐式锁。
1)查询使用是的主键的情况
事务一(session1)
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> select * from t1 where a = 1 for update; +---+------+------+------+------+ | a | b | c | d | e | +---+------+------+------+------+ | 1 | 1 | 1 | 1 | 1 | +---+------+------+------+------+ 1 row in set (0.00 sec)
事务二(session2)
注意是在session1未进行事务commit的情况下
mysql> select * from t1 where a = 1 for update; -- 会阻塞 mysql> select * from t1 where a = 2 for update; -- 不会阻塞
总结:查询使用的是主键时,只需要在主键值对应的那一个条数据加锁即可。
2)查询使用的是唯一索引
事务一(session1)
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> select * from t1 where b = 1 for update; +---+------+------+------+------+ | a | b | c | d | e | +---+------+------+------+------+ | 1 | 1 | 1 | 1 | 1 | +---+------+------+------+------+ 1 row in set (0.00 sec)
事务二(session2)
mysql> select * from t1 where b = 1 for update; -- 会阻塞 mysql> select * from t1 where b = 2 for update; -- 不会阻塞
总结:查询使用的是唯一索引时,只需要对查询值所对应的唯一索引记录项和对应的聚集索引上的项加锁即可。
3)查询使用的是普通索引
事务一(session1)
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> select * from t1 where e = ‘6‘ for update; +----+------+------+------+------+ | a | b | c | d | e | +----+------+------+------+------+ | 6 | 6 | 1 | 4 | 6 | | 12 | 12 | 1 | 1 | 6 | +----+------+------+------+------+ 2 rows in set (0.00 sec)
事务二(session2)
mysql> select * from t1 where a = 6 for update; -- 会阻塞 mysql> select * from t1 where a = 12 for update; -- 会阻塞 mysql> select * from t1 where a = 1 for update; -- 不会阻塞 mysql> select * from t1 where a = 2 for update; -- 不会阻塞 mysql> insert t1(b,c,d,e) values(20,1,1,‘51‘); -- 不会阻塞 mysql> insert t1(b,c,d,e) values(21,1,1,‘61‘); -- 不会阻塞
总结:查询使用的是普通索引时,会对满足条件的索引记录都加上锁,同时对这些索引记录对应的聚集索引上的项也加锁。
4)查询时没有使用到索引
事务一(session1)
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> select * from t1 where c = ‘1‘ for update; +----+------+------+------+------+ | a | b | c | d | e | +----+------+------+------+------+ | 1 | 1 | 1 | 1 | 1 | | 2 | 2 | 1 | 2 | 2 | | 4 | 3 | 1 | 1 | 4 | | 6 | 6 | 1 | 4 | 6 | | 8 | 8 | 1 | 8 | 8 | | 10 | 10 | 1 | 2 | 10 | | 12 | 12 | 1 | 1 | 6 | +----+------+------+------+------+ 7 rows in set (0.00 sec)
事务二(session2)
mysql> select * from t1 where a = 1 for update; -- 会阻塞 mysql> select * from t1 where a = 2 for update; -- 会阻塞 mysql> select * from t1 where a = 3 for update; -- 不会阻塞 mysql> select * from t1 where a = 7 for update; -- 不会阻塞
总结:查询的时候没有走索引,也只会对满足条件的记录加锁。
1)查询使用是的主键的情况
与READ COMMITTED隔离级别一样
2)查询使用的是唯一索引
与READ COMMITTED隔离级别一样
3)查询使用的是普通索引
事务一(session1)
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> select * from t1 where e = ‘6‘ for update; +----+------+------+------+------+ | a | b | c | d | e | +----+------+------+------+------+ | 6 | 6 | 1 | 4 | 6 | | 12 | 12 | 1 | 1 | 6 | +----+------+------+------+------+ 2 rows in set (0.00 sec)
事务二(session2)
mysql> select * from t1 where a = 6 for update; -- 会阻塞 mysql> select * from t1 where a = 12 for update; -- 会阻塞 mysql> select * from t1 where a = 1 for update; -- 不会阻塞 mysql> select * from t1 where a = 2 for update; -- 不会阻塞 mysql> insert t1(b,c,d,e) values(20,1,1,‘51‘); -- 会阻塞 mysql> insert t1(b,c,d,e) values(21,1,1,‘61‘); -- 会阻塞
总结:REPEATABLE READ级别可以解决幻读,解决的方式就是加了GAP锁。
4)查询时没有使用到索引
事务一(session1)
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> select * from t1 where c = ‘1‘ for update; +----+------+------+------+------+ | a | b | c | d | e | +----+------+------+------+------+ | 1 | 1 | 1 | 1 | 1 | | 2 | 2 | 1 | 2 | 2 | | 4 | 3 | 1 | 1 | 4 | | 6 | 6 | 1 | 4 | 6 | | 8 | 8 | 1 | 8 | 8 | | 10 | 10 | 1 | 2 | 10 | | 12 | 12 | 1 | 1 | 6 | +----+------+------+------+------+ 7 rows in set (0.00 sec)
事务二(session2)
mysql> select * from t1 where a = 1 for update; -- 会阻塞 mysql> select * from t1 where a = 2 for update; -- 会阻塞 mysql> select * from t1 where a = 3 for update; -- 不会阻塞 mysql> select * from t1 where a = 7 for update; -- 会阻塞(在READ COMMITTED级别中不会阻塞,跟解决幻读有关系)
总结:查询的时候没有走索引,会对表中所有的记录以及间隙加锁。
1、表级别的S锁、X锁
在对某个表执行SELECT、INSERT、DELETE、UPDATE语句时,InnoDB存储引擎是不会为这个表添加表级别的S锁或者X锁的。
在对某个表执行ALTER TABLE、DROP TABLE这些DDL语句时,其他事务对这个表执行SELECT、INSERT、DELETE、UPDATE的语句会发生阻塞,或者,某个事务对某个表执行SELECT、INSERT、DELETE、UPDATE语句时,其他事务对这个表执行DDL语句也会发生阻塞。这个过程是通过使用的元数据锁(英文名:Metadata Locks,简称MDL)来实现的,并不是使用的表级别的S锁和X锁。
尽量不用这两种方式去加锁,因为InnoDB的优点就是行锁,所以尽量使用行锁,性能更高。
2、IS锁、IX锁
IS、IX锁是表级锁,它们的提出仅仅为了在之后加表级别的S锁和X锁时可以快速判断表中的记录是否被上锁,以避免用遍历的方式来查看表中有没有上锁的记录。
3、AUTO-INC锁
4、系统变量innodb_autoinc_lock_mode:
悲观锁用的就是数据库的行锁,认为数据库会发生并发冲突,直接上来就把数据锁住,其他事务不能修改,直至提交了当前事务。
顾名思义就是不管什么情况都直接给上锁,性能不是很友好
乐观锁其实是一种思想,认为不会锁定的情况下去更新数据,如果发现不对劲,才不更新(回滚)。在数据库中往往添加一个version字段来实现。
往往添加一个额外字段来控制,比如在update中来根据version字段来判断是否修改
事务一(session1)
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> select * from t1 where a =1 for update; -- 1 +---+------+------+------+------+ | a | b | c | d | e | +---+------+------+------+------+ | 1 | 1 | 1 | 1 | 1 | +---+------+------+------+------+ 1 row in set (0.00 sec) mysql> update t1 set c = 2 where a = 4; -- 3,一开始会阻塞 ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
事务二(session2)
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> delete from t1 where a = 4; -- 2 Query OK, 1 row affected (0.00 sec) mysql> delete fromt t1 where a = 1; -- 4,按道理会阻塞,并产生死锁,但是mysql有死锁检查机制让死锁中断 ERROR 1064 (42000): You have an error in your SQL syntax; check the manual thatcorresponds to your MySQL server version for the right syntax to use near ‘t1 where a= 1‘ at line 1 mysql> delete from t1 where a = 1; Query OK, 1 row affected (0.01 sec)
SHOW ENGINE INNODB STATUS; ----看看最近死锁的日志
原文:https://www.cnblogs.com/tongxuping/p/12334677.html