java⼀致性_Java中⾼并发下怎么保证数据⼀致性?
数据库⼀致性:
以mysql来说,可能出现脏读、不可重复读以及幻读,mysql默认设置是可重复读,即⼀次事务中不会读取到不同的数据。
假如我们设置为RC(即读取已提交),那么当两个事务并⾏执⾏时,可能出现就是其中⼀个事务还没执⾏完,但是另⼀个事务已经执⾏完,那么就会引发不可重复读的问题;
⽽当我们设置为READUNCOMMITTED(即读取未提交),那么问题更⼤了,其中⼀个事务还没执⾏完,读取到另⼀个事务中间更改的数据,假设另⼀个事务出现回滚,那么就出现脏读了;
但是呢?并⾮说RR就完全没问题,RR可能出现幻读。
你可以做如下操作:
1)打开两个客户端,均设置为RR;
2)在⼀个事务中,查询某个操作查到某份数据;⽐如是某个字段version=1存在数据;
3)在另⼀个事务中,删除这份version=1的数据;删除后,在2所属的事务中查询数据是没有变化的,还是存在version=1的数据;
4)当我们在2所属的事务中继续更新数据,那么会发现更新不了,明明我们就看到了这份version=1的数据;
这就是幻读了。
那么什么才是安全呢?
假设⼀个场景,当⽤户在⽀付这个模块,这需要很强的⼀致性。此时假设我们存在两个表,⼀个是订单表,⼀个是⽤户账号余额表;假设⽤户连点了两次⽀付(不要跟我说⽀付得输密码什么的,反正我认为很快),那么我们平时的逻辑是先查看订单表,如果没有被⽀付,那么我们再来扣减余额,但是我们可以只是简单的扣减吗?安不安全呢?同上⾯模拟的,假如前⼀个⽀付已经⽀付成功了,但是我们当前⽀付由于还处于可重复读,即使外⾯已经⽀付成功,但是我们还是直接扣减余额,那么就会出现重复扣减的情况,保证⽤户下次想打你,信不信。
那么正确的做法是什么?当然得在更新的时候把⽀付状态带上,那么就会返回update=0,⽽我们也知道⽀付过了,即使查的时候还是未⽀付,没办法,为了并发量⾼,不可能去采⽤笨重的串⾏化。
缓存⼀致性:
缓存⼀致,与什么⼀致?是与数据库⼀致,对外查询每个时刻⼀致;所以在针对于缓存与数据库之间该先更新哪⼀个呢?可能有⼈觉得我先更新数据库,再更新缓存不就⾏了吗?但是有想过个问题吗?
当⽤户已经⽀付成功了,更新到数据库,但是呢?你还在缓存中显⽰未⽀付,在⽤户点击频率很⾼并且数据库压⼒过⼤,来不及同步到缓存时,那你是不是很尴尬,这就是典型的不⼀致了。此时⽤户再⽀付,那你⼜告诉他已经⽀付了,那他会把你骂死的
那该怎么来做呢?我们可以这样,先更新缓存再更新数据库,那么存在什么问题呢?
1)缓存更新成功,但是数据库更新失败,⽽被其它的并发线程访问到
2)缓存淘汰成功,但是数据库更新失败,这也会引发后期数据不⼀致
mysql下载不了怎么办这⼀块,我的思考是,读先从缓存中拿取数据,拿不到再去数据库中拿;但是当我们更新时,我们可以维护数据库⽅的⼀致,即更新或者插⼊先往数据库中写⼊,然后再异步地主动淘汰缓存,这样中⼼点在数据库,即使出现说读的不⼀致,也就是出现⼀个中间状态说,数据库更改好了,但是呢?缓存还没有被淘汰。但是相⽐于其它的采⽤2pc、3pc这种重量的保证⼀致好太多了吧,不过倒是可以考虑阿⾥刚出的paxos库看看怎么应⽤到缓存与数据库的同步上;不过对于我们来说,考虑第⼀张已经算⽐
较好的解决了。
对于并发地读这⼀块,我们可能需要考虑⼀个问题,就是过多的请求越过缓存地去读数据库,这就是典型的缓存击穿问题了,这时采⽤利⽤缓存的过期特性来模拟锁结合guava的漏⽃库是⽐较合适的⽅案。
当然什么数据⼀致性,⽬前认为最为好的⽅式就是分布式的⼀致性⽅案,也就是paxos,没有唯⼀,只有场景。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论