读操作 - 共享锁(InnoDB悲观锁)
因为mysql是自动提交,但是我们要实验不让自动提交使用命令set autocommit=0;
共享锁结论:
多个会话共享同一把锁。
共享锁/会话session | 读操作 | 写操作 | 锁操作 |
---|---|---|---|
当前session | 可以 | 可以 | 可以 |
其他session | 可以 | 阻塞等待操作session的commit(超时报错) | 可以 |
加锁操作:多个会话都可以加锁。
读操作:多个会话共享同一把锁,每个会话都可以去读数据。
写操作:但是对于写数据,如果一个会话写数据了,那么其他会话都要阻塞等待。
- 如果当前session修改了值,但是还没有commit提交,那么其他session无法修改表中的所有记录,只能等待超时。
- 其他session可以select查询数据,但是查询的结果是旧数据。
语句:
select XXXXXXX lock in share mode;
实验:
session1 | session2 |
---|---|
set autocommit =0; | |
select * from edu_user lock in share mode; | |
select * from edu_user; | |
update edu_user set password="456789" where username = "王五"; | |
update edu_user set password="012345" where username = "王五"; | |
等待:1205 - Lock wait timeout exceeded; try restarting transaction | |
commit; |
结果:
mysql> select password from edu_user where username="王五";
+----------+
| password |
+----------+
| 456789 |
+----------+
1 row in set (0.29 sec)
说明:
- 当前session1获得共享锁,session2也可以继续添加共享锁查询记录。
- 但是session2想要写记录,将会处于阻塞状态。超过了指定时间(50s)将报错。虽然session1写记录,但是必须要提交事务,session2才可见。
- 所以select * from t lock in share mode;一般配合事务commit命令使用。
- 查询共享锁等待时间
mysql> show variables like '%innodb_lock_wait_timeout%';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 50 |
+--------------------------+-------+
1 row in set (0.00 sec)
读操作 - 排他锁(InnoDB悲观锁 )
排他锁结论:
当前事务加了排他锁之后,其他事务什么锁都不能加。
排他锁/会话session | 读操作 | 写操作 | 锁操作 |
---|---|---|---|
当前session | 可以 | 可以 | 可以 |
其他session | 可以 | 阻塞等待(超时报错) | 阻塞等待(超时报错) |
加锁操作:如果多个session的其中一个session加锁了,那么其他session只能阻塞。
读操作:多个session可以读。
写操作:如果一个session的其中一个session加锁了,那么其他session写操作阻塞。
语句:
select XXXXXXX for update;
# 自动:delete / update / insert 默认加上排他锁
# 手动:select * from table for update
实验:
session1 | session2 |
---|---|
set autocommit =0; | |
select * from edu_user for update; | set autocommit =0; |
成功:select * from edu_user; | |
阻塞:select * from edu_user for update; | |
update edu_user set password="456789" where username = "王五"; | |
update edu_user set password = "13579" where username="王五"; | |
等待:1205 - Lock wait timeout exceeded; try restarting transaction | |
commit; |
说明:
当前事务添加了排他锁后,其他事务想要添加锁都将被阻塞。
写操作 - 排他锁(InnoDB悲观锁 )
delete / update / insert 默认会自动加上排他锁。
update操作:
获取需要更新的一条记录的位置,使用排他锁锁定该记录。
session1 | session2 |
---|---|
set autocommit =0; | |
update edu_user set username = "李保国" where id = '5045dfba5f5b4cb5b805c379fc123456'; | set autocommit =0; |
阻塞:update edu_user set username = "李大嘴" where id = '5045dfba5f5b4cb5b805c379fc123456'; | |
commit; 字段改变为李保国 | |
执行:update edu_user set username = "李大嘴" where id = '5045dfba5f5b4cb5b805c379fc123456'; | |
commit; 字段改变为李大嘴 |
注意:
当一个session在update操作过程中,其他session只能读操作,只能读到原来数据,写操作,会阻塞。
delete操作:
获取删除记录的位置,然后使用排他锁锁定该记录。
session1 | session2 |
---|---|
set autocommit =0; | |
delete from edu_user where id = '5045dfba5f5b4cb5b805c379fc123456'; | set autocommit =0; |
阻塞:delete from edu_user where id = '5045dfba5f5b4cb5b805c379fc123456'; | |
commit; 指定记录删除 | |
执行:delete from edu_user where id = '5045dfba5f5b4cb5b805c379fc123456'; | |
commit; |
insert操作:
不需要加锁,通过隐式锁来保护事务全过程。
insert操作检查:
- 情况1:如果记录之间存在有间隙锁,那么为了避免幻读情况,是不能插入记录。
- 情况2:如果插入记录主键冲突,也不能插入记录。
- 其他情况:插入隐式锁。
session1 | session2 |
---|---|
set autocommit =0; | |
insert into edu_user(id, username, password, role_id) values('1234df4446cb4cb6bc2f639830b12345','张三','123456',3); | set autocommit =0; |
非阻塞:update edu_user set username= '孙中山' where id = '5045dfba5f5b4cb5b805c379fc538bcb'; 老记录 | |
阻塞:update edu_user set username= '孙中山' where id = '5045dfba5f5b4cb5b805c379fc538bcb'; | |
commit; 新增一条张三记录 | |
commit; 更新记录 |
网友评论