Redis与关系型数据库的同步问题

Redis是一个高性能的key-value数据库。 redis的出现,很大程度补偿了memcached这类key-value存储的不足,在部分场合可以对关系数据库起到很好的补充作用。它提供了Python,Ruby,Erlang,PHP客户端,使用很方便。

按照我们一般的使用Redis的场景应该是这样的:

也就是说:我们会先去Redis中判断数据是否存在,如果存在,则直接返回缓存好的数据。而如果不存在的话,就会去数据库中,读取数据,并把数据缓存到redis中。适用场合:如果数据量比较大,但不是经常更新的情况(比如用户排行)

而第二种Redis的使用,跟第一种的情况完成不同,具体的情况请看:

 

这里我们会先去Redis中判断数据是否存在,如果存在,则直接更新对应的数据(吧对应更新过的key记录下来,比如也保存到redis中,key为save_update_keys),并把更新后的数据返回给页面,而如果不保存的话,就会去先更新数据库中的内容,然后把数据保存一份到Redis。后面的工作,后台会有相关机制把Redis中的save_update_keys存储的key,分别读取出来,找到对应的数据,更新到DB中。优点:主要目的是把Redis当做数据库使用,更新获取数据比DB块,非常适合大数据量的频繁变动(比如微博),缺点,对Redis的依赖很大,要做好宕机时的数据保存。

Redis和数据库同步问题

缓存充当数据库

比如说Session这种访问非常频繁的数据,就适合采用这种方案;当然了,既然没有涉及到数据库,那么也就不会存在一致性问题;

缓存充当数据库热点缓存

读操作

目前的读操作有个固定的套路,如下:

  1. 客户端请求服务器的时候,发现如果服务器的缓存中存在,则直接取服务器的;

  2. 如果缓存中不存在,则去请求数据库,并且将数据库计算出来的数据回填给缓存;

  3. 返回数据给客户端;

     

写操作

各种情况会导致数据库和缓存出现不一致的情况,这就是缓存和数据库的双写一致性问题;

目前缓存存在三种策略,分别是

  • Cache Aside 更新策略:同时更新缓存和数据库;

  • Read/Write Through 更新策略:先更新缓存,缓存负责同步更新数据库;

  • Write Behind Caching 更新策略:先更新缓存,缓存定时异步更新数据库;

 

三种策略各有优缺点,可以根据业务场景使用;

 

Cache Aside 更新策略

该策略大概的流程就是请求过来时先从缓存中取,如果命中缓存的话,则直接返回读取的数据;相反如果没有命中的话,接着会从数据库中成功获取到数据后,再去清除缓存中的数据;具体流程图如下:

 

 

但是以上在某些特殊的情况下是存在问题:

 

问题1:先更新数据库,后更新缓存

两个线程在高并发的情况下就会可能出现数据脏读的情况:

  1. 线程A执行写操作,成功更新数据库;

  2. 线程B同样执行和线程A一样的操作,但是在线程A执行更新缓存的过程中,线程B更新了新的数据库数据到缓存中;

  3. 线程A在线程B全部操作完成以后才将相对老的数据又更新到了缓存中;

 

 

问题2:先删除缓存,后更新数据库

同样的,在高并发场景下同样会出现脏读的情况:

  1. 线程A成功删除了缓存,等待更新数据库;

  2. 线程B进行读操作,由于此时缓存已经被删除了,因此线程B重新从数据库中获取老的数据并且更新到了缓存中;

  3. 线程A在线程B完成了整个的读操作以后,才更新数据库,此时缓存中的数据依旧是老的数据;

 

问题3:先更新数据库,后删除缓存

目前这是比较普遍的操作,即使它还是有可能会出现脏读的情况:

  1. 线程A进行读操作,此时正好没有命中缓存,接着请求数据库;

  2. 线程B进行写操作,在线程A没有从数据库中获取到数据之前,把数据写入到数据库中,并且还成功删除了缓存;

  3. 线程A在线程B完成了整个的写操作以后,才将相对老的数据更新到缓存中;

但是以上的情况比较不会出现,这是因为上述情况需要满足线程A的读操作要慢于线程B的写操作,但是在现实过程中,读操作通常都是要快于写操作得多的,但是为了避免发生以上的情况,通常都是要给缓存加上一个过期的时间

 

 

但是设想一下,如果上面的删除缓存失败了怎么办呢,这样显然会导致数据脏读的情况,我觉得方案如下:

  1. 设置缓存的过期时间(必须要做);

  2. 提供一个保障重试机制,将哪些删除失败的key提供给消息队列去消费;

  1. 从消息队列取出这些key再次进行删除,失败再次加入到消息队列中,超过一定次数以上则人工介入;

