当前位置: 首页 > news >正文

缓存一致性问题

1. 引言

1.1 数据库与缓存的工程实践

在软件工程领域,数据库(Database)和缓存(Cache)是两种常见的数据存储解决方案,它们在系统架构中扮演着至关重要的角色。数据库是数据持久化的后端存储,它提供了数据的长期存储、复杂查询和事务性支持。而缓存则是一种位于数据库和应用程序之间的临时存储,它通过存储频繁访问的数据来减少对数据库的直接访问次数,从而提高系统的性能。

数据库的特点

  • 持久性:数据长期存储,即使系统崩溃也能恢复数据。

  • 稳定性:成熟的事务支持,确保数据的一致性和完整性。

  • 容量:通常具有较大的存储容量。

缓存的特点

  • 速度:读写速度快,能够快速响应数据请求。

  • 容量:相对数据库来说,存储容量较小。

  • 成本:访问延迟低,成本相对较高。

在高并发的环境下,合理利用缓存可以显著提升数据访问速度,减轻数据库的负担。

1.2 缓存一致性问题的提出

尽管缓存可以提高性能,但它也带来了新的挑战——缓存一致性问题。当数据库中的数据更新后,如何确保缓存中的数据也同步更新,避免用户读取到过时的数据,是缓存一致性问题的核心。

缓存一致性可以分为三个层次:

  • 强一致性:缓存和数据库中的数据始终保持一致。

  • 最终一致性:缓存和数据库中的数据在一定时间内可能不一致,但最终会达到一致状态。

  • 弱一致性:不保证缓存和数据库中的数据一致性,数据的更新可能会延迟。

在实际应用中,强一致性虽然理想,但实现成本较高,而最终一致性和弱一致性虽然实现成本较低,但可能会影响用户体验。

结语

在本节中,我们探讨了数据库和缓存在工程实践中的应用,以及缓存一致性问题的重要性。理解这些基本概念对于后续深入分析缓存一致性问题和解决方案至关重要。在接下来的章节中,我们将详细分析缓存一致性问题,并探讨不同的解决方案。

  1. 理论分析

2.1 缓存一致性问题

缓存一致性问题是指在分布式系统中,缓存数据与数据库数据之间的同步问题。当数据库中的数据被更新或删除后,如何保证缓存中的数据也相应地更新或删除,以避免用户读取到过时的数据。

问题背景

  • 在一个高并发的系统中,数据库的压力非常大,使用缓存可以显著减轻这种压力。

  • 缓存通常存储热点数据,即那些被频繁访问的数据。

  • 缓存和数据库是两个独立的存储系统,它们之间的数据同步需要额外的机制来保证。

问题难点

  • 并发控制:在多线程或多进程环境中,如何保证数据操作的原子性和一致性。

  • 数据同步延迟:缓存和数据库之间的数据同步可能会有延迟,这可能导致数据不一致。

  • 系统复杂性:引入缓存一致性机制会增加系统的复杂性,可能会影响性能。

2.2 读写流程串讲

读写流程是缓存一致性问题中的核心概念。下面详细分析读写流程中的各个步骤:

写流程
  1. 写入数据库:当数据更新请求到达时,首先将数据写入数据库。

  2. 清除缓存:在数据写入数据库后,需要清除缓存中对应的数据项,以避免读操作读取到旧数据。

读流程
  1. 查询缓存:当数据读取请求到达时,首先查询缓存是否有该数据。

  2. 缓存未命中:如果缓存中没有数据(Cache Miss),则查询数据库。

  3. 更新缓存:从数据库中读取数据后,将其写入缓存,以便下次可以直接从缓存中读取。

流程图

2.3 缓存双删策略

缓存双删策略是一种解决缓存一致性问题的策略。在写流程中,不是只清除一次缓存,而是进行两次清除操作:

  1. 第一次清除:在数据写入数据库之前,先清除缓存中的数据。

  2. 写入数据库:执行数据的写入操作。

  3. 第二次清除:在数据写入数据库之后,再次清除缓存中的数据。

这种策略可以减少在写操作和第一次清除缓存之间的时间窗口,降低读操作读取到旧数据的风险。

流程图

2.4 缓存延时双删策略

缓存延时双删策略是对双删策略的改进,它在第二次清除缓存之前增加了一个延时:

  1. 第一次清除:在数据写入数据库之前,先清除缓存中的数据。

  2. 写入数据库:执行数据的写入操作。

  3. 延时:在写入数据库后,等待一段预设的时间。

  4. 第二次清除:延时结束后,再次清除缓存中的数据。

