写了一个接口。
update order_a u set u.product_price=?2 where u.id=?1
开启 50 个线程调接口修改 product_price,id 为主键。
用乐观锁 数据正常。
使用事务,当隔离级别为 SERIALIZABLE 时, 会成功部分,其他线程会报死锁。 使用事务,当隔离级别为 REPEATABLE_READ,READ_COMMITTED,READ_UNCOMMITTED,都会全部成功,只是数据不正确。
使用事务不是会加锁吗? 为什么会数据不正常?大佬们求指点
1
xxxyh 2021-01-15 15:55:01 +08:00
建议把接口代码放上来
|
2
bbao 2021-01-15 15:57:26 +08:00 1
mysql 默认自动提交。
使用事务,需要提前开始事务。 start transaction; select xx from table where condition = ? for update ; (悲观锁) 其他 session 查询相同 condition 时,会被锁住;直到超时或者对方 commit; 乐观锁: select xx from table where condition = ? and u.id = ? and 待修改字段 = 原始值; 如果待修改字段 != 原始值,表示已经有人修改了数据,你就直接业务返回就可以了。 |
3
lllllliu 2021-01-15 15:58:13 +08:00
你这个 update 只针对一条数据的话没必要加索吧。更新失败就失败。
也可以先锁住这一条,select for update 。这样其他线程操作同一个 id 的时候会等待。 |
4
cheng6563 2021-01-15 16:01:38 +08:00
单条 SQL 开不开没区别的,开事务其实只是关掉自动提交。
|
5
bruce0 2021-01-15 16:10:19 +08:00
|
6
kikione OP @xxxyh
@Transactional(isolation = Isolation.REPEATABLE_READ) @Override public String changePrice(Integer num,String id) { OrderA one = orderAReposity.getOne("1"); BigDecimal price = one.getProductPrice().add(BigDecimal.valueOf(num)); int i = orderAReposity.changePrice1("1", price); return i+""; } 接口代码 |
8
xxxyh 2021-01-15 16:18:17 +08:00
@kikione 接口代码有问题,前面 select 的时候没加锁,后面 update 的时候结果错误是正常的,如果不想加锁的话,可以把 sql 写成 set product_price = product_price + num
|
9
notejava 2021-01-15 16:19:29 +08:00
事务 != 加锁
事务只能保证事务内的语句要么全部执行成功,要么全部失败。 加锁是同一时刻,只允许一个线程修改数据。 |
11
keepeye 2021-01-15 16:24:01 +08:00
据我所知:除了 SERIALIZABLE,其他级别可能发生脏读或幻读现象。SERIALIZABLE 级别在事务中有 select 的时候可能会造成 update 死锁
|
12
keepeye 2021-01-15 16:24:59 +08:00
你要串行化,那就 select for update 吧,但要小心性能问题
|
13
cmai 2021-01-15 16:39:21 +08:00
RR 读在不加 S/X 锁的情况下是快照读,多个线程可能同时读到了同一版本的数据,然后做更新
|
14
kikione OP |
16
kikione OP |
17
cheng6563 2021-01-15 17:35:05 +08:00 1
@kikione
常见几种情况我列下吧。 1. 事务内单条 update update 有行锁,提交之后下一个 update 才能拿到锁继续操作,如果是 update xx set version=version+1 这样更新是没问题的。 2.事务内先 select,然后根据 select 的结果再 update 。 比如 select version from xx 把 version 放程序变量里,然后在程序里进行 version++,再 update xx set version=?。 这种情况 select 是不加锁的,多个线程会一起拿到一个相同的 version,后续的 update 可能都是设置了相同的值。 3.事务内先 select for update,然后根据 select 的结果再 update 。 select 加了 for update 后也会加行锁,在你这个事务提交前其他线程的 select for update 也会卡住,直到事务提交后才能 select for update,数据也没问题了。 |
18
zifangsky 2021-01-15 18:15:26 +08:00
你的问题不在于数据库中怎么加锁,而在于你需要给你的整个业务方法加锁。
|