天道不一定酬所有勤
但是,天道只酬勤

再有人问你万博manbext手机版万博man手机客户端一致性的问题,直接把这篇文章发给他

GitHub 19k Star 的Java工程师成神之路,不来了解一下吗!

在之前的一篇文章《为什么会出现万博manbext手机版和万博man手机客户端不一致的问题》中,我们介绍过万博man手机客户端和万博manbext手机版会出现数据不一致的几种情况。

我们提到过,在万博manbext手机版和万博man手机客户端的操作过程中,可能存在”先写万博manbext手机版,后删万博man手机客户端”、”先写万博manbext手机版,后更新万博man手机客户端”、”先删万博man手机客户端库,后写万博manbext手机版”以及”先更新万博man手机客户端库,后写万博manbext手机版”这四种。

那么,到底是应该删除万博man手机客户端好呢,还是更新万博man手机客户端好呢?到底应该先操作万博manbext手机版呢还是先操作万博man手机客户端呢?哪种方案更好呢?又该如何选择呢?

本文就来展开分析一下。

删除还是更新

为了保证万博manbext手机版和万博man手机客户端里面的数据是一致的,很多人会很多人在做数据更新的时候,会同时更新万博man手机客户端里面的内容。但是我其实告诉大家,应该优先选择删除万博man手机客户端而不是更新万博man手机客户端。

首先,我们暂时抛开数据一致性的问题,单独来看看更新万博man手机客户端和删除万博man手机客户端的复杂的的问题。

我们放到万博man手机客户端中的数据,很多时候可能不只是简单的一个字符串类型的值,他还可能是一个大的JSON串,一个map类型等等。

举个栗子,我们需要通过万博man手机客户端进行扣减库存的时候,你可能需要从万博man手机客户端中查出整个订单模型数据,把他进行反序列化之后,再解析出其中的库存字段,把他修改掉,然后再序列化,最后再更新到万博man手机客户端中。

可以看到,更新万博man手机客户端的动作,相比于直接删除万博man手机客户端,操作过程比较的复杂,而且也容易出错。

还有就是,在万博manbext手机版和万博man手机客户端的一致性保证方面,删除万博man手机客户端相比更新万博man手机客户端要更简单一点。

我们在《为什么会出现万博manbext手机版和万博man手机客户端不一致的问题》中介绍过的”写写并发”的场景中,如果同时更新万博man手机客户端和万博manbext手机版,那么很容易会出现因为并发的问题导致数据不一致的情况。如:

先写万博manbext手机版,再更新万博man手机客户端

W W
写万博manbext手机版,更新成20
写万博manbext手机版,更新成10
写万博man手机客户端,更新成10
写万博man手机客户端,更新成20(数据不一致)

先更新万博man手机客户端,后写万博manbext手机版:

W W
写万博man手机客户端,更新成20
写万博man手机客户端,更新成10
写万博manbext手机版,更新成10
写万博manbext手机版,更新成20(数据不一致)

但是,如果是做万博man手机客户端的删除的话,在写写并发的情况下,万博man手机客户端中的数据都是要被清除的,所以就不会出现数据不一致的问题。

但是,更新万博man手机客户端相比删除万博man手机客户端还是有一个小的缺点,那就是带来的一次额外的cache miss,也就是说在删除万博man手机客户端后的下一次查询会无法命中万博man手机客户端,要查询一下万博manbext手机版。

这种cache miss在某种程度上可能会导致万博man手机客户端击穿,也就是刚好万博man手机客户端被删除之后,同一个Key有大量的请求过来,导致万博man手机客户端被击穿,大量请求访问到万博manbext手机版。

但是,通过加锁的方式是可以比较方便的解决万博man手机客户端击穿的问题的。

总之,删除万博man手机客户端相比较更新万博man手机客户端,方案更加简单,而且带来的一致性问题也更少。所以,在删除和更新万博man手机客户端之间,我还是偏向于建议大家优先选择删除万博man手机客户端。

先写万博manbext手机版还是先删万博man手机客户端

在确定了优先选择删除万博man手机客户端而不是更新万博man手机客户端之后,留给我们的万博manbext手机版+万博man手机客户端更新的可选方案就剩下:”先写万博manbext手机版后删除万博man手机客户端”和”先删除万博man手机客户端后写万博manbext手机版了”。

那么,这两种方式各自有什么优缺点呢?该如何选择呢?