这种策略可以进一步减少读操作在写操作和第一次清除缓存之间的时间窗口内读取到旧数据的风险。

流程图

2.5 写缓存禁用机制

写缓存禁用机制是一种更为激进的策略,它在写操作期间暂时禁用缓存的写入:

  1. 禁用写入:在数据写入数据库之前,禁用缓存的写入功能。

  2. 写入数据库:执行数据的写入操作。

  3. 等待确认:等待数据库操作确认完成。

  4. 启用写入:数据库操作完成后,重新启用缓存的写入功能。

这种策略可以确保在写操作期间,缓存不会被更新,从而避免了缓存和数据库之间的数据不一致问题。

流程图

结语

在本节中,我们深入分析了缓存一致性问题,并探讨了几种常见的解决方案,包括缓存双删策略、缓存延时双删策略和写缓存禁用机制。这些策略各有优势和适用场景,选择合适的策略需要根据具体的业务需求和系统特点来决定。在实际应用中,可能需要结合多种策略来达到最佳的缓存一致性效果。在下一节中,我们将通过技术实战来进一步展示这些策略的具体实现。

  1. 技术实战

在本节中,我们将深入探讨一致性缓存服务的实现,包括架构设计、缓存模块、数据库模块以及服务层的具体实现。我们将通过代码示例和图表来详细展示如何构建一个高效的缓存系统。

3.1 架构设计

首先,我们需要设计一个清晰的架构,以支持缓存和数据库之间的一致性操作。架构的核心组件包括:

  • 一致性缓存服务(Service):作为系统的核心,负责协调缓存和数据库的操作,确保数据的一致性。

  • 缓存模块(Cache):负责缓存数据的存储和检索,以及与缓存相关的一致性操作。

  • 数据库模块(DB):负责数据库的交互,包括数据的持久化和查询。

架构图

3.2 缓存模块实现

缓存模块是实现数据快速访问的关键。以下是缓存模块需要实现的核心功能:

  • Get:从缓存中获取数据,如果数据不存在(Cache Miss),返回特定的错误。

  • Del:从缓存中删除指定的数据。

  • Disable:禁用特定数据的缓存写入机制,通常用于写操作期间。

  • Enable:延时启用特定数据的缓存写入机制,确保数据的一致性。

  • PutWhenEnable:当缓存写入机制启用时,将数据写入缓存。

缓存模块的伪代码示例

type Cache interface {Get(key string) (string, error)Del(key string) errorDisable(key string, expireSeconds int64) errorEnable(key string, delaySeconds int64) errorPutWhenEnable(key, value string, expireSeconds int64) error
}
3.3 数据库模块实现

数据库模块负责与数据库的交互,以下是数据库模块需要实现的核心功能:

  • Put:将数据写入或更新到数据库。

  • Get:从数据库中读取数据,如果数据不存在,返回特定的错误。

数据库模块的伪代码示例

type DB interface {Put(ctx context.Context, obj interface{}) errorGet(ctx context.Context, objPtr interface{}) error
}
3.4 一致性缓存服务Service实现

服务层是缓存模块和数据库模块的协调者,它实现了数据的读写操作,并确保了缓存和数据库之间的一致性。以下是服务层的核心实现逻辑:

  • 写操作:先在数据库中写入数据,然后根据策略禁用或延时禁用缓存写入机制。

  • 读操作:先尝试从缓存中读取数据,如果缓存未命中,则从数据库中读取并更新到缓存。

服务层的伪代码示例