但是以上情况需要在业务代码中进行操作,显然得需要进行解耦;

目前我们公司就是使用该方案,具体过程为在更新数据库数据的时候,数据库会以binlog日志的形式保存下来,通过canal开源软件将binlog解析成程序语言可以解析的地步,接着订阅程序获取到这些数据以后,尝试删除缓存操作,如果操作失败的话,则将其加入到消息队列中,重复消费,当删除操作的失败次数到达一定的次数以后,还是得人工介入。

 

 

Read/Write Through 更新策略

该模式下,程序只需要维护缓存即可,数据库的同步工作交由缓存来同步更新;

该策略具体又分为两种:

  1. Read Through:在查询的过程中更新缓存;

  2. Write Through:在写操作的过程中如果命中缓存,则直接更新缓存,数据库则由缓存自己同步去更新;

 

 

Write Behind Caching 更新策略

该策略只更新缓存,不会立马更新数据库,只会在一定的时间异步的批量去操作数据库;这样的好处在于直接操作缓存,效率极高,并且操作数据是异步的,还可以将多次的操作数据库语句合并到一个事务中一起提交,因此效率很客观;

但是,该策略没有办法做到数据强一致性,并且实现逻辑相对是比较复杂的,因为它需要确认哪些是需要更新到数据库的,哪些是仅仅想要存储在缓存中的;

 

 

比较

目前通常使用的是第一种策略中的先更新数据库,后更新缓存;其他的相较比起来实现都比较复杂;

最后想说的是,缓存本来就是为了牺牲强一致性来提高性能的,所以肯定会存在一定的延迟时间,我们只需要保证最终的数据一致性即可;

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

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

相关文章

.NET 实现并行的几种方式(一)

好久没有更新了,今天来一篇,算是《同步与异步》系列的开篇吧,加油,坚持下去(PS:越来越懒了)。 一、Thread 利用Thread 可以直接创建和控制线程,在我的认知里它是最古老的技术了。因为out了、所…

REVERSE-PRACTICE-BUUCTF-32

REVERSE-PRACTICE-BUUCTF-32[第四章 CTF之APK章]数字壳的传说[第五章 CTF之RE章]Hello, RE[第五章 CTF之RE章]BabyAlgorithm[第五章 CTF之RE章]BabyConst[第五章 CTF之RE章]BabyLib[第五章 CTF之RE章]easy_rust[第五章 CTF之RE章]easy_go[第五章 CTF之RE章]easy_mfc[第四章 CTF…

.NET 实现并行的几种方式(二)

本随笔续接:.NET 实现并行的几种方式(一) 四、Task 3)Task.NET 4.5 中的简易方式 在上篇随笔中,两个Demo使用的是 .NET 4.0 中的方式,代码写起来略显麻烦,这不 .NET 4.5提供了更加简洁的方…

redis 和 数据库mysql之间的关系

https://www.zhihu.com/question/20734566 https://www.zhihu.com/question/19660689 http://blog.csdn.net/Ideality_hunter/article/details/77621643 redis和mysql要根据具体业务场景去选型 mysql:数据放在磁盘redis:数据放在内存 redis适合放一些…

GPS/轨迹追踪、轨迹回放、围栏控制

折腾一个多月终于弄完了这个项目,起初都未曾接触GPS/轨迹追踪、轨迹回放、圈划围栏...等一些在百度地图或者Googel地图操作的一些业务,后端的业务相对来说简单点 cas单点登录,mongdb灵活的数据存储方式,ActiveMQ消息推送、Redis存储... 这篇…

C#.NET Thread多线程并发编程学习与常见面试题解析-1、Thread使用与控制基础

前言:因为平时挺少用到多线程的,写游戏时都在用协程,至于协程那是另一个话题了,除了第一次学习多线程时和以前某个小项目有过就挺少有接触了,最近准备面试又怕被问的深入,所以就赶紧补补多线程基础。网上已…

PWN-PRACTICE-BUUCTF-22

PWN-PRACTICE-BUUCTF-22hitcontraining_unlinkpicoctf_2018_leak_mesuctf_2018_basic pwnaxb_2019_brop64hitcontraining_unlink unlink,参考:[BUUCTF]PWN——hitcontraining_unlink # -*- coding:utf-8 -*- from pwn import * #ioprocess("./bam…

javaee, javaweb和javase的区别以及各自的知识体系

