专栏首页冰河技术​到底是先更新数据库还是先更新缓存?
原创

​到底是先更新数据库还是先更新缓存?

大家好,我是冰河~~

最近小伙伴最近都在问我,在系统中引入缓存后,当向数据库中写入数据时,是先写数据库还是先写缓存呢?先写数据库和先写缓存有什么区别吗?今天,我们就一起来聊聊这个话题。

从本质上讲,无论是先写数据库还是先写缓存,都是为了保证数据库和缓存的数据一致,也就是我们常说的数据一致性。

随着互联网的高速发展,当今时代已然从IT时代进入到DT时代。互联网系统架构也已经由最初的单体架构转变为分布式、微服务架构模式。从数据体量上来看,各系统存储的数据量越来越大,数据的查询性能越来越低。此时,就需要我们不断的进行优化,一种常用的优化手段就是引入缓存。而引入缓存后,我们在向数据库插入数据时,到底是先更新数据库还是先更新缓存呢?

缓存的一般使用

缓存,从本质上讲,是为了更好的协调两个速度差异比较大的组件而引入的一种中间缓存层。例如,如果需要将数据读入CPU进行计算处理,由于CPU的运算速度是非常快的,而磁盘的IO处理相比于CPU来说,慢了很多数量级,每次从磁盘读取数据,势会造成CPU长时间并且频繁等待磁盘IO。此时,我们就可以通过内存来缓和CPU和磁盘之间的速度差异。

从缓存的使用上来说,一般是按照如下的流程来使用缓存。

我们也可以表示成如下的序列图。

在上面的使用示例中,我们只是简单的将数据放入了缓存,最多为缓存设置一个过期时间,到期后,缓存自然就会被清除,后续的请求由于在缓存中获取不到数据,又会从数据库中获取数据,将数据写入缓存。

但是在后续更新数据的操作中,是更新完数据库,接下来更新缓存还是删除缓存?又或者是先删除缓存,再更新数据库?

缓存更新策略

从理论上来说,给缓存设置过期时间,其实是一中最终一致性的表现。这种方案下,可以对存入缓存的数据设置过期时间,所有的写操作以数据库为准,对缓存操作只是尽最大努力即可。也就是说如果数据库写成功,缓存更新失败,那么只要到达过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存。这也是一般情况下,使用的最多的一种方式。

先更新数据库再更新缓存

其实,这种方案很多有经验的小伙伴是很反对的,为啥,我们来分析下。

首先,这种方案会有线程安全的问题。

例如,同时有线程A和线程B对数据进行更新操作,可能会出现下面的执行顺序。

(1) 线程A更新了数据库

(2) 线程B更新了数据库

(3) 线程B更新了缓存

(4) 线程A更新了缓存

此时就会出现数据库中的数据与缓存的数据不一致的情况,这是因为线程A先更新了数据库,可能因为网络等异常情况,线程B更新完数据库进而更新了缓存,当线程B更新完缓存后,线程A才更新缓存,这就导致了数据库数据与缓存数据的不一致。

其次,这种方案也有其不适用的业务场景。

首先一个业务场景就是数据库写多读少的场景,这种场景下采用先更新数据库再更新缓存的策略,就会导致缓存并未被读取就会被频繁的更新,极大的浪费了服务器的性能。

再一个业务场景就是数据库中的数据不是直接写入缓存的,而是需要大量的复杂运算,将运算结果写入缓存。如果这种场景下使用先更新数据库再更新缓存的策略,也会造成服务器资源的浪费。

先删除缓存再更新数据库

先删除缓存再更新数据库的方案也存在着线程安全的问题,例如,线程A更新缓存,同时,线程B读取缓存的数据。可能会出现下面的执行顺序。

(1) 线程A删除缓存

(2) 线程B查询缓存,发现缓存中没有想要的数据

(3) 线程B查询数据库中的旧数据

(4) 线程B将查询到的旧数据写入缓存

(5) 线程A将新数据写入数据库

此时,就出现了数据库中的数据和缓存中的数据不一致的情况。如果删除缓存失败,也会出现数据库数据和缓存数据不一致的现象。

先更新数据库再删除缓存

首先,这种方式也有极小的概率发生数据库数据和缓存数据不一致的情况,例如,线程A做查询操作,线程B执行更新操作,其执行的顺序如下所示。

(1)缓存刚好失效

(2)请求A查询数据库,获取到数据库中的旧值

(3)请求B将新值写入数据库

(4)请求B删除缓存

(5)请求A将查到的旧值写入缓存

如果上述顺序一旦发生,就会造成数据库中的数据和缓存中的数据不一致的情况发生。

但是,先更新数据库再删除缓存的策略发生数据库和缓存数据不一致的概率很低,原因就是:(3)的写数据库操作比步骤(2)的读数据库操作耗时更短,才有可能使得步骤(4)先于步骤(5)执行。但是,往往数据库的读操作的速度远快于写操作,因此步骤(3)耗时比步骤(2)更短,这一场景很难出现。

如果删除缓存失败,也会出现数据库数据和缓存数据不一致的现象。

这样说来,貌似三种方案都不安全呀,那我们该如何做呢?最终要的就是需要引入重试机制。

推荐使用

在实际的生产环境中,推荐 使用先更新数据库再删除缓存 的操作。那么,我们该如何解决这种策略下的问题呢?

有两种方案,一种是在程序逻辑中处理失败重试的操作;另外,借助于阿里巴巴开源的Canal。

手动失败重试

流程如下所示

(1)更新数据库数据;

(2)删除缓存数据失败

(3)将需要删除的key发送至消息队列

(4)自己消费消息,获得需要删除的key

(5)继续重试删除操作,直到成功

这种方案有一个缺点,对业务线代码造成大量的侵入。

