案例
A用户的余额为 1000
元。现在,B给A转账新增 100
元,同时,A给C转账扣除 50
元。
一波操作下来,正常A用户最终余额应为 1000+100-50=1050
元。而实际有可能变为 1000-50=950
元。
这就是典型的高并发条件下出现的脏数据
问题。因为数据操作可能出现丢失更新
。
解决方案:
乐观锁
方案: 在提交阶段,先判断下原数据是否修改过。(读取数据时,同时获取版本号。数据有更新时,版本号加1。提交数据更新前,读取版本号,判断是否与之前一致,是则执行更新,并更新该数据的版本号。)
异步队列
方案:高并发的数据处理操作,全部堆栈到数据操作任务队列中,异步执行。如:电商秒杀使用的Redis异步队列
并发下事务会产生的问题
脏读
: 事务A读到了事务B还没有提交的数据不可重复读
: 在一个事务里面读取了两次某个数据,读出来的数据不一致。幻读
: 在一个事务里面的操作中发现了未被操作的数据丢失更新
:两个不同的事务在某一时刻对同一数据进行读取后,先后进行修改。导致第一次操作数据丢失。
丢失更新产生的原因(两种)
第一类丢失更新(回滚丢失,Lost update) (通过设置隔离级别可以防止 Repeatable Read)。
A事务撤销时,把已经提交的B事务的更新数据覆盖了。这种错误可能造成很严重的问题。
SQL92没有定义这种现象,标准定义的所有隔离界别都不允许第一类丢失更新发生
。
第二类丢失更新(覆盖丢失/两次更新问题,Second lost update) A事务覆盖B事务已经提交的数据,造成B事务所做操作丢失。(实际上和不可重复读是同一种问题)
。
事务隔离级别
事务隔离级别越高,在并发下会产生的问题就越少,但同时付出的性能消耗也将越大。
1. Read UnCommitted(读未提交)
最低的隔离级别。一个事务可以读取另一个事务并未提交的更新结果。
2. Read Committed(读提交)
大部分数据库采用的默认隔离级别。一个事务的更新操作结果只有在该事务提交之后,另一个事务才可以的读取到同一笔数据更新后的结果。
3. Repeatable Read(重复读)
mysql的默认级别。
整个事务过程中,对同一笔数据的读取结果是相同的,不管其他事务是否在对共享数据进行更新,也不管更新提交与否。
4. Serializable(序列化)
最高隔离级别。所有事务操作依次顺序执行。注意这会导致并发度下降,性能最差。通常会用其他并发级别加上相应的并发锁机制来取代它。
隔离级别 | 脏读 | 不可重复读 | 幻读 | 加锁读 |
---|---|---|---|---|
READ_UNCOMMITTED | 是 | 是 | 是 | 否 |
READ_COMMITED | 否 | 是 | 是 | 否 |
REPEATABLE_READ | 否 | 否 | 是 | 否 |
SERLALIZABLE | 否 | 否 | 否 | 是 |
事物隔离级别查看及修改(MySQL)
查看默认事务隔离级别, 修改当前会话事务隔离级别, 修改全局事务隔离级别
select @@tx_isolation;
SET session TRANSACTION ISOLATION LEVEL Serializable;
SET global TRANSACTION ISOLATION LEVEL Serializable;
参数可以为:Read uncommitted|Read committed|Repeatable read|Serializable
总结
大多数数据库的默认的事务隔离级别是提交读(Read committed),而MySQL的事务隔离级别是重复读(Repeatable read)。对于丢失更新,只有在序列化(Serializable)级别才可得到彻底解决。不过对于高性能系统而言,使用序列化级别的事务隔离,可能引起死锁或者性能的急剧下降。
就具体开发过程而言,一般分为 悲观锁
和 乐观锁
两种方式来解决并发冲突问题。
乐观锁(Optimistic Lock)
, 很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
悲观锁(Pessimistic Lock)
, 很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
并发事务导致的丢失更新及处理方式详解 https://blog.csdn.net/qq_39445473/article/details/90488673
事务及事务隔离级别 https://www.cnblogs.com/xrq730/p/5087378.html
并发数据库事务缺锁导致的数据不一致情况:丢失更新,脏读,不可重复读,幻读 https://www.cnblogs.com/yfdream/p/7842917.html
php并发mysql数据库丢失更新处理 https://blog.csdn.net/weixin_30838971/article/details/113473666