JavaSE Java SE 以前称为 J2SE。它允许开发和部署在桌面、服务器、嵌入式环境和实时环境中使用的 Java 应用程序。Java SE 包含了支持 Java Web 服务开发的类,并为 Java Platform,Enterprise Edition(Java EE)提供基础。 JavaE…

.NET 实现并行的几种方式(三)

在前两篇随笔中,先后介绍了 Thread 、ThreadPool 、IAsyncResult (即 APM系列) 、Task 、TPL (Task Parallel Library)。 写到这些笔者突然意识到 还有一个EMP系列没有写,在这里补充一下: 六、 EAP 、EAP中的典型代表是 WebClient: EAP系…

.NET异步编程之新利器——Task与Await、Async

一. FrameWork 4.0之前的线程世界     在.NET FrameWork 4.0之前,如果我们使用线程。一般有以下几种方式: 使用System.Threading.Thread 类,调用实例方法Start()开启一个新线程,调用Abort()方法来提前终止线程。使用System.T…

对比MS Test与NUnit Test框架

前言: 项目中进行Unit Test时,肯定会用到框架,因为这样能够更快捷、方便的进行测试。 .Net环境下的测试框架非常多,在这里只是对MS Test和NUnit Test进行一下比较, 因为这两个框架用的较多,也有大虾想过…

PWN-PRACTICE-BUUCTF-25

PWN-PRACTICE-BUUCTF-25wustctf2020_name_your_catciscn_2019_final_2mrctf2020_shellcode_revengezctf2016_note2wustctf2020_name_your_cat 通过数组越界覆写返回地址为后门shell的地址 from pwn import * #ioprocess(./wustctf2020_name_your_cat) ioremote(node4.buuoj.c…

使用 Github Pages 和 Hexo 搭建自己的独立博客【超级详细的小白教程】

2022-01-25 更新:博客新地址:https://www.itbob.cn/ 欢迎关注我的专栏:《个人博客搭建:HexoGithub Pages》,从搭建到美化一条龙,帮你解决 Hexo 常见问题! 推荐阅读:《Hexo 博客优化…

windows程序消息机制(Winform界面更新有关)

1. Windows程序消息机制 Windows GUI程序是基于消息机制的,有个主线程维护着消息泵。这个消息泵让windows程序生生不息。 Windows程序有个消息队列,窗体上的所有消息是这个队列里面消息的最主要来源。这里的While循环使用了GetMessage() 这个方法,这是个阻塞方法,也…

最新主流 Markdown 编辑器推荐

Markdown ,2004年由 John Gruberis 设计和开发,是一种可以使用普通文本编辑器编写的标记语言,通过简单的标记语法,它可以使普通文本内容具有一定的格式,以下将介绍目前比较流行的一些 Markdown 编辑器(排名…

PWN-PRACTICE-BUUCTF-27

PWN-PRACTICE-BUUCTF-27starctf_2019_babyshellpicoctf_2018_buffer overflow 0gyctf_2020_signinbjdctf_2020_YDSneedGrirlfriendstarctf_2019_babyshell 用\x00绕过shellcode检测,call rdx 跳转过去执行汇编代码,一个\x00必执行失败 于是需要找一条机…

敏捷开发之Scrum扫盲,及敏捷开发中XP与SCRUM的区别

敏捷开发之Scrum扫盲篇 现在敏捷开发是越来越火了,人人都在谈敏捷,人人都在学习Scrum和XP... 为了不落后他人,于是我也开始学习Scrum,今天主要是对我最近阅读的相关资料,根据自己的理解,用自己的话来讲述S…

有关不蒜子访问统计无法显示的解决方法

十月初,不蒜子统计失效了,如下图: 进入不蒜子官网看看: 问题来了,官网写着:因七牛强制过期『dn-lbstatics.qbox.me』域名,与客服沟通无果,只能更换域名到『busuanzi.ibruce.info』…

SCRUM与XP区别

敏捷开发 1、敏捷的含义 敏捷开发是一种以人为核心、迭代、增量的开发方法。在敏捷开发中,把一个大项目分为多个相互联系,可独立运行的小项目,并分别完成,在此过程中软件一直处于可使用状态。 上面提到3个关键词,下…

Java编写程序时出现警告:Resource leak: 'xxx' is never closed 解决方案

菜鸟新手,学校在教Java,作业里面要求我们编程实现运算一个简单的程序,程序写好了,却发现冒出一个警告,如下图: 百度了一下,意思是申明了名为a的数据输入扫描仪(Scanner&#xff09…