首页 > 数据库 >一文打通——数据库与缓存的数据一致性问题分析

一文打通——数据库与缓存的数据一致性问题分析

时间:2022-08-21 20:02:35浏览次数:107  
标签:缓存 删除 数据库 DB 更新 线程 一致性


缓存读写策略

介绍三种缓存读写策略,各有优劣

Read/Write Through Pattern(读写穿透)

Read/Write Through Pattern 中服务端把 cache 视为主要数据存储,从中读取数据并将数据写入其中。cache 服务负责将此数据读取和写入 DB,从而减轻了应用程序的职责。<!--more-->

  • 先查缓存,缓存中不存在,直接更新DB。

  • 缓存中存在,则先更新缓存,然后缓存服务自己更新 DB(同步更新缓存和 DB)。

 

 

读(Read Through):

  • 从缓存中读取数据,读取到就直接返回 。

  • 读取不到的话,先从 DB 加载,写入到缓存后返回响应。

 

 

Write Behind Pattern(异步缓存写入)

Read/Write Through 是同步更新 cache 和 DB,而 Write Behind Caching 则是只更新缓存,不直接更新 DB,而是改为异步批量的方式来更新 DB。

这种方式下,缓存和数据库的一致性不强,对一致性要求高的系统要谨慎使用。但是它适合频繁写的场景,例如MySQL的InnoDB Buffer Pool机制就使用到了这种模式

接下来登场的这位是本文的重量级选手

Cache Aside Pattern(旁路缓存模式)

我们平时使用比较多的一个缓存读写模式,比较适合读请求比较多的场景。

  • 先更新 DB

  • 然后直接删除缓存。

 

 

:

  • 从缓存中读取数据,读取到就直接返回

  • 缓存中读取不到的话,就从DB中读取数据返回

  • 再把数据放到缓存中。

 

 

这里就引出一个面试的时候常见的一个问题写操作的时候执行的顺序问题导致的缓存和数据库的数据一致性问题

数据库与缓存的数据一致性问题分析

以下问题是从数据库单机的情况下分析的

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

这种情况首先淘汰,如果我们先更新缓存,再更新数据库,如果数据库回滚,缓存就形同虚设了

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

 

 

  1. 线程A写操作,更新数据库

  2. 线程B写操作,更新数据库

  3. 此时线程A由于某些原因没有线程B快,线程B先更新缓存

  4. 线程A更新缓存,此时脏数据出现了,缓存和数据库数据不一致

更新缓存相对于删除缓存还有两点劣势

  1. 如果你写入的缓存值,是经过复杂计算才得到的话,更新缓存频率高的话就浪费性能了

  2. 在写的场景多的情况下,数据很多时候还没有读取到就更新了

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

 

 

 

  1. 线程A写操作,先删除缓存

  2. 线程B读操作,缓存未命中

  3. 线程B读数据库中的值

  4. 线程B将数据更新到缓存中

  5. 此时线程A将新的数据写入数据库中,此时缓存和数据库数据不一致

针对这种情况我们一般使用缓存延时双删,具体来讲就是进行写操作的时候在更新数据库前后两次删除缓存

 

 

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

其实这种情况也有一定概率,但是概率非常小

 

 

  1. 缓存失效的情况线程A读操作直接查询数据库

  2. 线程B写操作更新数据库

  3. 线程B删除缓存

  4. 此时线程A将数据写入缓存

为什么说这种概率比较小呢,因为学过MySQL的直到,select是没有锁的只有update,delete等才会加锁,不加锁的语句肯定是比较快的,而且缓存的写入速度是比数据库的写入速度快很多!。

而先更新数据库再删除缓存这样的写操作步骤也是Cache Aside Pattern所采用的

删除缓存重试机制

不管是延时双删还是先更新数据库再删除缓存,都有可能存在第二步的删除缓存失败,导致数据的不一致性问题,可以使用这种方案优化:删除失败就多删除几次,保证删除成功即可(本质就是删除缓存重试机制保证缓存删干净了)

异步消息队列重试

 

 

  1. 写操作更新数据库

  2. 缓存因为某些原因删除失败

  3. 把删除失败的key放入到消息队列

  4. 消费者消费消息队列中的信息

  5. 重试删除缓存操作

通常都是通过消息队列异步重试

  • 消息队列保证可靠性:写到队列中的消息,成功消费之前不会丢失(重启项目也不担心)

  • 消息队列保证消息成功投递:下游从队列拉取消息,成功消费后才会删除消息,否则还会继续投递消息给消费者

读取binlog异步删除缓存

订阅数据库变更日志,再操作缓存。

拿 MySQL 举例,当一条数据发生修改时,MySQL 就会产生一条变更日志(Binlog),我们可以订阅这个日志,拿到具体操作的数据,然后再根据这条数据,去删除对应的缓存。

