缓存与数据库一致性保证

本文主要讨论这么几个问题:

(1)啥时候数据库和缓存中的数据会不一致

(2)不一致优化思路

(3)如何保证数据库与缓存的一致性

一、需求缘起

上一篇《缓存架构设计细节二三事》(点击查看)引起了广泛的讨论,其中有一个结论:当数据发生变化时,“先淘汰缓存,再修改数据库”这个点是大家讨论的最多的。

上篇文章得出这个结论的依据是,由于操作缓存与操作数据库不是原子的,非常有可能出现执行失败。


假设先写数据库,再淘汰缓存:第一步写数据库操作成功,第二步淘汰缓存失败,则会出现DB中是新数据,Cache中是旧数据,数据不一致【如上图:db中是新数据,cache中是旧数据】。


假设先淘汰缓存,再写数据库:第一步淘汰缓存成功,第二步写数据库失败,则只会引发一次Cache miss【如上图:cache中无数据,db中是旧数据】。

结论:先淘汰缓存,再写数据库。

引发大家热烈讨论的点是“先操作缓存,在写数据库成功之前,如果有读请求发生,可能导致旧数据入缓存,引发数据不一致”,这就是本文要讨论的主题。

二、为什么数据会不一致

回顾一下上一篇文章中对缓存、数据库进行读写操作的流程。

写流程:

(1)先淘汰cache

(2)再写db

读流程:

(1)先读cache,如果数据命中hit则返回

(2)如果数据未命中miss则读db

(3)将db中读取出来的数据入缓存

什么情况下可能出现缓存和数据库中数据不一致呢?


在分布式环境下,数据的读写都是并发的,上游有多个应用,通过一个服务的多个部署(为了保证可用性,一定是部署多份的),对同一个数据进行读写,在数据库层面并发的读写并不能保证完成顺序,也就是说后发出的读请求很可能先完成(读出脏数据):

(a)发生了写请求A,A的第一步淘汰了cache(如上图中的1)

(b)A的第二步写数据库,发出修改请求(如上图中的2)

(c)发生了读请求B,B的第一步读取cache,发现cache中是空的(如上图中的步骤3)

(d)B的第二步读取数据库,发出读取请求,此时A的第二步写数据还没完成,读出了一个脏数据放入cache(如上图中的步骤4)

即在数据库层面,后发出的请求4比先发出的请求2先完成了,读出了脏数据,脏数据又入了缓存,缓存与数据库中的数据不一致出现了

三、不一致优化思路

能否做到先发出的请求一定先执行完成呢?常见的思路是“串行化”,今天将和大家一起探讨“串行化”这个点。

先一起细看一下,在一个服务中,并发的多个读写SQL一般是怎么执行的


上图是一个service服务的上下游及服务内部详细展开,细节如下:

(1)service的上游是多个业务应用,上游发起请求对同一个数据并发的进行读写操作,上例中并发进行了一个uid=1的余额修改(写)操作与uid=1的余额查询(读)操作

(2)service的下游是数据库DB,假设只读写一个DB

(3)中间是服务层service,它又分为了这么几个部分

(3.1)最上层是任务队列

(3.2)中间是工作线程,每个工作线程完成实际的工作任务,典型的工作任务是通过数据库连接池读写数据库

(3.3)最下层是数据库连接池,所有的SQL语句都是通过数据库连接池发往数据库去执行的

工作线程的典型工作流是这样的:

void work_thread_routine(){

Task t = TaskQueue.pop(); // 获取任务

// 任务逻辑处理,生成sql语句

DBConnection c = CPool.GetDBConnection(); // 从DB连接池获取一个DB连接

c.execSQL(sql); // 通过DB连接执行sql语句

CPool.PutDBConnection(c); // 将DB连接放回DB连接池

}

 

提问:任务队列其实已经做了任务串行化的工作,能否保证任务不并发执行?

答:不行,因为

(1)1个服务有多个工作线程,串行弹出的任务会被并行执行

(2)1个服务有多个数据库连接,每个工作线程获取不同的数据库连接会在DB层面并发执行

提问:假设服务只部署一份,能否保证任务不并发执行?

答:不行,原因同上

提问:假设1个服务只有1条数据库连接,能否保证任务不并发执行?

答:不行,因为

(1)1个服务只有1条数据库连接,只能保证在一个服务器上的请求在数据库层面是串行执行的

(2)因为服务是分布式部署的,多个服务上的请求在数据库层面仍可能是并发执行的

提问:假设服务只部署一份,且1个服务只有1条连接,能否保证任务不并发执行?

答:可以,全局来看请求是串行执行的,吞吐量很低,并且服务无法保证可用性

