本文以转账操作为例,实现并测试乐观锁和悲观锁。
死锁问题
当 A, B 两个账户同时向对方转账时,会出现如下情况:
时刻
事务 1 (A 向 B 转账)
事务 2 (B 向 A 转账)
T1
Lock A
Lock B
T2
Lock B (由于事务 2 已经 Lock A,等待)
Lock A (由于事务 1 已经 Lock B,等待)
由于两个事务都在等待对方释放锁,于是死锁产生了,解决方案:按照主键的大小来加锁,总是先锁主键较小或较大的那行数据。
建立数据表并插入数据(MySQL)
create table account
(
id int auto_increment
primary key,
deposit decimal(10, 2) default 0.00 not null,
version int default 0 not null
);
INSERT INTO vault.account (id, deposit, version) VALUES (1, 1000, 0);
INSERT INTO vault.account (id, deposit, version) VALUES (2, 1000, 0);
INSERT INTO vault.account (id, deposit, version) VALUES (3, 1000, 0);
INSERT INTO vault.account (id, deposit, version) VALUES (4, 1000, 0);
INSERT INTO vault.account (id, deposit, version) VALUES (5, 1000, 0);
INSERT INTO vault.account (id, deposit, version) VALUES (6, 1000, 0);
INSERT INTO vault.account (id, deposit, version) VALUES (7, 1000, 0);
INSERT INTO vault.account (id, deposit, version) VALUES (8, 1000, 0);
INSERT INTO vault.account (id, deposit, version) VALUES (9, 1000, 0);
INSERT INTO vault.account (id, deposit, version) VALUES (10, 1000, 0);
Mapper 文件
悲观锁使用 select ... for update,乐观锁使用 version 字段。
/p>
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
select *
from account
where id = #{id}
update account
set deposit=#{deposit},
version = version + 1
where id = #{id}
and version = #{version}
select *
from account
where id = #{id} for
update
update account
set deposit=#{deposit}
where id = #{id}
select sum(deposit) from account;
Mapper 接口
@Component
public interface AccountMapper {
Account selectById(int id);
Account selectByIdForUpdate(int id);
i