8张图带你分析Redis与MySQL数据一致性问题

InfoQ 2020-11-09 22:19:06
Mysql 分析 redis 带你 数据一致性


{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"前言"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"},{"type":"strong"}],"text":"原创公众号:bigsai"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"对于Web来说,用户量和访问量增加一定程度上推动项目技术和架构的更迭和进步。可能会有以下的一些状况:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"页面并发量和访问量并不多,MySQL"},{"type":"codeinline","content":[{"type":"text","text":"足以支撑"}]},{"type":"text","text":"自己逻辑业务的发展。那么其实可以不加缓存。最多对静态页面进行缓存即可。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"页面的并发量显著增多,数据库有些压力,并且有些数据更新频率较低"},{"type":"codeinline","content":[{"type":"text","text":"反复被查询"}]},{"type":"text","text":"或者查询速度"},{"type":"codeinline","content":[{"type":"text","text":"较慢"}]},{"type":"text","text":"。那么就可以考虑使用缓存技术优化。对高命中的对象存到key-value形式的Redis中,那么,如果数据被命中,那么可以不经过效率很低的db。从高效的redis中查找到数据。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"当然,可能还会遇到其他问题,你还通过静态页面缓存页面、cdn加速、甚至负载均衡这些方法提高系统并发量。这里就不做介绍。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/85/85ed5751ed1bc4ec6e44e5413343d1f6.png","alt":"image-20201106173835116","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"缓存思想无处不在"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们从一个算法问题开始了解缓存的意义。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "},{"type":"text","marks":[{"type":"strong"}],"text":"问题1:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"输入一个数n(n<20),求"},{"type":"codeinline","content":[{"type":"text","text":"n!"}]},{"type":"text","text":";"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"分析1"},{"type":"text","text":": "}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"单单考虑算法,不考虑数值越界问题。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当然我们知道"},{"type":"codeinline","content":[{"type":"text","text":"n!=n * (n-1) * (n-2) * ... * 1= n * (n-1)!"}]},{"type":"text","text":";"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那么我们可以用一个递归函数解决问题。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"static long jiecheng(int n)\n{\n\tif(n==1||n==0)return 1;\n\telse {\n\t return n*jiecheng(n-1);\n\t}\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 这样每输入求一次需要执行"},{"type":"codeinline","content":[{"type":"text","text":"n"}]},{"type":"text","text":"次。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"问题2:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"输入t组数据(可能成百上千),每组一个xi(xi<20),求"},{"type":"codeinline","content":[{"type":"text","text":"xi!"}]},{"type":"text","text":";"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"分析2"},{"type":"text","text":":"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果使用"},{"type":"codeinline","content":[{"type":"text","text":"递归"}]},{"type":"text","text":",输入t组数据,每次输入为xi,那么每次都要执行次数为:"}]}]}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c3/c323b51171ffe5cc8b0d86ab2de5df4b.png","alt":null,"title":"","style":[{"key":"width","value":"50%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当每次输入的Xi过大或者t过大都会造成不小的负担!时间复杂度为"},{"type":"text","marks":[{"type":"strong"}],"text":"O(n2)"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那么能否换个思想的。没错、是"},{"type":"codeinline","content":[{"type":"text","text":"打表"}]},{"type":"text","text":"。打表常用于ACM算法中,常用于解决多组输入输出、图论搜索结果、路径储存问题。那么,对于这个求阶乘。我们只需要申请一个数组,按照编号从前往后将在需求的数存到数组中,后面再取得时候直接输出数组值就可以,思想很明确吧:"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import java.util.Scanner;\npublic class test {\npublic static void main(String[] args) {\n\t// TODO Auto-generated method stub\n\tScanner sc=new Scanner(System.in);\n\tint t=sc.nextInt();\n\tlong jiecheng[]=new long[21];\n\tjiecheng[0]=1;\n\tfor(int i=1;i<21;i++)\n\t{\n\t\tjiecheng[i]=jiecheng[i-1]*i;\n\t}\n for(int i=0;i"},{"type":"codeinline","content":[{"type":"text","text":"内存"}]},{"type":"text","text":"。我们知道大多数关系数据库是"},{"type":"codeinline","content":[{"type":"text","text":"基于硬盘读写"}]},{"type":"text","text":"的,其效率和资源有限,而redis是基于内存的,其读写速度差别差别很大。当并发过高关系数据库性能达到瓶颈时候,就可以策略性将常访问数据放到Redis提高系统吞吐和并发量。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"对于常用网站和场景,关系数据库主要可能慢在两个地方:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"读写IO性能较差"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一个数据可能通过较大量计算得到"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以使用缓存能够减少磁盘IO次数和关系数据库的计算次数。读取上速度快也从两个方面体现:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基于内存,读写较快"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用哈希算法直接定位结果不需要计算"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以对于像样的,有点规模的网站,缓存是很 "},{"type":"codeinline","content":[{"type":"text","text":"necessary"}]},{"type":"text","text":"的,而Redis无疑是最好的选择之一。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/16/16fd3d3966013f30c25e113f88a82358.png","alt":"image-20201106180929673","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"需要注意的问题"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"缓存使用不当会带来很多问题。所以需要对一些细节进行认真考量和设计。当然最难得数据一致性在下面单独分析。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"是否用缓存"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"项目不能为了用缓存而用缓存,缓存并一定适合所有场景,如果对"},{"type":"text","marks":[{"type":"strong"}],"text":"数据一致性要求极高"},{"type":"text","text":",又或者"},{"type":"text","marks":[{"type":"strong"}],"text":"数据频繁更改而查询并不多"},{"type":"text","text":",又或者根本没并发量的、查询简单的不一定需要缓存,还可能浪费资源使得项目变得臃肿难维护,并且使用redis缓存多多少少可能会遇到数据一致性问题需要考虑。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"缓存合理设计"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在设计缓存的时候,很可能会遇到多表查询,如果遇到多表查询缓存的键值对就需要合理考虑,是拆分还是合在一起?当然如果组合种类多但常出现的不多也可以直接缓存,具体的设计要根据项目业务需求来看,并没有一个非常绝对的标准。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"过期策略选择"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"缓存装的是相对热点和常用的数据,Redis资源也是有限,需要选择一个合理的策略让缓存过期删除。我们学过"},{"type":"codeinline","content":[{"type":"text","text":"操作系统"}]},{"type":"text","text":"也知道在计算机的缓存实现中有先进先出的算法("},{"type":"text","marks":[{"type":"strong"}],"text":"FIFO"},{"type":"text","text":");最近最少使用算法("},{"type":"text","marks":[{"type":"strong"}],"text":"LRU"},{"type":"text","text":");最佳淘汰算法("},{"type":"text","marks":[{"type":"strong"}],"text":"OPT"},{"type":"text","text":");最少访问页面算法("},{"type":"text","marks":[{"type":"strong"}],"text":"LFR"},{"type":"text","text":")等磁盘调度算法。设计Redis缓存时候也可以借鉴。根据时间来的FIFO是最好实现的。且Redis在"},{"type":"codeinline","content":[{"type":"text","text":"全局key"}]},{"type":"text","text":"支持过期策略。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"并且过期时间也要根据系统情况合理设置,如果硬件好点当前可以稍微久一点,但是过期时间过久或者过短可能都不太好,过短可能缓存命中率不高,而过久很可能造成很多冷门数据存储在Redis中不释放。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"数据一致性问题*"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面其实提到数据一致性问题。如果对一致性要求极高那么不建议使用缓存。下面稍微梳理一下缓存的数据。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在Redis缓存中经常会遇到数据一致性问题。对于一个缓存,下面罗列几种情况:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"读"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"read"}]},{"type":"text","text":":从Redis中读取,如果Redis中没有,那么就从MySQL中获取更新Redis缓存。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面流程图描述常规场景,没啥争议:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d2/d292899310d79d72393e1b70ef4c3adb.png","alt":"image-20201106184713215","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"写1:先更新数据库,再更新缓存(普通低并发)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/75/75c10073f2ab054943a313b8e6df7f5b.png","alt":"image-20201106184914749","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"更新数据库信息,再更新Redis缓存。这是常规做法,缓存基于数据库,取自数据库。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是其中可能遇到一些问题,例如上述如果更新缓存失败(宕机等其他状况),将会使得数据库和Redis数据不一致。"},{"type":"text","marks":[{"type":"strong"}],"text":"造成DB新数据,缓存旧数据"},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"写2:先删除缓存,再写入数据库(低并发优化)"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/01/014d75d59cd837b025f16a81466f3421.png","alt":"image-20201106184958339","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"解决的问题"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这种情况能够有效避免"},{"type":"text","marks":[{"type":"strong"}],"text":"写1"},{"type":"text","text":"中防止写入Redis失败的问题。将缓存删除进行更新。理想是让下次访问Redis为空去MySQL取得最新值到缓存中。但是这种情况仅限于低并发的场景中而不适用高并发场景。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"存在的问题"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"写2虽然能够"},{"type":"codeinline","content":[{"type":"text","text":"看似写入Redis异常的问题"}]},{"type":"text","text":"。看似较为好的解决方案但是在高并发的方案中其实还是有问题的。我们在"},{"type":"text","marks":[{"type":"strong"}],"text":"写1"},{"type":"text","text":"讨论过如果更新库成功,缓存更新失败会导致脏数据。我们理想是删除缓存让"},{"type":"codeinline","content":[{"type":"text","text":"下一个线程"}]},{"type":"text","text":"访问适合更新缓存。问题是:如果这"},{"type":"text","marks":[{"type":"strong"}],"text":"下一个线程来的太早、太巧"},{"type":"text","text":"了呢?"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/8e/8efd5b578bdb7e9a6d74c4518c2871f2.png","alt":"image-20201106191042265","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因为多线程你也不知道谁先谁后,谁快谁慢。如上图所示情况,将会出现Redis缓存数据和MySQL不一致。当然你可以对key进行"},{"type":"codeinline","content":[{"type":"text","text":"上锁"}]},{"type":"text","text":"。但是锁这种重量级的东西对并发功能影响太大,能不用锁就别用!上述情况就高并发下依然会造成"},{"type":"text","marks":[{"type":"strong"}],"text":"缓存是旧数据,DB是新数据"},{"type":"text","text":"。并且如果缓存没有过期这个问题会一直存在。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"写3:延时双删策略"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e9/e9ffc29b44cc93e241aee35b077bc0c9.png","alt":"image-20201106191310072","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这个就是延时双删策略,能过缓解在"},{"type":"text","marks":[{"type":"strong"}],"text":"写2"},{"type":"text","text":"中在更新MySQL过程中有读的线程进入造成Redis缓存与MySQL数据不一致。方法就是"},{"type":"text","marks":[{"type":"strong"}],"text":"删除缓存->更新缓存->延时(几百ms)(可异步)再次删除缓存"},{"type":"text","text":"。即使在更新缓存途中发生"},{"type":"text","marks":[{"type":"strong"}],"text":"写2"},{"type":"text","text":"的问题。造成数据不一致,但是延时(具体实间根据业务来,一般几百ms)再次删除也能很快的解决不一致。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是就写的方案其实还是有漏洞的,比如第二次删除错误、多写多读高并发情况下对MySQL访问的压力等等。当然你可以选择用MQ等消息队列异步解决。其实实际的解决很难顾及到万无一失,所以不少大佬在设计这一环节可能会因为一些纰漏会被喷。作为菜菜的笔者在这里就更不献丑了,各位大佬欢迎贡献你们的方案。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"写4:直接操作缓存,定期写入sql(适合高并发)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当有"},{"type":"codeinline","content":[{"type":"text","text":"一堆并发(写)"}]},{"type":"text","text":"扔过来的后,前面几个方案即使使用消息队列异步通信但也很难给用户一个舒适的体验。并且对大规模操作sql对系统也会造成不小的压力。所以还有一种方案就是直接操作缓存,将缓存定期写入sql。因为Redis这种非关系数据库又基于内存操作KV相比传统关系型要快很多。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/11/119a03bf70d0129245b3fe7fae14f146.png","alt":"image-20201106192531468","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面适用于高并发情况下业务设计,这个时候以Redis数据为主,MySQL数据为辅助。定期插入(好像数据备份库一样)。当然,这种高并发往往会因为业务对"},{"type":"codeinline","content":[{"type":"text","text":"读"}]},{"type":"text","text":"、"},{"type":"codeinline","content":[{"type":"text","text":"写"}]},{"type":"text","text":"的顺序等等可能有不同要求,可能还要借助"},{"type":"codeinline","content":[{"type":"text","text":"消息队列"}]},{"type":"text","text":"以及"},{"type":"codeinline","content":[{"type":"text","text":"锁"}]},{"type":"text","text":"完成针对业务上对数据和顺序可能会因为高并发、多线程带来的不确定性和不稳定性,提高业务可靠性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"总之,越是"},{"type":"codeinline","content":[{"type":"text","text":"高并发"}]},{"type":"text","text":"、越是对"},{"type":"codeinline","content":[{"type":"text","text":"数据一致性要求高"}]},{"type":"text","text":"的方案在数据一致性的设计方案需要"},{"type":"codeinline","content":[{"type":"text","text":"考虑和顾及"}]},{"type":"text","text":"的"},{"type":"codeinline","content":[{"type":"text","text":"越复杂、越多"}]},{"type":"text","text":"。上述也是笔者针对Redis数据一致性问题的学习和自我发散(胡扯)学习。如果有解释理解不合理或者还请各位大佬指正!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最后,感觉不错的话还请一键三连,欢迎关注原创公众号:「"},{"type":"text","marks":[{"type":"strong"}],"text":"bigsai"},{"type":"text","text":"」,在这里,不仅能学到知识和干货,还给你准备了很多进阶资料,回复「bigsai」口令即可领取!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ed/eded3adec06b5165335f9a0c9b897a1e.jpeg","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
版权声明
本文为[InfoQ]所创,转载请带上原文链接,感谢
https://xie.infoq.cn/article/45b71e537b123e62052d93f9e?utm_source=rss&utm_medium=article

  1. 【计算机网络 12(1),尚学堂马士兵Java视频教程
  2. 【程序猿历程,史上最全的Java面试题集锦在这里
  3. 【程序猿历程(1),Javaweb视频教程百度云
  4. Notes on MySQL 45 lectures (1-7)
  5. [computer network 12 (1), Shang Xuetang Ma soldier java video tutorial
  6. The most complete collection of Java interview questions in history is here
  7. [process of program ape (1), JavaWeb video tutorial, baidu cloud
  8. Notes on MySQL 45 lectures (1-7)
  9. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  10. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  11. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  12. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  13. 【递归,Java传智播客笔记
  14. [recursion, Java intelligence podcast notes
  15. [adhere to painting for 386 days] the beginning of spring of 24 solar terms
  16. K8S系列第八篇(Service、EndPoints以及高可用kubeadm部署)
  17. K8s Series Part 8 (service, endpoints and high availability kubeadm deployment)
  18. 【重识 HTML (3),350道Java面试真题分享
  19. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  20. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  21. [re recognize HTML (3) and share 350 real Java interview questions
  22. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  23. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  24. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  25. RPC 1: how to develop RPC framework from scratch
  26. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  27. RPC 1: how to develop RPC framework from scratch
  28. 一次性捋清楚吧,对乱糟糟的,Spring事务扩展机制
  29. 一文彻底弄懂如何选择抽象类还是接口,连续四年百度Java岗必问面试题
  30. Redis常用命令
  31. 一双拖鞋引发的血案,狂神说Java系列笔记
  32. 一、mysql基础安装
  33. 一位程序员的独白:尽管我一生坎坷,Java框架面试基础
  34. Clear it all at once. For the messy, spring transaction extension mechanism
  35. A thorough understanding of how to choose abstract classes or interfaces, baidu Java post must ask interview questions for four consecutive years
  36. Redis common commands
  37. A pair of slippers triggered the murder, crazy God said java series notes
  38. 1、 MySQL basic installation
  39. Monologue of a programmer: despite my ups and downs in my life, Java framework is the foundation of interview
  40. 【大厂面试】三面三问Spring循环依赖,请一定要把这篇看完(建议收藏)
  41. 一线互联网企业中,springboot入门项目
  42. 一篇文带你入门SSM框架Spring开发,帮你快速拿Offer
  43. 【面试资料】Java全集、微服务、大数据、数据结构与算法、机器学习知识最全总结,283页pdf
  44. 【leetcode刷题】24.数组中重复的数字——Java版
  45. 【leetcode刷题】23.对称二叉树——Java版
  46. 【leetcode刷题】22.二叉树的中序遍历——Java版
  47. 【leetcode刷题】21.三数之和——Java版
  48. 【leetcode刷题】20.最长回文子串——Java版
  49. 【leetcode刷题】19.回文链表——Java版
  50. 【leetcode刷题】18.反转链表——Java版
  51. 【leetcode刷题】17.相交链表——Java&python版
  52. 【leetcode刷题】16.环形链表——Java版
  53. 【leetcode刷题】15.汉明距离——Java版
  54. 【leetcode刷题】14.找到所有数组中消失的数字——Java版
  55. 【leetcode刷题】13.比特位计数——Java版
  56. oracle控制用户权限命令
  57. 三年Java开发,继阿里,鲁班二期Java架构师
  58. Oracle必须要启动的服务
  59. 万字长文!深入剖析HashMap,Java基础笔试题大全带答案
  60. 一问Kafka就心慌?我却凭着这份,图灵学院vip课程百度云