0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

先操作缓存还是先操作数据库?

jf_78858299 来源:蝉沐风的码场 作者:蝉沐风 2023-03-03 10:28 次阅读

在数据读多写少的情况下作为缓存来使用,恐怕是Redis使用最普遍的场景了。当使用Redis作为缓存的时候,一般流程是这样的。

  • 如果缓存在Redis中存在,即缓存命中,则直接返回数据

图片

  • 如果Redis中没有对应缓存,则需要直接查询数据库,然后存入Redis,最后把数据返回

图片

通常情况下,我们会为某个缓存设置一个key值,并针对key值设置一个过期时间,如果被查询的数据对应的key过期了,则直接查询数据库,并将查询得到的数据存入Redis,然后重置过期时间,最后将数据返回,伪代码如下:

/**
 * 根据用户名获取用户详细信息
 * @author 公众号【电子发烧友】
 */
public User getUserInfo(String userName) {
      User user = redisCache.getName("user:" + userName);
      if (user != null) {
          return user;
      }

      // 从数据库中直接搜索
      user = selectUserByUserName(userName);
      // 将数据写入Redis,并设置过期时间
      redisCache.set("user:" + userName, user, 30000);
      // 返回数据
      return user;
}

一致性问题

但是,在Redis的key值未过期的情况下,用户修改了个人信息,我们此时既要操作数据库数据,也要操作Redis数据。现在我们面临了两种选择:

  1. 先操作Redis的数据,再操作数据库的数据
  2. 先操作数据库的数据,再操作Redis的数据

如论选择哪种方法,最理想的情况下,两个操作要么同时成功,要么同时失败,否则就会出现Redis和数据库数据不一致的情况。

遗憾的是,目前没有什么框架能够保证Redis的数据和数据库的数据的完全一致性。我们只能根据场景和所需要付出的代码来采取一定的措施降低数据不一致出现的概率,在一致性和性能之间取得一个折中。

下面我们来讨论一下关于Redis和数据库之间数据一致性的一些方案。

方案选择

是删除缓存还是更新缓存?

当数据库数据发生变化的时候,Redis的数据也需要进行相应的操作,那么这个「操作」到底是用「更新」还是用「删除」呢?

「更新」的话调用Redis的set方法,新值替换旧值;「删除」直接删除原来的缓存,下次查询的时候重新读取数据库,然后再更新Redis。

结论:推荐直接使用「删除」操作

因为使用「更新」操作的话,你会面临两种选择

  1. 先更新缓存,再更新数据库
  2. 先更新数据库,再更新缓存

第1种不用考虑了,下面讨论一下「先更新数据库,再更新缓存」这种方案。

图片

如果线程1和线程2同时进行更新操作,但是每个线程的执行顺序如上图所示,此时就会导致数据不一致,因此从这个角度上我们推荐直接使用删除缓存的方式。

此外,推荐使用「删除缓存」还有两点原因。

  1. 如果写数据库的场景比读数据场景多,采用这种方案就会导致缓存就被频繁写入,浪费性能;
  2. 如果缓存要经过一系列复杂的计算才能得到,那么每次写入数据库后,都再次计算写入的缓存无疑也是浪费性能的。

明确这个问题之后,摆在我们面前的就只有两个选择了:

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

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

这种方式可能存在以下两种异常情况

  1. 更新数据库失败,这时可以通过程序捕获异常,直接返回结果,不再继续删除缓存,所以不会出现数据不一致的问题
  2. 更新数据库成功,删除缓存失败。导致数据库是最新数据,缓存中的是旧数据,数据不一致

第2种情况应该怎么办呢?我们有两种方式:失败重试异步更新

失败重试

如果删除缓存失败,我们可以捕获这个异常,把需要删除的 key 发送到消息队列。自己创建一个消费者消费,尝试再次删除这个 key,直到删除成功为止。

图片

这种方式有个缺点,首先会对业务代码造成入侵,其次引入了消息队列,增加了系统的不确定性。

异步更新缓存

因为更新数据库时会往 binlog 中写入日志,所以我们可以启动一个监听 binlog变化的服务(比如使用阿里的 canal开源组件),然后在客户端完成删除 key 的操作。如果删除失败的话,再发送到消息队列。

总结

总之,对于删除缓存失败的情况,我们的做法是不断地重试删除操作,直到成功。无论是重试还是异步删除,都是最终一致性的思想。

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

这种方式可能存在以下两种异常情况

  1. 删除缓存失败,这时可以通过程序捕获异常,直接返回结果,不再继续更新数据库,所以不会出现数据不一致的问题
  2. 删除缓存成功,更新数据库失败。在多线程下可能会出现数据不一致的问题

图片

这时,Redis中存储的旧数据,数据库的值是新数据,导致数据不一致。这时我们可以采用延时双删的策略,即更新数据库数据之后,再删除一次缓存。