type Service struct {cache Cachedb    DBopts  *options
}
func (s *Service) Write(ctx context.Context, key string, value string) error {// 写入数据库err := s.db.Put(ctx, value)if err != nil {return err}// 根据策略禁用或延时禁用缓存写入if s.opts.DisableCacheWrite {return s.cache.Disable(key, s.opts.DisableCacheWriteDuration)} else {return s.cache.Enable(key, s.opts.CacheWriteDelayDuration)}
}
func (s *Service) Read(ctx context.Context, key string, objPtr interface{}) error {value, err := s.cache.Get(key)if err == nil {// 缓存命中,直接返回数据return nil} else if err != s.cache.ErrorCacheMiss {// 缓存错误,返回错误return err}// 缓存未命中,从数据库中读取err = s.db.Get(ctx, objPtr)if err != nil {return err}// 更新到缓存return s.cache.PutWhenEnable(key, value, s.opts.CacheExpiration)
}

结语

在本节中,我们详细介绍了一致性缓存服务的架构设计和核心组件的实现。通过分离缓存模块和数据库模块,我们能够清晰地管理数据的读写操作,并确保缓存和数据库之间的一致性。在下一节中,我们将通过具体的代码示例和案例分析,进一步展示这些概念在实际开发中的应用。

  1. 缓存一致性问题的深入分析与策略

在本节中,我们将深入分析缓存一致性问题,并探讨不同的解决策略。我们将详细讨论每种策略的工作原理、优缺点,并通过图表和代码示例来展示如何在实际系统中应用这些策略。

4.1 缓存一致性问题的根源

缓存一致性问题主要源于缓存和数据库之间的数据同步问题。当数据库中的数据更新时,如果缓存中的数据没有同步更新,就可能导致用户读取到过时的数据。这种情况在高并发系统中尤为常见,因为多个请求可能同时修改和读取同一数据项。

4.2 缓存一致性策略
4.2.1 缓存失效策略(Cache Invalidation)

工作原理:当数据库中的数据更新或删除时,立即或在很短的时间内使缓存中对应的数据项失效。

优点

  • 简单直接,易于实现。

  • 可以保证数据的强一致性。

缺点

  • 可能会因为频繁的失效操作导致缓存的利用率降低。

  • 在高并发情况下,可能会引起缓存雪崩现象。

4.2.2 缓存双删策略(Double Cache Eviction)

工作原理:在更新数据库前先删除缓存中的数据,待数据库更新完成后再次删除缓存中的数据。

优点

  • 减少了缓存中保留过时数据的时间窗口。

缺点

  • 两次删除操作之间仍然存在时间窗口,可能无法完全避免数据不一致。

4.2.3 缓存延时双删策略(Delayed Double Cache Eviction)

工作原理:在数据库更新后,不是立即进行第二次删除缓存操作,而是等待一段预设的时间。

优点

  • 进一步减少了数据不一致的风险。

缺点

  • 增加了实现的复杂性。

  • 需要合理设置延时时间,以平衡性能和一致性。

4.2.4 写缓存禁用机制(Write-Through Cache Disable)

工作原理:在数据写入数据库的过程中,暂时禁用缓存的写入操作。

优点

  • 确保了写操作期间缓存不会被更新,从而避免了数据不一致。

缺点

  • 写操作期间缓存的写入性能受到影响。

4.2.5 缓存穿透和雪崩解决方案

缓存穿透:指查询不存在的数据,导致请求直接打到数据库上。

解决方案

  • 使用布隆过滤器来快速判断数据是否存在。

  • 缓存空结果或使用特殊标记。

缓存雪崩:指大量缓存数据在同一时间过期,导致大量请求直接打到数据库上。

解决方案

  • 为缓存数据设置随机过期时间。

  • 使用分布式锁或其他同步机制来控制缓存重建。

4.3 策略选择与应用

选择缓存一致性策略时,需要考虑以下因素:

  • 业务需求:是否需要强一致性或最终一致性。

  • 系统负载:高并发情况下的系统表现。

  • 实现复杂性:策略的实现难度和对现有系统的影响。

4.4 实例分析

我们将通过一个具体的示例来展示如何在实际系统中应用上述策略。假设我们有一个电子商务平台,需要处理商品信息的缓存和数据库同步。

示例场景

  • 商品信息更新操作。

  • 商品信息读取操作。

代码示例

// 商品信息更新操作
func UpdateProduct(ctx context.Context, service *Service, key string, product *Product) error {// 禁用缓存写入err := service.cache.Disable(key, service.opts.WriteDisableDuration)if err != nil {return err}// 更新数据库err = service.db.Put(ctx, product)if err != nil {return err}// 延时启用缓存写入return service.cache.Enable(key, service.opts.WriteEnableDelay)
}
// 商品信息读取操作
func GetProduct(ctx context.Context, service *Service, key string) (*Product, error) {product, err := service.cache.Get(key)if err == nil {return product, nil}// 缓存未命中,从数据库中读取product = &Product{}err = service.db.Get(ctx, product)if err != nil {return nil, err}// 更新到缓存return product, service.cache.PutWhenEnable(key, product, service.opts.CacheExpiration)
}

结语

在本节中,我们深入分析了缓存一致性问题,并探讨了多种解决策略。我们讨论了每种策略的工作原理、优缺点,并提供了如何在实际系统中应用这些策略的示例。缓存一致性是一个复杂的问题,需要根据具体的业务需求和系统特点来选择合适的策略。在下一节中,我们将通过一个开源项目来进一步展示这些策略的具体实现和应用效果。

相关文章:

缓存一致性问题

1. 引言 1.1 数据库与缓存的工程实践 在软件工程领域,数据库(Database)和缓存(Cache)是两种常见的数据存储解决方案,它们在系统架构中扮演着至关重要的角色。数据库是数据持久化的后端存储,它…...

【MYSQL】MYSQL逻辑架构

mysql逻辑架构分为3层 mysql逻辑架构分为3层 1). 连接层:主要完成一些类似连接处理,授权认证及相关的安全方案。 2). 服务层:在 MySQL据库系统处理底层数据之前的所有工作都是在这一层完成的,包括权限判断,SQL接口&…...

