redis 与 DB 的一致性 7 种策略

news2025/3/5 5:30:40

    • 为什么要使用 redis 做缓存?
      • 封底估算
          • 为什么是单行数据的QPS,而不是总的?
        • 什么时候使用DB,Redis,本地缓存
      • 数据的分类
      • 一致性的方案
        • 1. 先清除Redis,再更新 DB
        • 2. 先更新DB,再清除 Redis
          • 使用场景:
        • 3. 延迟删除与延迟双删
          • 使用场景
        • 4. 监听 binlog 清除
        • 5. 双写
          • 使用场景:
        • 6. 监听binlog更新
        • 7.定时刷新
          • 使用场景
      • 选择
      • 其他问题
        • 缓存穿透

为什么要使用 redis 做缓存?

高性能

  • redis压力测试: 20w QPS(瓶颈是网络 io)
  • MySQL: 2w QPS(瓶颈是磁盘io)

封底估算

是否使用缓存是有单行数据的 QPS 决定的

为什么是单行数据的QPS,而不是总的?
  • 使用(Redis)缓存主要是利用 缓存( Redis)高性能的特性减少DB的重复查询
  • 就是数据需要多次查询,并且每次查询的结果是相同的,这时候加到缓存中,可以拦截大量的请求在缓存层,减少db的请求压力
    举个例子
  • 一些 IM 系统的总的 QPS 有几十万(qq,wx 这些)
  • 但是每个人接受的消息是不同的,并且消息是一次性消费的,所以针对单行消息而言,QPS 是小于 1 的并且是一次性消费,不涉及多次重复查询,即使总的 QPS 非常高,也不会使用缓存(IM 系统会使用Redis,但是不是做缓存使用的)
什么时候使用DB,Redis,本地缓存

使用 DB

  • 当数据是部分人可见的(比如后台管理/GM 的需求):单行数据的QPS<1(是一个常数)

使用Redis 缓存

  • 数据所有用户可见,单行数据的QPS>1 ,就需要考虑 Redis 缓存了

本地缓存

  • 原因 Redis 有 20 万 QPS,但是单 key 的 QPS 只有几千;
  • 而本地内存的读写性能有千万(map 读写)
  • 所以当单行数据的 QPS>5000(参考阿里云的热 key 二级缓存标准 QPS) 就可以考虑使用本地缓存了

数据的分类

  • 热数据: 考虑极端热数据的情景(1w 的 QPS)
  • 冷数据: 0 QPS(没人访问)
  • 由冷数据突然变成热数据: QPS 0 突变-> QPS 2000

我们的核心要求是:

  1. 热数据不能失效,因为热数据失效容易造成缓存击穿的问题
  2. 冷数据不能长时间储存在缓存(redis)中: 控制成本
  3. 冷数据(没有缓存)突变到热数据(大量请求): 不能全部请求都回源(查询 DB),否则会造成缓存击穿问题

一致性的方案

1. 先清除Redis,再更新 DB
  • 存在的问题:
    • 间隙查询的不一致问题
    • 有缓存击穿的风险

举个例子:

  1. 不一致
  • 再清除 Redis 后,更新 DB 前有一个查询请求
  • 因为 DB 没有更新,查询请求查询到旧数据,然后将旧数据更新到 Redis
  • 这时 Redis 中的就是一条脏数据,造成数据不一致的问题
  1. 如果清除到一条有 1万 QPS 的 key 很容易发生击穿的问题
2. 先更新DB,再清除 Redis
  • 存在的问题:
    • 缓存击穿的问题

还是清除到 1 万 QPS 的 key 很容易发生击穿

使用场景:
  • QPS不高(单行数据QPS<20,不存在热key 的场景)的非核心业务:比如用户的主页
3. 延迟删除与延迟双删

工作流程: 先更新 DB,再等一段时间清除 Redis / 先清除 Redis,再更新 DB,再等一段时间清除 Redis

核心解决的问题: 查询从库导致的数据不一致的问题

举个例子:

  • 更新 DB 是更新的主库,从库需要一段时间进行同步
  • 但是 DB更新完后,从库没有同步好,这时有一个查询,查询到有个未同步的从库(旧数据)
  • 然后将数据更新到 Redis

所以延迟的作用就是等待从库的数据同步好,保证数据一直

延迟多久?

  • 具体要根据项目中的同步的耗时单行数据的 qps来确定是否需要延迟删除/延迟双删
