update account set money=money+2000 where id=123;在事务2中执行下面这条sql:
update account set money=money+3000 where id=123;这两条sql执行成功之后,你的money可能是:3000、4000、6000,这三种情况中的一种。你之前的想法是,用户A和用户B总共给你转账5000,最终你账户的钱应该是6000才对,3000和4000是怎么来的?
假如user表现在有这样的数据库,数据库的版本是:8.0.21,数据库的隔离级别是:REPEATABLE-READ。
创建的索引如下:
其中id是主键字段,code是唯一索引字段,name是普通索引字段,其他的都是普通字段。
begin; select * from user where id=1 for update; update user set age=22 where id=1;where条件中的id是数据库的主键,并且使用for update关键字,加了一个行锁,这个事务没有commit。
begin; update user set age=23 where id=1; commit;
在执行事务2的sql语句的过程中,会一直等待事务1释放锁。
如果事务1一直都不释放行锁,事务2最后会报下面这个异常:
begin; update user set age=23 where id=2; commit;
执行结果如下:
由于事务3中更新的另外一行数据,因此可以执行成功。说明使用for update关键字,锁住了主键id=1的那一行数据,对其他行的数据并没有影响。
# 堆代码 duidaima.com begin; select * from user where code='101' for update; update user set age=22 where code='101';where条件中的code是数据库的唯一索引,并且使用for update关键字,加了一个行锁,这个事务没有commit。
begin; update user set age=23 where code='101'; commit;执行结果跟主键的情况是一样的。
begin; select * from user where name='周星驰' for update; update user set age=22 where name='周星驰';where条件中的name是数据库的普通索引,并且使用for update关键字,加了一个行锁,这个事务没有commit。此时,开启了另外一个事务2,也更新name=周星驰的用户的年龄:
begin; update user set age=23 where name='周星驰'; commit;执行结果跟主键的情况也是一样的。
begin; select * from user where id in (1,2) for update; update user set age=22 where id in (1,2);where条件中的id是数据库的主键范围,并且使用for update关键字,加了多个行锁,这个事务没有commit。
begin; update user set age=23 where id=1; commit;执行结果跟主键的情况也是一样的。
begin; update user set age=23 where id=2; commit;执行结果跟主键的情况也是一样的。
begin; select * from user where age=22 for update; update user set age=22 where age=22 ;where条件中的age是数据库的普通字段,并且使用for update关键字,加的是表锁,这个事务没有commit。
begin; update user set age=23 where age=22 ; commit;此时,执行事务2时,会一直阻塞等待事务1释放锁。调整一下sql条件,查询条件改成age=23:
begin; update user set age=23 where age=23 ; commit;此时,行事务3时,也会一直阻塞等待事务1释放锁。也就是说,在for update语句中,使用普通字段作为查询条件时,加的是表锁,而并非行锁。
begin; select * from user where id=66 for update; update user set age=22 where id=66 ;这条数据是不存在的。此时,开启了另外一个事务2,也更新id=66的用户的年龄:
begin; update user set age=23 where id=66 ; commit;
执行结果:
执行成功了,说明这种情况没有加锁。
查询空数据:不加锁。