mysql共享锁vs排他锁

读操作 - 共享锁(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 = '1234df4446cb4cb6bc2f639830b12345';
commit; 新增一条张三记录
commit; 更新记录

你可能感兴趣的:(mysql共享锁vs排他锁)