订阅变更日志,目前也有了比较成熟的开源中间件,例如阿里的 canal,使用这种方案的优点在于:

  • 无需考虑写消息队列失败情况:只要写 MySQL 成功,Binlog 肯定会有

  • 自动投递到下游队列:canal 自动把数据库变更日志「投递」给下游的消息队列

 

 

MySQL主从复制读写分离的情况下

 

 

  1. 线程A执行写操作,更新主库数据库,binlog中记录好更新语句但是在主从同步的时候主从库延迟

  2. 删除缓存

  3. 线程B执行读操作

  4. 缓存为空直接查询从库

  5. 此时由于主从库延迟从库读到的是未同步的数据

  6. 线程B将未同步的数据更新到缓存中

  7. 线程A此时才将binlog中的数据同步到从库导致缓存数据不一致

解决方法如下

读取binlog异步删除缓存

 

 

延时双删

具体看这篇凭借经验评估

可以做到强一致吗?

看到这里你可能会想,这些方案还是不够完美,我就想让缓存和数据库「强一致」,到底能不能做到呢?

其实很难。

要想做到强一致,最常见的方案是 2PC、3PC、Paxos、Raft 这类一致性协议,但它们的性能往往比较差,而且这些方案也比较复杂,还要考虑各种容错问题。

相反,这时我们换个角度思考一下,我们引入缓存的目的是什么?

没错,性能。

一旦我们决定使用缓存,那必然要面临一致性问题。性能和一致性就像天平的两端,无法做到都满足要求。

而且,就拿我们前面讲到的方案来说,当操作数据库和缓存完成之前,只要有其它请求可以进来,都有可能查到「中间状态」的数据。

所以如果非要追求强一致,那必须要求所有更新操作完成之前期间,不能有「任何请求」进来。

虽然我们可以通过加「分布锁」的方式来实现,但我们要付出的代价,很可能会超过引入缓存带来的性能提升。

所以,既然决定使用缓存,就必须容忍「一致性」问题,我们只能尽可能地去降低问题出现的概率。

同时我们也要知道,缓存都是有「失效时间」的,就算在这期间存在短期不一致,我们依旧有失效时间来兜底,这样也能达到最终一致。

 

标签:缓存,删除,数据库,DB,更新,线程,一致性
From: https://www.cnblogs.com/ccywmbc/p/16610673.html

相关文章

  • 一致性哈希算法
    一致性哈希算法主要应用于Redis分布式缓存问题引出在单节点的情况下,Redis缓存不用担心命中率的问题,但是一旦上升到分布式的架构中,可能会造成一台机器有缓存而另一台机器......
  • http缓存学习
    今天项目上线后,上级看了项目来找到我,发现前端页面没有更新,但我访问没问题,于是排除了上线的问题。看了上级未更新页面的控制台,发现页面html文件竟然是从缓存中取得,为啥不同......
  • 【2022.8.19】MySQL数据库(6)
    学习内容概要视图触发器存储过程事物内置函数流程控制、循环结构索引与慢查询内容详细视图解释:SQL语句执行的结果为一张虚拟表我们基于这张虚拟表去做其......
  • 关于Java 连接 MySQL 数据库报错:Failed to obtain JDBC Connection; ...: Communicati
    原因:是因为Java连接MySQL没有收到任何数据包,也就是说连接失败。解决方法:打开Windows服务程序,找到mysql进程,重启一下就可以了。......
  • 达梦数据库数据守护集群
     1.实验环境:操作系统:银河麒麟V10primary192.168.17.7standby192.168.17.8monitor192.168.17.9 2.配置主机名:hostnamectlset-hostnameprimaryhostnamectlset......
  • 数据库
    安装mysql下载安装压缩包在mysql目录下新建my.ini---my.ini[mysqld]basedir=安装目录\datadir=安装目录\data\default-storage-engine=INNODBport=3306#设置m......
  • 阿里云服务器ECS安装MySQL数据库、初始化密码修改,远程访问权限设置、开放服务器3306端
    1.修改配置文件/etc/my.cnf,在[mysqld]下面添加一行代码:skip-grant-tables2.servicemysqldrestart3.mysql-uroot-p //此时直接回车,既可以进入数据库。4.进数据库后......
  • Sqlite Expert Professional 如何打开加密数据库【转】
    最近要使用个安全的轻量级的数据库,最终选择了SQLITE,好不容上手了,突然发现查询的数据不对头。想着用个工具看看,就下载了SqliteExpertProfessional5.4试试。结果还是打不......
  • 【数据库】 C#利用System.Data.SQLite实现对SQLite的操作
      System.Data.SQLite的一个优点是它是由SQLite团队开发的,该团队已声明长期致力于支持它。支持多种数据类型,比较旧Microsoft.Data.Sqlite的一个优点是它是由Micro......
  • 纯真IP数据库转mysql方法详解
    纯真ip数据库转mysql_如何把纯真ip数据库导入到MySQL数据表中纯真中国IP地理位置  https://www.cz88.net一、下载最新版的QQWry.Dat二、下载IPLook使用IPLook把QQWry......