读锁与写锁区别
语法
// 读锁
select ... lock in share mode
// 写锁
select ... for update
共同点
- 都是作用在 select 语句中
- A 事务对某 select 语句加锁之后(未提交事务),其他事务无法对该数据执行修改操作(update/delete)
- 只有当 A 事务提交或回滚,其他事务才能对数据进行修改操作(update/delete)
- 不管读锁还是写锁,它们都属于悲观锁的一种形式
不同点
- 读锁:当 A 事务对 select 语句加上读锁时,其他事务对 select 加上读锁获取数据不会阻塞
- 写锁:当 A 事务对 select 语句加上写锁时,其他事务对 select 加上读锁/写锁获取数据阻塞,只有等待 A 事务提交或回滚,其他事务才能读取到数据
读锁写锁示例
假设有张 user 表,表中数据如下
读锁(共享锁)
------------------A事务------------------ | ------------------B事务------------------ |
---|---|
对 id = 1 的数据增加读锁,但是并不提交事务 |
|
不加锁的查询,查询成功 |
|
加上读锁查询,查询成功 |
|
对 id = 2 的数据进行修改,修改成功 |
|
对 id = 1 的数据进行修改,修改出现阻塞 |
|
事务提交 |
由于 A事务 commit 了,阻塞状态取消,修改成功 |
写锁(排他锁)
------------------A事务------------------ | ------------------B事务------------------ |
---|---|
对 id = 1 的数据增加写锁,但是并不提交事务 |
|
不加锁的查询,查询成功 |
|
加上写锁查询 id = 2 的数据,查询成功 |
|
加上写锁查询 id = 1 的数据,查询出现阻塞 |
|
事务提交 |
由于 A事务 commit 了,阻塞状态取消,查询成功 |
对 id = 1 的数据增加写锁,但是并不提交事务 |
|
修改 id = 2 的数据,修改成功 |
|
修改 id = 1 的数据,修改出现阻塞 |
|
事务提交 |
由于 A事务 commit 了,阻塞状态取消,修改成功 |
注意事项
注意注意注意:for update 仅适用于InnoDB,并且必须开启事务,在begin与commit之间才生效。或者在代码中使用@Transactional才能生效。
行锁:访问数据库的时候,锁定整个行数据,防止并发错误。 如InnoDB存储引擎使用行锁
表锁:访问数据库的时候,锁定整个表数据,防止并发错误。 如MyISAM存储引擎使用表锁
例1:明确指定主键,并且数据真实存在,行锁
select status from t_goods where id = 1 for update;
例2:明确指定主键,但数据不存在,无锁
select status from t_goods where id = 0 for update;
例3:主键不明确,表锁
select status from t_goods where id <= 3 for update;
例4:无主键,表锁
select status from t_goods for update;