MySQL并发导致的脏数据分析
来源:互联网 发布:服务器提供商 知乎 编辑:程序博客网 时间:2024/05/17 23:50
记录一下一个并发导致的脏数据问题(基于MySQL)。问题描述(银行操作员例子):比如A、B操作员同时读取一余额为1000元的账户,A操作员为该账户增加100元,B操作员同时为该账户扣除50元,A先提交,B后提交。最后实际账户余额为1000-50=950元,但本该为1000+100-50 = 1050 。
首先分析下如何会导致问题的出现:
我们来些一个伪代码表示下A操作员操作执行的过程:User user = userDao.getById(1); ----------(步骤1)user.setAccount(user.getAccount()+100); ----------(步骤2)userDao.update(user); ------------------(步骤3)而B操作员执行的过程为:User user = userDao.getById(1); ----------(步骤1)user.setAccount(user.getAccount()-50); ----------(步骤2)userDao.update(user); ------------------(步骤3)
当A操作的程序执行到步骤1之后,步骤3之前的时候B执行了步骤1 。之后A执行了步骤3,B再执行步骤2跟3 。此时问题就出现了。这就是典型的因为并发而导致脏数据的问题。有人说给代码加事务啊,在方法上加入@Transational就不会出现这问题了嘛。在这我想说这样做是没用的。当然,加事务也是可以解决这个问题的,但是你需要设置事务的隔离级别为SERIALIZABLE(可串行化)才能做到不会有幻读的出现。但是这样做在并发的情况下效率会特别低下(每个对该数据行的请求,包括读跟写的请求都需要等待前面的读跟写全部执行完才能执行)。然而对于MySQL的默认事务隔离级别是REPEATABLE(可重复读),在这个隔离级别下,即使A执行步骤1后给数据行加上了读锁,但是B执行步骤1时也可以对数据行进行读取,此时就出现了幻读的问题。所以单纯的在方法上加入@Transational注解是解决不了问题的。
相关知识点:
事务的四个特性:原子性,一致性,隔离性,持久性
- 原子性:包含在事务内的所有操作,要么全部执行完成,要么全部执行失败
- 一致性:包含在事务内的所有操作设计的数据行,能被查看到的要么全部执行完成后的结果,要么全部完成前的结果。也就是说小明有100块,小花有10块,小明给小花转了50块。那么对于其他事务来说,能看到的只有是小明有50块同时小花有60块。或者是小明有100块同时小花有10块。而不能出现小明有50块而小花有10块。这就叫做一致性
- 持久性:事务执行完成后数据被持久化到磁盘。
- 隔离性:隔离性有四大隔离级别:
① Serializable (串行化):可避免脏读、不可重复读、幻读的发生。
② Repeatable read (可重复读):可避免脏读、不可重复读的发生。
③ Read committed (读已提交):可避免脏读的发生。
④ Read uncommitted (读未提交):最低级别,任何情况都无法保证。
那么什么是脏读,幻读呢?
1,脏读
脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。
当一个事务正在多次修改某个数据,而在这个事务中这多次的修改都还未提交,这时一个并发的事务来访问该数据,就会造成两个事务得到的数据不一致。例如:用户A向用户B转账100元,对应SQL命令如下
update user set account=account+100 where name=’B’; (此时A通知B) update user set account=account - 100 where name=’A’;
当只执行第一条SQL时,A通知B查看账户,B发现确实钱已到账(此时即发生了脏读),而之后无论第二条SQL是否执行,只要该事务不提交,则所有操作都将回滚,那么当B以后再次查看账户时就会发现钱其实并没有转。
2,不可重复读
不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。
例如事务T1在读取某一数据,而事务T2立马修改了这个数据并且提交事务给数据库,事务T1再次读取该数据就得到了不同的结果,发送了不可重复读。
不可重复读和脏读的区别是,脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前一事务提交的数据。
在某些情况下,不可重复读并不是问题,比如我们多次查询某个数据当然以最后查询得到的结果为主。但在另一些情况下就有可能发生问题,例如对于同一个数据A和B依次查询就可能不同,A和B就可能打起来了……
3,虚读(幻读)
幻读是事务非独立执行时发生的一种现象。例如事务T1对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作,这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读。
幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。
巴拉巴拉了一大堆,现在来讲讲解决方案(自己的想法,有不足之处请大家帮忙指出~)
对于这个问题我们可以使用乐观锁的方式来实现:在user表中加入一个version字段,每次执行update时version++;
操作员A执行的sql操作:select id, account,version from user where id="1"; 查询结果:id=1, account=1000, version=1 update usersetaccount=account+100, version=version+1 whereid="1" and version=1 select id, account,version from user where id="1"; 查询结果:id=1, account=1100, version=2操作员B执行的sql操作select id, account, version from user where id="1"; 查询结果:id=1, account=1000, version=1#操作员A已修改成功,实际user.account=1100、user.version=2,操作员B也将版本号加一(version=2)试图向数据库提交数据(account=950),但此时比对数据库记录版本时发现,操作员B提交的数据版本号为2,数据库记录当前版本也为2,不满足 “提交版本必须大于记录当前版本才能执行更新 “的乐观锁策略,因此,操作员B的提交被驳回。update userset account=account-50, version=version+1 where id="1" and version=1select id, account, version from user where id="1"; 查询结果:id=1, account=1100, version=2
- MySQL并发导致的脏数据分析
- mysql 针对高并发下,同时修改单条数据发生冲突导致数据错误的问题(例如:高并发下单,导致库存为负数)
- mysql新旧数据不一致导致的问题
- MySQL高并发下读取脏数据问题的解决方案
- MySQL的并发控制与加锁分析
- MySQL的并发控制与加锁分析
- mysql的并发控制和加锁分析
- MySQL的并发控制和加锁分析
- ListView item View重用导致并发情况下数据不一致分析
- MySQL + PHP的模式在大并发压力下导致MySQL中存在大量僵死进程
- mysql百万数据的分析
- 该问题应该可以归纳为并发导致的数据库数据重复
- MySQL大数据高并发处理之-查询的优化
- 并发链接,大批量写入数据的mysql插入优化
- MySQL大数据高并发处理之-查询的优化
- MySQL大数据高并发处理之-查询的优化
- MySQL大数据高并发处理之-查询的优化
- MYSQL大数据并发处理
- Java高级篇(四十)------Java IO深入理解
- Hibernate之对象之间的关联操作(cascade与fetch)
- C语言+\microsoft visual studio 10.0\vc\include\cmath(40): error C2059: 语法错误:“;”
- PHP如何获取客户端和服务器端的IP
- 开通CSDN
- MySQL并发导致的脏数据分析
- 单链表的冒泡排序
- Fiddler手机抓包
- 您的文档里包含文档检查器无法删除的个人信息怎么破
- Android截屏幕实现,截取屏幕为bitmap,并保存进手机图库,activity之间传递bitmap对象
- 信息聚合系统的数据库后台(比如RSS订阅,feedly)应该如何设计?
- maven项目发布到tomcat上之后报404错误
- 设计模式之单例模式
- java HashTable 哈希表 及 映射 示例