使用场景
  • QPS不高(单行数据QPS<20,不存在热key 的场景)的非核心业务:比如用户的主页 (本质与方案 2 是相同的,不能有热 key; 只是增加了延迟解决从库查询的不一致问题)
4. 监听 binlog 清除

解决的问题:

  • 将自当成 DB 的一个从节点,监听主节点的 binlog,并删除Redis
  • 服务解耦和: 与之前不同的是这种方案是低耦合实现

问题:

  • 同样只适用于 QPS<20(没有热 key 问题的需求)
  • 实现复杂: 监听 binlog 的逻辑是比较复杂的(比如 binlog 的获取,解析,消费等)
5. 双写

工作流程: 先更新 DB,再更新 Redis

  • 存在的问题与解决方案
    • 并发写的不一致问题
      • 举个例子: 有两个写请求(请求 1,将数据改为 A; 请求 2 将数据改为 B)
      • 这时请求 1 先执行,将DB 改为 A,然后请求 1 阻塞
      • 请求 2 后执行,先将 DB 改成 B , 再将 Redis 改成 B
      • 这时请求 1 阻塞恢复了,将 Redis 改成 A
      • 这时 DB 是 B,Redis(缓存)是 A,造成了一个数据不一致的问题
    • 解决方案: 加分布式锁,不允许同时更改,只能一个执行完成才能执行下一个
使用场景:
  • 完善一下

    • 热数据的过期失效问题(自动续期)
    • 冷数据突变为热数据(只放一个请求回源更新,其他请求返回空)
  • 完善后可以适用于高读,高写,高一致性的场景: 比如抢红包业务

  • 抢红包业务分析:

    • 红包发出会有大量的抢红包的操作,就涉及到大量的写(并发写)
    • 红包发出有大量的人查询抢红包的记录: 大量读
    • 抢完红包后需要所有人看到最新的记录: 高的一致性
6. 监听binlog更新

核心解决的问题

  • binlog 是有序的,如果能顺序消费 binlog 就可以解决双写中并发写的问题

如何订阅 binlog?

  1. 使用Canal组件(原理就是伪装成从节点向主节点发送 dump)

缺点:

  • 复杂,binlog 的获取,解析,消费整套是比较复杂的逻辑(比锁实现起来要更复杂),并且还要考虑过期失效的问题与冷数据突变为热数据的问题
  • 一致性没有双写好(双写使用了分布式锁,基本就是一个强一致的方案)
7.定时刷新

实现方式:

  • 储存(Redis)缓存的时候储存一个数据的更新标记(val 是一个空值),并且 更新标记的过期时间<数据的过期时间

    • 比如:更新标记 10s 过期; 数据 10 分钟过期
  • 查询的时候先使用 set nx px 命令检查更新标记是否存在,

    • 如果存在(写失败): 就使用 Redis 的数据
    • 如果不存在(写成功): 就回源(查询 DB)更新 Redis(更新数据与过期时间)

如此就可以实现数据的定时刷新(每个更新标记的存活时间刷新一次),这样可以解决一致性的问题与热数据过期失效的问题,一举两得

解决完热数据的问题还剩冷数据与冷数据突变为热数据

  1. 冷数据: 不使用会自动过期,释放空间
  2. 冷数据突变为热数据: 第一个会回源(set nx 成功),其他请求暂时返回空,知道回源完成

为什么是返回空而不是回源或者阻塞?

  1. 回源的问题: 击穿
  2. 阻塞的问题: 阻塞大量的请求(协程)(假设有 1万 QPS,回源需要 10s,就会阻塞 10 万个请求)

优点:

  1. 实现简单: 定时刷新同时解决了一致性问题与热数据的失效问题
  2. 好拓展: 不仅适用于 Redis 与 DB 的一致性,还适用于内存与 Redis 的一致性
使用场景
  1. 一致性要求不高(不敏感): 比如要求是更新后 10 分钟内可以看到数据最新的状态
  2. 读高: 有大量读并存在热点问题

比如: 短视频平台的视频数据

选择

  1. 封顶估算: 对单行数据进行封顶估算决定是否使用缓存,使用什么缓存
  2. 任务分解: 将实际需求中可能遇到的情况罗列出来(比如:热数据,冷数据,冷数据突变为热数据)
  3. 分析每一种情况,并提供对应的解决方案.

参考: https://blog.csdn.net/weixin_42338901/article/details/129231758

其他问题

上面主要解决的是击穿问题,Redis 缓存的常见的三大问题还有两个

  1. 缓存穿透: 使用不存在的 key 进行访问
  2. 缓存雪崩: 缓存大面积失效
