文章目录
- 一 缓存和数据库双写有什么问题
-
- 1.1 设置过期时间的策略
- 1.2 设置过期时间的问题
- 1.3 设置过期时间的例子
- 二 常规更新策略
-
- 2.1 先更新数据库 再更新缓存
- 2.2 先删除缓存 再更新数据库
- 2.3 先更新数据库 再删除缓存
- 2.4 删除失败的问题
一 缓存和数据库双写有什么问题
想要知道如何解决问题,必须先知道问题是什么存和数据库双写会有什么问题p>
1.1 设置过期时间的策略
一般来说,我们会选择设置过期时间的策略来保证最终一致性。怎么保证r> 实际上就是一种片“读写分离”,我的读操作全部从缓存里读,写操作则直接写进数据库,也就是说,缓存里的值没过期之前是不会更新的
1.2 设置过期时间的问题
那有什么问题有问题了,那些在缓存中的值在没过期的这段时间里不管数据库改了多少次,缓存里的对应值都不会变,这确实保证了用户每次读到的值不会左右横跳,但是数据库里的值可是真真的发生了改变,这对于那种需要掌握实时数据的业务就不适用
1.3 设置过期时间的例子
股民李四在某软件上炒股,10点之前他买的股票疯涨,可是程序员张三在前一天误操作了一波,使用了上述策略,在设置过期时间的时候,猝死倒在了键盘上,原本几毫秒的过期时间被多添了n个0。结果呢在咬牙熬过了前一天的绿油油之后,准时在10点打开软件定睛那么一瞧,仍然是那么绿(因为显示的是昨天的数据),绷不住了,含泪怒卖一波,以为自己血亏。结果显示到账99999…,突然大悲转大喜,以为软件出错了,血压也飙升,但不敢声张,只能强行平复心情。10点之后,该股票又暴跌,跌的惨绝人寰,李四下午打开一看,我靠,大涨(因为看到的是10点前的数据),心情顿时又不好了,血压再次飙升,由于年事已高,一个没崩住,被送走了…
由此可见,这种实时业务场景就不适合设置这种策略,要是好事变坏事,徒增悲伤!
二 常规更新策略
不是说设置过期时间的策略一定不好,一些对实时读取要求不明显的业务就可以用,但是我们的业务那么多,那么复杂,只有一个策略必然是无法满足所有的要求,那就轮到我们好好根据不同场景选择不同的更新策略了
2.1 先更新数据库 再更新缓存
- 当我们需要写操作时,先更新数据库,再把缓存里对应的值更新
- 有问题吗有!从线程安全的角度来讲,假设有请求A和请求B同时进行进行更新:
- A更新数据库
- B更新数据库
- B更新缓存
- A更新缓存
- 例子:张三和李四先后追一个姑娘,张三决定在姑娘的生日蛋糕上放上戒指并署名,李四知道了这件事,决定抄袭他的想法并取而代之,于是他们进行了如下操作
- 张三买了一枚戒指,写好了情话
- 李四也买了一枚戒指,写好了情话
- 结果李四先张三一步把戒指和情书放到蛋糕上了
- 张三准备放的时候一看,决定姑娘面前不讲兄弟情面!丢掉了李四送的戒指和情话放上了自己的
- 于是原本李四的计划就落空了,没有成功摘到张三的桃子,姑娘还是被张三追到手了,这是有问题的,毕竟谁也不想当李四吧
- 从业务角度来讲,也是有问题的,如果没有读操作,每次更新数据库后都要更新缓存不是会造成很大的资源浪费吗
- 例子:
- 同样的场景,我们知道姑娘直到吃蛋糕之前都不会打开蛋糕盒看的,可是张三和李四较上劲了
- 当张三买了戒指,写好情话就去放一下
- 李四买了戒指,写好情话也去放一下,丢掉了张三的东西
- 张三想想自己送的东西不完美,重新选戒指,写情书,重新放
- 李四也一样
- 就这么来来回回n次,浪费了整整一天选礼物写情书,结果最后姑娘只在打开的时候看到一封信一枚戒指,您说亏不亏
2.2 先删除缓存 再更新数据库
- 当进行写操作时,把对应的缓存删了,再更新数据库
- 同样的,有问题吗有问题,同样在多请求场景下,一个写,一个读:
- A准备更新数据库,删除缓存
- B准备读数据库,找不到缓存,到mysql里把旧值找出来,不仅放进缓存,还读到了旧值
- A把新值写入数据库
- 例子:孔乙己去买酒,酒馆的黑板(缓存)和账簿(数据库)里都记着他的账单
- 孔乙己排出9文大钱,大喊还账
- 小二刚擦掉黑板上的欠账,这时候老板正好走过来问他孔乙己还欠多少酒钱
- 小二也是个愣的,翻看账单回答还差9文大钱,老板就在黑板上又挂上了孔乙己欠九文大钱的字样
- 然后小二才把账单上的欠账划掉,记录不欠账了
- 但是老板和酒店客人都不知道,然后因为此事狠狠打趣孔乙己
- 孔乙己也糊涂,真以为自己还欠着账,嘴里喃喃地说着什么“读书人的事,不叫欠账,读书人的事”,然后就是一顿之乎者也的晦涩难懂的话,店里店外顿时充满了快活的气息
- 本来孔乙己好不容易腰板挺直一回,这种情况又导致他被嘲笑,划向悲剧深渊,不难受吗
- 那怎么解决呢可以用双删延时策略:
- 先删除缓存
- 写数据库
- 休眠一秒再删缓存
- 例子:
- 同样的场景,老板问完欠账写到黑板
- 小二才把账簿的账划掉
- 等老板写完没开始打趣孔乙己之前,小二又把黑板擦掉
- 老板刚想打趣,发现黑板上没有账了,明白孔乙己还了欠账
- 一场闹剧被避免了
- 因此,这个等待多长时间的时机很重要,不一定是一秒,要根据具体场景来
- 那如果这么做,每次都要等待一段时间,吞吐量不就降低了我们可以另起线程来做删除操作,而不是等待
2.3 先更新数据库 再删除缓存
- 这个思路也很简单,每次更新完数据后再让缓存失效
- 会有问题吗,下面这种情况就会出问题:
- 某一个值刚好失效
- A向数据库修改该值
- B从数据库读到旧值
- A删除缓存
- B将旧值写入缓存
- 但在实际中,第四步和第五步是很难调换顺序的,为什么
- 因为读比写快!
- 等到A修改完该值准备删除缓存的时候,B早就读到旧值并写入缓存了
- 因此绝大多数情况都是第四步先发生,第五步再发生
2.4 删除失败的问题
实际上,在2.2和2.3中我们都默认删除缓存一定成功,但是这个操作是有可能失败的,怎么解决失败就多删几次呗!使用重试机制,每次删除失败就重试,这里不深挖,有兴趣的同学自己可以探究一下
文章知识点与官方知识档案匹配,可进一步学习相关知识Java技能树使用JDBC操作数据库数据库操作93012 人正在系统学习中
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!