先写万博manbext手机版

因为万博manbext手机版和万博man手机客户端的操作是两步的,没办法做到保证原子性,所以就有可能第一步成功而第二步失败。

而一般情况下,如果把万博man手机客户端的删除动作放到第二步,有一个好处,那就是万博man手机客户端删除失败的概率还是比较低的,除非是网络问题或者万博man手机客户端服务器宕机的问题,否则大部分情况都是可以成功的。

还有就是,先写万博manbext手机版后删除万博man手机客户端虽然不存在”写写并发”导致的数据一致性问题,但是会存在”读写并发”情况下的数据一致性问题。

我们知道,当我们使用了万博man手机客户端之后,一个读的线程在查询数据的过程是这样的:

1、查询万博man手机客户端,如果万博man手机客户端中有值,则直接返回 2、查询万博manbext手机版 3、把万博manbext手机版的查询结果更新到万博man手机客户端中

所以,对于一个读线程来说,虽然不会写万博manbext手机版,但是是会更新万博man手机客户端的,所以,在一些特殊的并发场景中,就会导致数据不一致的情况。

读写并发的时序如下:

W R
读万博man手机客户端,万博man手机客户端中没有值
读万博manbext手机版,万博manbext手机版中得到结果为10
写万博manbext手机版,更新成20
写万博man手机客户端,更新成10(数据不一致)

也就是说,假如一个读线程,在读万博man手机客户端的时候没查到值,他就会去万博manbext手机版中查询,但是如果自查询到结果之后,更新万博man手机客户端之前,万博manbext手机版被更新了,但是这个读线程是完全不知道的,那么就导致最终万博man手机客户端会被重新用一个”旧值”覆盖掉。

这也就导致了万博man手机客户端和万博manbext手机版的不一致的现象

但是这种现象其实发生的概率比较低,因为一般一个读操作是很快的,万博manbext手机版+万博man手机客户端的读操作基本在十几毫秒左右就可以完成了。

而在这期间,更好另一个线程执行了一个比较耗时的写操作的概率确实比较低。

先删万博man手机客户端

那么,如果是先删除万博man手机客户端后操作万博manbext手机版的话,会不会方案更完美一点呢?

首先,如果是选择先删除万博man手机客户端后写万博manbext手机版的这种方案,那么第二步的失败是可以接受的,因为这样不会有脏数据,也没什么影响,只需要重试就好了。

但是,先删除万博man手机客户端后写万博manbext手机版的这种方式,会无形中放大前面我们提到的”读写并发”导致的数据不一致的问题。

因为这种”读写并发”问题发生的前提是读线程读万博man手机客户端没读到值,而先删万博man手机客户端的动作一旦发生,刚好可以让读线程就从万博man手机客户端中读不到值。

所以,本来一个小概率会发生的”读写并发”问题,在先删万博man手机客户端的过程中,问题发生的概率会被放大。

而且这种问题的后果也比较严重,那就是万博man手机客户端中的值一直是错的,就会导致后续的所以命中万博man手机客户端的查询结果都是错的!

延迟双删

那么,虽然先写数据后删除万博man手机客户端的这种情况,可以大大的降低并发问题的概率,但是,根据墨菲定律,只要有可能发生的坏事,那就基本上会发生。越是庞大的系统发生的概率越高。

那么,有没有什么办法可以来解决一下这种情况带来的不一致的问题呢?

其实是有一个比较常见的方案的,在很多公司内用的也比较多,那就是延迟双删

因为”读写并发”的问题会导致并发发生后,万博man手机客户端中的数被读线程写进去脏数据,那么就只需要在写线程在写万博manbext手机版、删万博man手机客户端之后,延迟一段时间,在执行一把删除动作就行了。

这样就能保证万博man手机客户端中的脏数据被清理掉,避免后续的读操作都读到脏数据。当然,这个延迟的时长也很久讲究,到底多久来删除呢?一般建议设置1-2s就可以了。

当然,这种方案也是有一个弊端的,那就是可能会导致万博man手机客户端中准确的数据被删除掉。当然这也问题不大,就像我们前面说过的,只是增加一次cache miss罢了。

如何选择

前面介绍了几种情况的具体问题和解决方案,那么实际工作中应该如何选择呢?

我觉得主要还是根据实际的业务情况来分析。

比如,如果业务量不大,并发不高的情况,可以选择先删除万博man手机客户端,后更新万博manbext手机版的方式,因为这种方案更加简单。