缓存穿透

常见的解决方案:

  1. 布隆过滤器
    实现: 多次哈希,然后标记对应的 bit 位(变为 1)
    判断是否出现过: 多次哈希,所有 bit 位都为 1 才算

问题:

  • 不是 100% 准确的,存在误判的可能
  • 只能记录是否出现过(适合做加法,很难做减法)
    举个例子: 某个 key 开始是不存在的,加到布隆过滤器中防止穿透,后面存在了;这时候别人访问这个 key 就会拦截掉.
  1. 记录空值
  • 相同的不存在的 key 重复访问会拦截到 Redis 层

缺点:
比布隆过滤器消耗空间更大

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2309824.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker安装Redpandata-console控制台

介绍 Redpanda控制台&#xff0c;这是一个功能强大的Web UI&#xff0c;用于管理和监控您的Redpanda 集群。探索实际示例和场景&#xff0c;以帮助您了解如何利用 Redpanda 控制台实现不同的用例&#xff0c;包括数据可观察性、Redpanda 管理、访问控制和连接。 可对Redpanda…

【分享】网间数据摆渡系统,如何打破传输瓶颈,实现安全流转?

在数字化浪潮中&#xff0c;企业对数据安全愈发重视&#xff0c;网络隔离成为保护核心数据的重要手段。内外网隔离、办公网与研发网隔离等措施&#xff0c;虽为数据筑牢了防线&#xff0c;却也给数据传输带来了诸多难题。传统的数据传输方式在安全性、效率、管理等方面暴露出明…

Docker创建自定义网桥并指定网段

前言 docker0是Docker默认网络的核心组件, 通过虚拟网桥和NAT技术, 实现了容器间的通信以及容器与外部网络的交互。然而, docker0网段是固定的(通常是172.17.0.0/16), 为了更灵活地管理容器网络&#xff0c;Docker支持创建自定义网桥&#xff0c;允许用户指定网段。 例如, 在…

03.03 QT

1.在注册登录的练习里面&#xff0c;追加一个QListwidget 项目列表 要求:点击注册之后&#xff0c;将账号显示到 1istwidget上面去 以及&#xff0c;在listwidget中双击某个账号的时候&#xff0c;将该账号删除 Widget.h: #ifndef WIDGET_H #define WIDGET_H#include <QWi…

华为云 | 快速搭建DeepSeek推理系统

DeepSeek&#xff08;深度求索&#xff09;作为一款国产AI大模型&#xff0c;凭借其高性能、低成本和多模态融合能力&#xff0c;在人工智能领域崛起&#xff0c;并在多个行业中展现出广泛的应用潜力。 如上所示&#xff0c;在华为云解决方案实践中&#xff0c;华为云提供的快速…

BUUCTF [BJDCTF2020]EasySearch1

写一篇文章来学习一下 ssi 注入 以及 dirmap 工具的使用 看到这两个框框没什么想法&#xff0c;边探索边扫下目录吧。显示前端报错&#xff0c;先禁用了js&#xff0c;然后又尝试抓了下包&#xff0c;没有发现什么&#xff0c;只好看看扫出来的目录了&#xff0c;最终扫出来了…

探秘基带算法:从原理到5G时代的通信变革【五】CORDIC算法

文章目录 2.4 CORDIC算法2.4.1 CORDIC算法的基本原理2.4.2 方法论与分类体系旋转模式矢量模式线性模式 2.4.3 **CORDIC 算法中的误差来源****角度逼近误差的分析****缩放效应误差的分析****精度需求与迭代次数的关系****常见应用场景下的迭代次数建议****总结** 2.4.4优缺点分析…

FPGA学习篇——Verilog学习2

1 系统函数 Verilog 语言中预先定义了一些任务和函数&#xff0c;用于完成一些特殊的功能&#xff0c;它们被称为系统任务和系统函数&#xff0c;这些函数大多数都是只能在 Testbench 仿真中使用的&#xff0c;使我们更方便的进行验证。 1.1 时间预编译指令及延时 时间精度&a…

51单片机编程学习笔记——74HC245八路三态输出双向收发器

大纲 组成电源引脚和地引脚使能输入端DIR&#xff08;T/R&#xff09;引脚A端和B端 工作原理数据传输方向控制使能控制 在单片机系统里&#xff0c;单片机的 I/O 口驱动能力往往有限。当需要连接较多外部设备或者负载较大时&#xff0c;就可能出现信号传输不稳定的问题。74HC24…