完了,看似无望了,

1)任务队列不能保证串行化

2)单服务多数据库连接不能保证串行化

3)多服务单数据库连接不能保证串行化

4)单服务单数据库连接可能保证串行化,但吞吐量级低,且不能保证服务的可用性,几乎不可行,那是否还有解?

退一步想,其实不需要让全局的请求串行化,而只需要“让同一个数据的访问能串行化”就行。

在一个服务内,如何做到“让同一个数据的访问串行化”,只需要“让同一个数据的访问通过同一条DB连接执行”就行。

如何做到“让同一个数据的访问通过同一条DB连接执行”,只需要“在DB连接池层面稍微修改,按数据取连接即可”

获取DB连接的CPool.GetDBConnection()【返回任何一个可用DB连接】改为

CPool.GetDBConnection(longid)【返回id取模相关联的DB连接】

这个修改的好处是:

(1)简单,只需要修改DB连接池实现,以及DB连接获取处

(2)连接池的修改不需要关注业务,传入的id是什么含义连接池不关注,直接按照id取模返回DB连接即可

(3)可以适用多种业务场景,取用户数据业务传入user-id取连接,取订单数据业务传入order-id取连接即可

这样的话,就能够保证同一个数据例如uid在数据库层面的执行一定是串行的

稍等稍等,服务可是部署了很多份的,上述方案只能保证同一个数据在一个服务上的访问,在DB层面的执行是串行化的,实际上服务是分布式部署的,在全局范围内的访问仍是并行的,怎么解决呢?能不能做到同一个数据的访问一定落到同一个服务呢?

四、能否做到同一个数据的访问落在同一个服务上?

上面分析了服务层service的上下游及内部结构,再一起看一下应用层上下游及内部结构


上图是一个业务应用的上下游及服务内部详细展开,细节如下:

(1)业务应用的上游不确定是啥,可能是直接是http请求,可能也是一个服务的上游调用

(2)业务应用的下游是多个服务service

(3)中间是业务应用,它又分为了这么几个部分

(3.1)最上层是任务队列【或许web-server例如tomcat帮你干了这个事情了】

(3.2)中间是工作线程【或许web-server的工作线程或者cgi工作线程帮你干了线程分派这个事情了】,每个工作线程完成实际的业务任务,典型的工作任务是通过服务连接池进行RPC调用

(3.3)最下层是服务连接池,所有的RPC调用都是通过服务连接池往下游服务去发包执行的

工作线程的典型工作流是这样的:

voidwork_thread_routine(){

Task t = TaskQueue.pop(); // 获取任务

// 任务逻辑处理,组成一个网络包packet,调用下游RPC接口

ServiceConnection c = CPool.GetServiceConnection(); // 从Service连接池获取一个Service连接

c.Send(packet); // 通过Service连接发送报文执行RPC请求

CPool.PutServiceConnection(c); // 将Service连接放回Service连接池

}

似曾相识吧?没错,只要对服务连接池进行少量改动:

获取Service连接的CPool.GetServiceConnection()【返回任何一个可用Service连接】改为

CPool.GetServiceConnection(longid)【返回id取模相关联的Service连接】

这样的话,就能够保证同一个数据例如uid的请求落到同一个服务Service上。

五、总结

由于数据库层面的读写并发,引发的数据库与缓存数据不一致的问题(本质是后发生的读请求先返回了),可能通过两个小的改动解决:

(1)修改服务Service连接池,id取模选取服务连接,能够保证同一个数据的读写都落在同一个后端服务上

(2)修改数据库DB连接池,id取模选取DB连接,能够保证同一个数据的读写在数据库层面是串行的

六、遗留问题

提问:取模访问服务是否会影响服务的可用性?

答:不会,当有下游服务挂掉的时候,服务连接池能够检测到连接的可用性,取模时要把不可用的服务连接排除掉。

提问:取模访问服务与 取模访问DB,是否会影响各连接上请求的负载均衡?

答:不会,只要数据访问id是均衡的,从全局来看,由id取模获取各连接的概率也是均等的,即负载是均衡的。