但是,如果是业务量比较大,并发度很高的话,那么建议选择先更新万博manbext手机版,后删除万博man手机客户端的方式,因为这种方式并发问题更少一些。但是可能会引入加锁、延迟双删等更多机制,使得整个方案会更加复杂。

其实,先操作万博manbext手机版,后操作万博man手机客户端,是一种比较典型的设计模式——Cache Aside Pattern

这种模式的主要方案就是先写万博manbext手机版,后删万博man手机客户端,而且万博man手机客户端的删除是可以在旁路异步执行的。

这种模式的优点就是我们说的,他可以解决”写写并发”导致的数据不一致问题,并且可以大大降低”读写并发”的问题,所以这也是Facebook比较推崇的一种模式。

优化方案

Cache Aside Pattern 这种模式中,我们可以异步的在旁路处理万博man手机客户端。其实这种方案在大厂中确实有的还蛮多的。

主要的方式就是借助万博manbext手机版的binlog或者基于异步消息订阅的方式。

也就是说,在代码的主要逻辑中,先操作万博manbext手机版就行了,然后万博manbext手机版操作完,可以发一个异步消息出来。

然后再由一个监听者在接到消息之后,异步的把万博man手机客户端中的数据删除掉。

或者干脆借助万博manbext手机版的binlog,订阅到万博manbext手机版变更之后,异步的清除万博man手机客户端。

这两种方式都会有一定的延时,通常在毫秒级别,一般用于在可接受秒级延迟的业务场景中。

万博man手机客户端更新的设计模式

前面介绍过了Cache Aside Pattern这种关于万博man手机客户端操作的设计模式,那么其实还有几种其他的设计模式,也一起展开介绍一下:

Read/Write Through Pattern

在这两种模式中,应用程序将万博man手机客户端作为主要的数据源,不需要感知万博manbext手机版,更新万博manbext手机版和从万博manbext手机版的读取的任务都交给万博man手机客户端来代理。

Read Through模式下,是由万博man手机客户端配置一个读模块,它知道如何将万博manbext手机版中的数据写入万博man手机客户端。在数据被请求的时候,如果未命中,则将数据从万博manbext手机版载入万博man手机客户端。

Write Through模式下,万博man手机客户端配置一个写模块,它知道如何将数据写入万博manbext手机版。当应用要写入数据时,万博man手机客户端会先存储数据,并调用写模块将数据写入万博manbext手机版。

也就是说,这两种模式下,不需要应用自己去操作万博manbext手机版,万博man手机客户端自己就把活干完了。

Write Behind Caching Pattern

这种模式就是在更新数据的时候,只更新万博man手机客户端,而不更新万博manbext手机版,然后再异步的定时把万博man手机客户端中的数据持久化到万博manbext手机版中。

这种模式的优缺点比较明显,那就是读写速度都很快,但是会造成一定的数据丢失。

这种比较适合用在比如统计文章的访问量、点赞等场景中,允许数据少量丢失,但是速度要快。

没有银弹

《人月神话》的作者Fred Brooks在早年有一篇很著名文章《No Silver Bullet》 ,他提到:

在软件开发过程里是没有万能的终杀性武器的,只有各种方法综合运用,才是解决之道。而各种声称如何如何神奇的理论或方法,都不是能杀死“软件危机”这头人狼的银弹。

也就是说,没有哪种技术手段或者方案,是放之四海皆准的。如果有的话,我们这些工程师也就没有存在的必要了。

所以,任何的技术方案,都是一个权衡的过程,要权衡的问题有很多,业务的具体情况,实现的复杂度、实现的成本,团队成员的接受度、可维护性、容易理解的程度等等。

所以,没有一个”完美”的方案,只有”适合”的方案。

但是,如何能选出一个适合的方案,这里面就需要有很多的输入来做支撑了。希望本文的内容可以为你日后的决策提供一点参考!

(全文完)

扫描二维码,关注作者微信公众号
赞(3)
如未加特殊说明,此网站文章均为原创,转载必须注明出处。HollisChuang's Blog » 再有人问你万博manbext手机版万博man手机客户端一致性的问题,直接把这篇文章发给他
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  • HollisChuang's Blog

    联系我关于我
  • 微信咨询
    关注微信:themebetter
    复制微信号
  • 去评论
    去评论
  • 回顶
    回顶部