同步数据库数据

先来一张图,这种图从整体架构上解决了数据库数据和缓存数据不一致的情况。

流程如下图所示:

(1)更新数据库数据

(2)数据库将数据表数据的变更信息写入binlog日志当中

(3)订阅程序获取所需要的数据以及key

(4)程序逻辑中处理具体的业务逻辑,接收订阅binlog、发起删除缓存的请求。

(5)尝试删除缓存操作,发现删除失败

(6)将这些信息发送至消息队列

(7)重新从消息队列中获得该数据,重试操作。

好了,今天就到这儿吧,我是冰河,我们下期见~~

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 到底是先更新数据库还是先更新缓存?

    很多小伙伴最近都在问我,在系统中引入缓存后,当向数据库中写入数据时,是先写数据库还是先写缓存呢?先写数据库和先写缓存有什么区别吗?今天,我们就一起来聊聊这个话题...

    冰河
  • 高并发场景下,到底先更新缓存还是先更新数据库?

    在大型系统中,为了减少数据库压力通常会引入缓存机制,一旦引入缓存又很容易造成缓存和数据库数据不一致,导致用户看到的是旧数据。

    用户1516716
  • 高并发场景下,到底先更新缓存还是先更新数据库?

    在大型系统中,为了减少数据库压力通常会引入缓存机制,一旦引入缓存又很容易造成缓存和数据库数据不一致,导致用户看到的是旧数据。

    程序IT圈
  • 高并发场景下,到底先更新缓存还是先更新数据库?

    在大型系统中,为了减少数据库压力通常会引入缓存机制,一旦引入缓存又很容易造成缓存和数据库数据不一致,导致用户看到的是旧数据。

    PHP开发工程师
  • 究竟先操作缓存,还是数据库?

    但是,一旦没有命中缓存,或者一旦涉及写操作,流程会比没有缓存更加复杂,这些是今天要分享的话题。

    架构师之路
  • 并发环境下,先操作数据库还是先操作缓存?

    来源:https://mp.weixin.qq.com/s/2ZvPScfbpl85ZGCDbifY1w

    苏三说技术
  • 并发环境下,先操作数据库还是先操作缓存?

    在分布式系统中,缓存和数据库同时存在时,如果有写操作的时候,先操作数据库还是先操作缓存呢?先思考一下,可能会存在哪些问题,再往下看。下面我分几种方案阐述。

    捡田螺的小男孩
  • 数据库事务环境下表级缓存的更新问题

    表数据过多时,通常会为表的记录增加缓存。在我们的业务中,用户的信息是使用redis来做缓存的,避免用户的每次请求都直接查询数据库。 在一些场景下,需要为用户的...

    Tencent JCoder
  • 业务需求:数据库如何保证先查询后插入/更新 原子性?

    当操作积分用户表时,如果accountId在表中没有数据,那么我们新增一条数据,设置用户积分。如果accountId在表中有数据,我们需要更新用户积分。

    一枝花算不算浪漫
  • 一条更新SQL在MySQL数据库中是如何执行的

    前边的在《一条SQL查询在MySQL中是怎么执行的》中我们已经介绍了执行过程中涉及的处理模块,包括连接器、分析器、优化器、执行器、存储引擎等。今天我们来一起看看...

    故里
  • 用新华字典来彻底解释清:数据库索引到底是什么

    数据库超级重要,这个大家应该清楚,学过数据库的朋友一定知道,数据库在使用时,即使没有加索引也可以运行,但是所有学习数据库的资料、教程,一定会有大量的篇幅在介绍数...

    帅地
  • 用新华字典来彻底解释清:数据库索引到底是什么

    数据库超级重要,这个大家应该清楚,学过数据库的朋友一定知道,数据库在使用时,即使没有加索引也可以运行,但是所有学习数据库的资料、教程,一定会有大量的篇幅在介绍数...

    五分钟学算法
  • 数据库中存储日期的字段类型到底应该用varchar还是datetime ?

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huyuyang6688/article/...

    DannyHoo
  • Redis系列 | 缓存穿透、击穿、雪崩、预热、更新、降级

    Redis是高性能的分布式内存数据库,对于内存数据库经常会出现下面几种情况,也经常会出现在Redis面试题中:缓存穿透、缓存击穿、缓存雪崩、缓存预热、缓存更新、...

    王知无-import_bigdata
  • 美团二面:Redis与MySQL双写一致性如何保证?

    四月份的时候,有位好朋友去美团面试。他说,被问到Redis与MySQL双写一致性如何保证?这道题其实就是在问缓存和数据库在双写场景下,一致性是如何保证的?本文将...

    捡田螺的小男孩
  • 缓存架构设计细节二三事

    本文主要讨论这么几个问题: (1)“缓存与数据库”需求缘起 (2)“淘汰缓存”还是“更新缓存” (3)缓存和数据库的操作时序 (4)缓存和数据库架构简析 一、...

    架构师之路
  • MySQL和Redis如何保证数据一致性?

    由于缓存的高并发和高性能已经在各种项目中被广泛使用,在读取缓存这方面基本都是一致的,大概都是按照下图的流程进行操作:

    PHP开发工程师
  • 你知道怎么解决DB读写分离,导致数据不一致问题吗?

    我们小伙伴们有没有考虑到缓存更新的问题,小伙伴们肯定会说肯定用过啊,有数据更新时,把缓存清空掉就行了啊,下一次访问的时候服务就会把新值设置到缓存中了。这样不就行...

    用户5546570
  • 【玩转腾讯云】秒杀系统实战 | 缓存与数据库双写一致性深度分析

            ———— 已经拥有黑眼圈,但还没学会小猪老师时间管理学的蛮三刀同学

    蛮三刀酱

扫码关注云+社区

领取腾讯云代金券