图片

用伪代码表示就是:

/**
 * 延时双删
 * @author 公众号【蝉沐风】
 */
public void update(String key, Object data) {
    // 首先删除缓存
    redisCache.delKey(key);
    // 更新数据库
    db.updateData(data);
    // 休眠一段时间,时间依据数据的读取耗费的时间而定
    Thread.sleep(500);
    // 再次删除缓存
    redisCache.delKey(key);
}
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据
    +关注

    关注

    8

    文章

    7003

    浏览量

    88944
  • 缓存
    +关注

    关注

    1

    文章

    239

    浏览量

    26672
  • 数据库
    +关注

    关注

    7

    文章

    3794

    浏览量

    64362
  • Redis
    +关注

    关注

    0

    文章

    374

    浏览量

    10871
收藏 人收藏

    评论

    相关推荐

    使用ADO操作数据库

    使用ADO操作数据库要运行程序必须将数据库文件demo.mdb与可执行文件放在一起,如果在vc开发环境中运行,则需要将该数据库文件放在工程目录下
    发表于 10-15 11:41

    使用OLE DB操作数据库

    使用OLE DB操作数据库 要运行程序必须将数据库文件students.mdb与可执行文件放在一起,如果在vc开发环境中运行,则需要将该数据库文件放在工程目录下
    发表于 10-15 11:41

    使用DAO操作数据库

    使用DAO操作数据库要运行程序必须将数据库文件course.mdb与可执行文件放在一起,如果在vc开发环境中运行,则需要将该数据库文件放在工程目录下
    发表于 10-15 11:42

    labview远程访问数据库操作数据库

    各位高手:多指教,小弟请教一个问题:数据库的基本操作现在已经实现。我现在想这样做,想远程连接数据库并且操作数据库文件,就是在同一局域网内的
    发表于 07-28 12:17

    《Dot.NET数据库开发技术》操作数据库.pdf

    《Dot.NET数据库开发技术》操作数据库.pdf[hide][/hide]
    发表于 06-23 16:27

    pymysql怎么简单的操作数据库

    pymysql简单操作数据库
    发表于 05-01 07:33

    python的操作数据库

    python操作数据库
    发表于 05-20 12:11

    Labsql不能操作数据库连接池吗

    如图,好像连接字符串也只能指定provider才行,指定了max pool还是不会报错。C#里for循环是会报错的。还有一个问题,我要多线程操作数据库,应该怎么玩?数据库会错乱吗?有好多个工位会在不定时间里读写。请问应该怎么
    发表于 09-09 14:44

    使用SQL语句操作数据库

    第一步:Linux开发环境搭建第二步:安装sqlite数据库第三步:sqlite数据库使用1.sqlite部分命令2.使用SQL语句操作数据库(1)创建一个数据表:student(2)
    发表于 11-04 06:23

    《Dot.NET数据库开发技术》操作数据库

    《Dot.NET数据库开发技术》操作数据库
    发表于 02-07 15:11 0次下载

    PHP的使用教程之操作数据库的详细资料说明

    本文档的主要内容详细介绍的是PHP的使用教程之操作数据库的详细资料说明包括了:1.掌握PHP连接数据库的方法,2.掌握PHP创建一个数据库以及表的方法,3.掌握PHP插入和读取数据的方
    发表于 05-29 16:51 12次下载
    PHP的使用教程之<b class='flag-5'>操作数据库</b>的详细资料说明

    分布式系统中的数据库缓存操作顺序

    在分布式系统中,缓存数据库同时存在时,如果有写操作的时候,操作数据库还是
    的头像 发表于 05-03 14:36 2218次阅读
    分布式系统中的<b class='flag-5'>数据库</b>和<b class='flag-5'>缓存</b><b class='flag-5'>操作</b>顺序

    操作数据库还是操作缓存

    来源:捡田螺的小男孩 前言 在分布式系统中,缓存数据库同时存在时,如果有写操作操作数据库还是
    的头像 发表于 10-30 11:09 2005次阅读
    <b class='flag-5'>先</b><b class='flag-5'>操作数据库</b><b class='flag-5'>还是</b><b class='flag-5'>先</b><b class='flag-5'>操作</b><b class='flag-5'>缓存</b>?

    分布式系统中操作数据库还是操作缓存

    前言 在分布式系统中,缓存数据库同时存在时,如果有写操作操作数据库还是
    的头像 发表于 09-30 14:46 1596次阅读

    扫描操作数的信号上升沿

    使用“扫描操作数的信号上升沿”指令,可以确定所指定操作数)的信号状态是否从“0”变为“1”。
    的头像 发表于 06-27 09:39 2695次阅读
    扫描<b class='flag-5'>操作数</b>的信号上升沿