【Python】数据类型之字符串

本篇文章将继续讲解字符串其他功能: 1、求字符串长度 功能:len(str) ,该功能是求字符串str的长度。 代码演示: 2、通过索引获取字符串的字符。 功能:str[a] str为字符串,a为整型。该功能是获取字符…...

c++编写java模式的线程类

在 C11 中&#xff0c;我们可以使用 <thread> 标准库来创建和管理线程。然而&#xff0c;C 不像 Java 那样提供一个内置的 Thread 类&#xff0c;而是提供了一个更底层的 API。下面是一个模拟 Java 中 Thread 类功能的 C11 实现。 我们将创建一个名为 SimpleThread 的类…...

vcpkg install libtorch[cuda] -allow-unsupported-compiler

在vcpkg中不懂如何使用 nvcc 的 -allow-unsupported-compiler, 所以直接注释了CUDA中对版本的检查代码. C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.8\include\crt\host_config.h 奇了怪了,我是用的是vs2022,但是还是被检查为不支持的编译器!!! 可以试一下改这…...

Flink的DateStream API中的ProcessWindowFunction和AllWindowFunction两种用于窗口处理的函数接口的区别

目录 ProcessWindowFunction AllWindowFunction 具体区别 ProcessWindowFunction 示例 AllWindowFunction 示例 获取时间不同&#xff0c;一个数据产生的时间一个是数据处理的时间 ProcessWindowFunction AllWindowFunction 具体示例 ProcessWindowFunction 示例 Al…...

MATLAB中dmperm函数用法

目录 语法 说明 dmperm函数的功能是完成Dulmage-Mendelsohn 分解。 语法 p dmperm(A) [p,q,r,s,cc,rr] dmperm(A) 说明 如果列 j 与行 i 匹配&#xff0c;p dmperm(A) 得到的结果为向量 p&#xff0c;这样 p(j) i&#xff0c;如果列 j 与其不匹配&#xff0c;得到的结…...

苹果折叠屏设备:创新设计与技术突破

本文首发于公众号“AntDream”&#xff0c;欢迎微信搜索“AntDream”或扫描文章底部二维码关注&#xff0c;和我一起每天进步一点点 苹果折叠屏设备&#xff1a;创新设计与技术突破 在科技迅速发展的今天&#xff0c;苹果公司以其一贯的创新精神和对产品质量的严格把控&#x…...

C#加班统计次数

C#加班统计次数 运行环境&#xff1a;vs2022 .net 8.0 社区版 1、用C#语言&#xff1b;2、有界面上传Excel文件; 3、对Excel列&#xff08;部门、人员姓名、人员编号、考勤时间 &#xff09;处理&#xff1a;&#xff08;1&#xff09;按人员编号、考勤日期分组且保留原来字段&…...

【资治通鉴】“ 将欲取之、必先予之 “ 策略 ① ( 魏桓子 割让土地 | 资治通鉴原文分析 | 道德经、周书、吕氏春秋、六韬 中的相似策略 )

文章目录 一、" 将欲取之、必先予之 " 策略1、魏桓子 割让土地2、资治通鉴原文分析 二、" 将欲取之、必先予之 " 类似的原理1、将欲败之&#xff0c;必姑辅之&#xff1b;将欲取之&#xff0c;必姑与之 - 周书2、将欲歙之&#xff0c;必固张之&#xff0c;…...

Spring5 的日志学习

