比如账户余额有100块钱,交易时检测如果 余额大于交易额,就交易成功。
现在假设这个用户同一时刻,产生了多比交易,检测的时候就会出现问题。
问题:
请问这个时候用事务的SERIERLIZED 隔离级别是否可以解决此问题。
或者给这行数据加锁,那请问这个锁该怎么加?
比如账户余额有100块钱,交易时检测如果 余额大于交易额,就交易成功。
现在假设这个用户同一时刻,产生了多比交易,检测的时候就会出现问题。
问题:
请问这个时候用事务的SERIERLIZED 隔离级别是否可以解决此问题。
或者给这行数据加锁,那请问这个锁该怎么加?
用乐观锁解决此问题,就像楼上说的,如果这样操作的话,容易出现过多的乐观锁异常,需要自己手动进行对乐观锁异常进行补偿,来更新数据。
这样的问题最好还是在业务逻辑层次来进行处理,例如:如果是批量操作的,可以对账户进行汇总更新,或者通过同步队列来进行处理,但是这样对性能方面都会有所影响,主要还是得具体的业务场景和系统要求
理论上是可以解决问题的。但是如果从数据库层面修改事务隔离级别为SERIERLIZED对所有依赖这个数据库的系统是一场灾难。所有的数据库事务都变成了串行,系统基本上就趴窝了。
编码层面使用乐观锁可以提高性能,但是乐观锁的缺点是不适用与写操作较多的情况,因为频繁的发现无法更新数据,应用系统会不停的retry,导致应用系统压力越来越大,占用cpu资源,影响应用系统。
要看当时的业务场景具体分析,需要在整体架构上,思考如何解决问题。
典型的乐观锁场景,通过在写入时判断条件匹配情况,而不是在一开始就进行同步阻塞。
sql 演示:
update order set price=price-100 where id = 1 and price >= 100
对于有 ABA 问题的,也可以通过添加 version 来解决。
SERIERLIZED 采用的是悲观锁,性能会有损失。
15 回答8.4k 阅读
8 回答6.2k 阅读
5 回答3.2k 阅读✓ 已解决
3 回答3.6k 阅读✓ 已解决
1 回答4k 阅读✓ 已解决
3 回答1.8k 阅读✓ 已解决
3 回答6k 阅读
隔离级别有四种,可串行化,可重复读,已提交读,未提交读。
一般数据库都是默认运行在已提交读的情况上,但是innodb是运行在可重复读的隔离级别上。
问题1:请问这个时候用事务的SERIERLIZED 隔离级别是否可以解决此问题。
肯定可以。这个是最高的隔离级别,等价于所有事务是串行执行,因此不会产生并发的问题。
简单重设下事务:
比如账户余额有100块钱,交易时检测如果 余额大于交易额,就交易成功。现在假设这个用户同一时刻,产生了多比交易,检测的时候就会出现问题。
事务可以用伪代码表示:
假设两个上面的事务并发执行,那么肯定是会出现问题:
由于read A通常是使用类似select balance from tbl,这种属于不加锁的读,也就是说这种情况下不会加任何的锁。确保重复读只是在第一次read的时候创建快照。也就是说,T1的read A,第一次读到的是100元,而T2的read A也是读到了100元。
这个时候,业务逻辑if(A>B),假设T1的B为99元,T2的B为98元,则都为真,因此虽然在write(A-B)先执行的事务中会加上写锁,但是最后的结果是这两笔交易都能成功进行,因此会造成交易异常。
那么如果增加写锁呢?
通过这个时候会使用select...for update语句。当事务执行该语句的时候,会获得所需的锁。
不管哪个事务先执行,select..for update都会对A加上写锁,因此两个事务是不会冲突的。
但是,这种情况下(属于悲观锁),对于短事务是一种可行的方案。但是对于长事务来说,性能开销太大了。因此当应用场景是在长事务下,可以使用乐观锁来实现。
问题2:或者给这行数据加锁,那请问这个锁该怎么加?
可以这么加:select *from tbl where id=xxx for update;