缓存与数据库一致性保证相关推荐

  1. mysql缓存一致性,缓存与数据库一致性保证

    全是干货!本文主要讨论这么几个问题: (1)啥时候数据库和缓存中的数据会不一致 (2)不一致优化思路 (3)如何保证数据库与缓存的一致性 一.需求缘起 当数据发生变化时,"先淘汰缓存,再修改 ...

  2. 缓存和数据库如何保证一致性

    缓存和数据库如何保证一致性 1. 问题提出 正常情况下,我们从客户端发起请求从数据库拿到数据 当客户端请求变多的时候,我们就会引入缓存来提高接口的性能 但是,由于请求的并发性和数据处理的顺序性,更新数 ...

  3. 如何保证缓存和数据库一致性?

    如何保证缓存和数据库一致性? 引入缓存提高性能 缓存利用率和一致性问题 并发引起的一致性问题 删除缓存可以保证一致性吗? 如何保证两步都执行? 主从延迟和延迟双删问题 可以做到强一致性吗? 总结 如何 ...

  4. 好文推荐 | 缓存与数据库一致性问题深度剖析 (修订)

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"书",获取 来源:后端技术漫谈 当我们在做数据库与缓存数据同步时,究竟更新缓存,还是删 ...

  5. 缓存和数据库一致性问题,看这篇就够了

    作者 | Magic Kaito 来源 | 水滴与银弹 如何保证缓存和数据库一致性,这是一个老生常谈的话题了. 但很多人对这个问题,依旧有很多疑惑: 到底是更新缓存还是删缓存? 到底选择先更新数据库, ...

  6. Redis 缓存实战——缓存、数据库一致性问题分析与解决方案

    引言 缓存与数据库一致性的问题自从出现了缓存概念后就一直被提及,它是一个缓存方案的先天缺陷,只要存在缓存,就势必会讨论缓存与数据库一致性的问题. 一致性问题还广泛存在于各种分布式存储场景中,如主从一致 ...

  7. 缓存和数据库一致性问题

    文章目录 1. 缓存和数据库一致性问题 2. 三个经典的缓存模式 2.1 Cache-Aside 2.1.1 读缓存 2.1.2 写缓存 2.1.3 延迟双删 2.1.4 如何确保操作缓存和操作数据库 ...

  8. REDIS11_缓存和数据库一致性如何保证、解决方案、提供Canel解决数据一致性问题

    文章目录 ①. 缓存和数据库双写一致保证 ②. 缓存数据一致性-解决方案 ③. 缓存数据一致性-解决-Canal ①. 缓存和数据库双写一致保证 ①. 只要用缓存,就可能会涉及到缓存与数据库双存储双写 ...

  9. 就是这么应对面试官的缓存与数据库一致性问题?

    在日常开发过程中,对于Redis和MySQL的使用想必是不陌生的.当面对一些较为简单的使用场景时,貌似也不会很困难.但是在涉及到缓存和数据库之间的数据同步问题的时候,一个考虑不慎,也许就该准备简历了. ...

最新文章

  1. 关于path.join,path.resolve的详细介绍
  2. 《HelloGitHub》第 19 期
  3. python 实现redis订阅发布功能
  4. css背景上能添加文字,CSS3怎么为文字添加背景?
  5. opengl编程指南第8版源码编译详细说明
  6. 有奶瓶的linux系统,用U盘启动BEINI(奶瓶)系统
  7. python计算两字符串中的位置_python – 计算两个字符串之间距离的算法
  8. 报错,Exception: Required request body is missing: public org.springframework.ui.ModelMap cn.yihuazt.co
  9. python将图片导出数据库_python 将图片转换为base64编码转储进数据库
  10. Kubernetes学习总结(15)—— Kubernetes 实战之部署 Mysql 集群
  11. Android 获取网络错误
  12. 中国大数据市场规模分析及预测
  13. oracle 启动报错03113,oracle数据库无法启动,总报ora-03113错误
  14. 空间解析几何 | 经典例题、李林880例题
  15. SQL Server 添加Northwind数据库
  16. Golang学习资源整理
  17. android开启wifi热点命令,Win7共享WIFI热点让Android手机上网
  18. 用js实现一个自动阅读的代码
  19. wgs84坐标格式转换度分秒_WGS84经纬度坐标转换到西安80高斯投影坐标。
  20. Go组队学习 9.异常处理

热门文章

  1. mysql 加减乘除取小数点_mysql加减乘除
  2. STM32 ADC模数转换
  3. 使用LD链接时候文件的顺序
  4. [计算机网络] - TCP半连接队列和全连接队列
  5. 51单片机dds信号发生器 扫频 c语言,基于AT89C51单片机和DDS器件实现频率特征测试仪的设计...
  6. mysql时间间隔年份_MySQL DATEDIFF函数获取两个日期的时间间隔的方法
  7. 卸载centos7自带mysql_centos7完全卸载mysql
  8. 【重难点】【Redis 03】缓存雪崩、缓存穿透、缓存击穿、Redis 的内存过期策略、并发读写和双写
  9. 『嗨威说』数据结构 - 第七章学习内容小结
  10. [转]将西部数据 My Passport Wireless 移动存储连接到任何支持的云存储上