C++:类和对象(下篇)

1. 再谈构造函数 1.1 构造函数体赋值 在创建对象时&#xff0c;编译器通过调用构造函数&#xff0c;给对象中各个成员变量一个合适的初始值。 class Date { public:Date(int year, int month, int day){_year year;_month month;_day day;} private:int _year;int _mont…

DeepSeek集成到VScode工具,让编程更高效

DeepSeek与VScode的强强联合&#xff0c;为编程效率树立了新标杆。 DeepSeek&#xff0c;一款卓越的代码搜索引擎&#xff0c;以其精准的索引和高速的检索能力&#xff0c;助力开发者在浩瀚的代码海洋中迅速定位关键信息。 集成至VScode后&#xff0c;开发者无需离开熟悉的编辑…

PPT 小黑第38套

对应大猫40 幻灯片母板-最后一页-重命名为奇数页 奇偶页-点中标题-形状格式-形状填充-青色 最后一页页码左对齐 更换幻灯片背景&#xff1a;设计-设置背景格式-图片填充 【开始】-段落居中&#xff0c;对齐文本-中部对齐&#xff0c;排列-对齐-底端&#xff0c;-再水平居中…

[Web 安全] PHP 反序列化漏洞 —— PHP 魔术方法

关注这个专栏的其他相关笔记&#xff1a;[Web 安全] 反序列化漏洞 - 学习笔记-CSDN博客 PHP 魔术方法 - 简介 - PHP 魔术方法 - 简单教程&#xff0c;简单编程PHP 中&#xff0c;以两个下划线 ( __ ) 开头方法称之为 「 魔术方法 」 这些 「 魔术方法 」 在 [PHP](/l/yufei/php…

基于Matlab的多目标粒子群优化

在复杂系统的设计、决策与优化问题中&#xff0c;常常需要同时兼顾多个相互冲突的目标&#xff0c;多目标粒子群优化&#xff08;MOPSO&#xff09;算法应运而生&#xff0c;作为群体智能优化算法家族中的重要成员&#xff0c;它为解决此类棘手难题提供了高效且富有创新性的解决…

20250303-代码笔记-class CVRPTester

文章目录 前言一、class CVRPTester:__init__(self,env_params,model_params, tester_params)1.1函数解析1.2函数分析1.2.1加载预训练模型 1.2函数代码 二、class CVRPTester:run(self)函数解析函数代码 三、class CVRPTester:_test_one_batch(self, batch_size)函数解析函数代…

C++学习之C++初识、C++对C语言增强、对C语言扩展

一.C初识 1.C简介 2.第一个C程序 //#include <iostream> //iostream 相当于 C语言下的 stdio.h i - input 输入 o -output 输出 //using namespace std; //using 使用 namespace 命名空间 std 标准 &#xff0c;理解为打开一个房间&#xff0c;房间里有我们所需…

关于虚拟环境中遇到的bug

conda和cmd介绍 介绍 Conda 概述&#xff1a; Conda是一个开源包管理系统和环境管理系统&#xff0c;尤其适用于Python和R语言的开发环境。它允许用户创建独立的虚拟环境&#xff0c;方便地管理依赖包和软件版本。 特点&#xff1a; 环境管理&#xff1a;可以创建、导入、导…

【网络安全 | 渗透测试】GraphQL精讲一:基础知识

未经许可,不得转载, 文章目录 GraphQL 定义GraphQL 工作原理GraphQL 模式GraphQL 查询GraphQL 变更(Mutations)查询(Queries)和变更(Mutations)的组成部分字段(Fields)参数(Arguments)变量别名(Aliases)片段(Fragments)订阅(Subscriptions)自省(Introspecti…

什么是JTAG、SWD?

一、什么是JTAG&#xff1f; JTAG&#xff08;Joint Test Action Group&#xff0c;联合测试行动小组&#xff09;是一种国际标准测试协议&#xff0c;常用于芯片内部测试及对系统进行调试、编程等操作。以下从其起源、工作原理、接口标准、应用场景等方面详细介绍&#xff1a…

如何在Apple不再支持的MacOS上安装Homebrew

手头有一台2012年产的Macbook Pro&#xff0c;系统版本停留在了10.15.7&#xff08;2020年9月24日发布的&#xff09;。MacOS 11及后续的版本都无法安装到这台老旧的电脑上。想通过pkg安装Homebrew&#xff0c;发现Homebrew releases里最新的pkg安装包不支持MacOS 10.15.7&…