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,一经查实,立即删除!

相关文章

Delphi各个版本和发展历史

Delphi,是Windows平台下著名的快速应用程序开发工具(Rapid Application Development,简称RAD)。它的前身,即是DOS时代盛行一时的“BorlandTurbo Pascal”,最早的版本由美国Borland(宝兰)公司于1995年开发。…

PWN-PRACTICE-BUUCTF-12

PWN-PRACTICE-BUUCTF-12cmcc_simpleroppicoctf_2018_buffer overflow 2babyfengshui_33c3_2016xdctf2015_pwn200cmcc_simplerop 静态编译的32位elf,找一个"int 80h"执行系统调用 前提是利用栈溢出读入字符串"/bin/sh\x00",然后找po…

C# 基础——C#特性

.NET C# Web开发学习之路——C#特性 C#历史办版本及特性 语言版本发布时间.NET Framework要求Visual版本C# 1.02002.1.NET Framework 1.0Visual Studio .NET 2002C# 1.1\1.22003.4.NET Framework 1.1Visual Studio .NET 2003C# 2.02005.11.NET Framework 2.0Visual Studio 20…

PWN-PRACTICE-BUUCTF-13

PWN-PRACTICE-BUUCTF-13[ZJCTF 2019]Logininndy_ropmrctf2020_shellcodejarvisoj_level1[ZJCTF 2019]Login 参考:ZJCTF 2019 Pwn from pwn import * ioremote(node4.buuoj.cn,27513) #io process("./login") shell 0x400e88 io.recvuntil("user…

同步与异步系列之二 导读目录

.NET 同步与异步 之 EventWaitHandle(Event通知) (十三) .NET 同步与异步 之 Mutex (十二) .NET 同步与异步 之 线程安全的集合 (十一) .NET 同步与异步 之 警惕闭包(十) .NET 同…

PWN-PRACTICE-BUUCTF-14

PWN-PRACTICE-BUUCTF-14bbys_tu_2016ciscn_2019_n_3roarctf_2019_easy_pwngyctf_2020_borrowstackbbys_tu_2016 栈溢出,覆盖eip到printFlag函数 from pwn import * #ioprocess(./bbys_tu_2016) ioremote(node4.buuoj.cn,27817) elfELF(./bbys_tu_2016) #io.recvun…

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

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

PWN-PRACTICE-BUUCTF-15

PWN-PRACTICE-BUUCTF-15axb_2019_fmt32wustctf2020_getshell_2others_babystackpwnable_startaxb_2019_fmt32 格式化字符串漏洞 第一次打印出printf的真实地址,进而计算libc基地址,得到system真实地址 第二次修改got表,使printf的got指向sys…

PWN-PRACTICE-BUUCTF-16

PWN-PRACTICE-BUUCTF-16mrctf2020_easyoverflowhitcontraining_magicheapciscn_2019_s_40ctf_2017_babyheapmrctf2020_easyoverflow 覆盖main函数中的v5,使之为"n0t_r311y_f1g" from pwn import * rremote("node4.buuoj.cn",29521) payloada*…

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提供了更加简洁的方…

PWN-PRACTICE-BUUCTF-17

PWN-PRACTICE-BUUCTF-17hitcontraining_heapcreatorwustctf2020_closedciscn_2019_es_7hitcon2014_stkofhitcontraining_heapcreator 单字节溢出,修改下一个chunk的size,造成chunk overlap,实现任意地址读写 参考:buuctf hitcont…

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适合放一些…

PWN-PRACTICE-BUUCTF-18

PWN-PRACTICE-BUUCTF-18ciscn_2019_final_3ciscn_2019_s_9jarvisoj_level5pwnable_hacknoteciscn_2019_final_3 tcache dup 参考:[V&N2020 公开赛]easyTHeap ciscn_2019_final_3 ——heap中tcache的一些简单利用方法 # -*- coding:utf-8 -*- from pwn impor…

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

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

PWN-PRACTICE-BUUCTF-19

PWN-PRACTICE-BUUCTF-19hitcontraining_bambooboxpicoctf_2018_shellcodenpuctf_2020_easyheapcmcc_pwnme2hitcontraining_bamboobox unlink,参考:hitcontraining_bamboobox 堆技巧 unlink # -*- coding:utf-8 -*- from pwn import * #ioprocess("…

推荐使用:Vue.js ReactJS Angular 2 AngularJS

概要: 现在, Vue 还没有 React (由 Facebook 维护) 或者 Angular 2 (受到 Google 的支持) 流行。不过,许多开发者都已经转向 Vue 了。Laravel 社区也在考虑将它作为可选用的前端框架之一。 总之,Vue 给 React & Angular 的弊病提供了一道…

PWN-PRACTICE-BUUCTF-20

PWN-PRACTICE-BUUCTF-20actf_2019_babystackpicoctf_2018_can_you_gets_mepicoctf_2018_got_shellmrctf2020_easy_equationactf_2019_babystack 两次栈迁移 # -*- coding:utf-8 -*- from pwn import * #context.log_level"debug" #ioprocess("./ACTF_2019_bab…

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

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

PWN-PRACTICE-BUUCTF-21

PWN-PRACTICE-BUUCTF-21wdb_2018_2nd_easyfmtciscn_2019_es_1axb_2019_fmt64x_ctf_b0verfl0wwdb_2018_2nd_easyfmt 格式化字符串漏洞 第一次printf通过printf_got将printf的实际地址打印出来,计算libc基地址,得到system的实际地址 第二次printf通过prin…