关于java:面试官高并发场景下你们是怎么保证数据的一致性的

5次阅读

共计 1407 个字符,预计需要花费 4 分钟才能阅读完成。

面试的时候,总会遇到这么一个场景。

1. 场景剖析

面试官: 你们的服务的 QPS 是多少?

我: 咱们的服务高峰期访问量还挺大的,大概是 3 万吧。

面试官: 这么大的访问量,你们的服务器能撑住吗?有加缓存吗?

我: 有的,咱们应用了 Redis 做缓存,接口优先查问缓存,缓存不存在,才拜访数据库。这样能够缩小数据库拜访压力,放慢查问效率。

面试官: 一份数据存储在两个中央,更新数据的时候,你们是怎么保证数据的一致性的?

看到了吧,好的面试官个别不间接问你数据一致性的解决方案,而是谆谆告诫,联合具体的应用场景,再问你解决办法。如果你没做过这方面,没有线上的实战经验,个别很难答复的有条理性、有思考性。

保证数据一致性,个别有这 4 种办法:

  1. 先更新缓存,再更新数据库。
  2. 先更新数据库,再更新缓存。
  3. 先删除缓存,再更新数据库。
  4. 先更新数据库,再删除缓存。

每种计划都具体的讨论一下:

2. 解决方案

2.1 先更新缓存,再更新数据库

如果同时来了两个并发写申请,执行过程是这样的:

  1. 写申请 1 更新缓存,设置 age 为 1
  2. 写申请 2 更新缓存,设置 age 为 2
  3. 写申请 2 更新数据库,设置 age 为 2
  4. 写申请 1 更新数据库,设置 age 为 1

执行后果就是,缓存里 age 被设置 2,数据库里的 age 被设置成 1,导致数据不统一,此计划不可行。

2.2 先更新数据库,再更新缓存

如果同时来了两个并发写申请,执行过程是这样的:

  1. 写申请 1 更新数据库,设置 age 为 1
  2. 写申请 2 更新数据库,设置 age 为 2
  3. 写申请 2 更新缓存,设置 age 为 2
  4. 写申请 1 更新缓存,设置 age 为 1

执行后果就是,数据库里 age 被设置 2,缓存里的 age 被设置成 1,导致数据不统一,此计划不可行。

2.3 先删除缓存,再更新数据库

如果同时来了两个并发读写申请,执行过程是这样的:

  1. 写申请删除了缓存
  2. 读申请查问缓存没数据,而后查询数据库,再把数据写到缓存中
  3. 写申请更新数据库

执行后果是,缓存中是旧数据,而数据库里是新数据,导致数据不统一,此计划不可行。

2.4 先更新数据库,再删除缓存

这种计划,在并发写的时候,不会出问题。因为都是先更新数据库再删除缓存,不会呈现不统一的状况。

然而在并发读写的时候,还是有可能呈现数据不统一。

  1. 读申请查问缓存没数据,而后查询数据库
  2. 写申请更新数据库,删除缓存
  3. 读申请回写缓存

执行后果是,缓存中是旧数据,而数据库里是新数据,导致数据不统一。

其实这种状况呈现的概率很低,写缓存比写数据库快出几个量级,读写缓存都是内存操作,速度十分快。

遇到了这种极其场景,咱们也须要做一下兜底计划,缓存都要设置过期工夫。这种计划属于数据的弱一致性和最终一致性,而不是强一致性。

3. 总结与思考

有读者可能会好奇,为什么不在更新缓存和数据库办法上加上事务注解,实现强一致性,这么哪种计划都不会有问题。

是的,当咱们的服务只在一台机器上,加本地事务是可行的。然而工作中,咱们会把一个服务部署到几十台、上百台机器上,有时候为了应答更极其的查问申请,又在 Redis 缓存加一层本地缓存,这时候咱们再用本地事务是不起作用的。

一份数据在多台机器上,存在多个正本,为了实现强一致性,咱们也能够应用分布式事务。这样一来更新缓存操作将会变得非常复杂,得失相当。

然而在另外的一些场景,比方更新订单状态、更新用户资产,这种场景,咱们无论付出多大代价也要实现数据的强一致性,具体实现计划个别有以下几种:

  1. 二阶段提交
  2. TCC
  3. 本地音讯表
  4. MQ 事务音讯
  5. 分布式事务中间件

下篇文章咱们再一起具体的剖析这几种计划优缺点。

正文完
 0