我们在使用 Spring5 的过程中会出现这样的现像&#xff0c;就是 Spring5 内部代码打印的日志和我们自己的业务代码打印日志使用的不是统一日志实现&#xff0c;尤其是在项目启动的时候&#xff0c;Spring5 的内部日志使用的是 log4j2&#xff0c;但是业务代码打印使用的可能是 …...

python爬虫实践

两个python程序的小实验&#xff08;附带源码&#xff09; 题目1 爬取http://www.gaosan.com/gaokao/196075.html 中国大学排名&#xff0c;并输出。提示&#xff1a;使用requests库获取页面的基本操作获取该页面&#xff0c;运用BeautifulSoup解析该页面绑定对象soup&#x…...

【前端面试】七、算法-数组展平

目录 1.判断数组 2.二维数组展平 3.多维数组展平 1.判断数组 // 判断数组console.log([].constructor Array);console.log( Array.isArray([]));console.log( [] instanceof Array);console.log(Object.prototype.toString.call([]) [object Array]); 2.二维数组展平 const…...

Laravel php框架与Yii php 框架的优缺点

Laravel和Yii都是流行的PHP框架&#xff0c;它们各自具有独特的优点和缺点。以下是对这两个框架优缺点的详细分析&#xff1a; Laravel PHP框架的优缺点 优点 1、设计思想先进&#xff1a;Laravel的设计思想非常先进&#xff0c;非常适合应用各种开发模式&#xff0c;如TDD&…...

使用 addRouteMiddleware 动态添加中间

title: 使用 addRouteMiddleware 动态添加中间 date: 2024/8/4 updated: 2024/8/4 author: cmdragon excerpt: 摘要&#xff1a;文章介绍了Nuxt3中addRouteMiddleware的使用方法&#xff0c;该功能允许开发者动态添加路由中间件&#xff0c;以实现诸如权限检查、动态重定向及…...

Zookeeper未授权访问漏洞

Zookeeper未授权访问漏洞 Zookeeper是分布式协同管理工具&#xff0c;常用来管理系统配置信息&#xff0c;提供分布式协同服务。Zookeeper的默认开放端口是 2181。Zookeeper安装部署之后默认情况下不需要任何身份验证&#xff0c;造成攻击者可以远程利用Zookeeper&#xff0c;…...

【JavaEE】定时器

目录 前言 什么是定时器 如何使用java中的定时器 实现计时器 实现MyTimeTask类 Time类中存储任务的数据结构 实现Timer中的schedule方法 实现MyTimer中的构造方法 处理构造方法中出现的线程安全问题 完整代码 考虑在限时等待wait中能否用sleep替换 能否用PriorityBlo…...

2024带你轻松玩转Parallels Desktop19虚拟机!让你在Mac电脑上运行Windows系统

大家好&#xff0c;今天我要给大家安利一款神奇的软件——Parallels Desktop 19虚拟机。这款软件不仅可以让你在Mac电脑上运行Windows系统&#xff0c;还能轻松切换两个操作系统之间的文件和应用程序&#xff0c;让你的工作效率翻倍&#xff01; 让我来介绍一下Parallels Desk…...

【算法】递归实现二分查找(优化)以及非递归实现二分查找

递归实现二分查找 思路分析 1.首先确定该数组中间的下标 mid (left right) / 2; 2.然后让需要查找的数 findVal 和 arr[mid] 比较 findVal > arr[mid]&#xff0c;说明要查找的数在 arr[mid] 右边&#xff0c;需要向右递归findVal < arr[mid]&#xff0c;说明要查…...

CDN 是什么?

CDN是一种分布式网络服务&#xff0c;通过将内容存储在分布式的服务器上&#xff0c;使用户可以从距离较近的服务器获取所需的内容&#xff0c;从而加速互联网上的内容传输。 就近访问&#xff1a;CDN 在全球范围内部署了多个服务器节点&#xff0c;用户的请求会被路由到距离最…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

黑马Mybatis

Mybatis 表现层&#xff1a;页面展示 业务层&#xff1a;逻辑处理 持久层&#xff1a;持久数据化保存 在这里插入图片描述 Mybatis快速入门 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6501c2109c4442118ceb6014725e48e4.png //logback.xml <?xml ver…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log&#xff0c;共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题&#xff0c;不能使用ELK只能使用…...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

postgresql|数据库|只读用户的创建和删除(备忘)

CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...