当前位置 : 主页 > 网络编程 > PHP >

数据库与缓存怎样做同步最好

来源:互联网 收集:自由互联 发布时间:2023-09-07
前言: 在读取与写入缓存方面大家都是这么做的:判断是否有缓存数据,无数据的话从数据库加载,若查出数据不为null,则写入缓存,再把数据返回调用方。 但是这里有一个问题需要分


前言:

           在读取与写入缓存方面大家都是这么做的:判断是否有缓存数据,无数据的话从数据库加载,若查出数据不为null,则写入缓存,再把数据返回调用方。

            但是这里有一个问题需要分析,缓存与数据库的同步,在更新完数据库后,是更新缓存还是删除缓存,还是先删缓存,再更新数据库。从理论上来说,设置过期时间是最终保持一致的解决方案。但是这不是最好的办法,在缓存有效期内或者高并发情况下,会很可能出现读取到的数据与缓存不一致的情况。

三种策略:

           1.先更新数据库,再更新缓存

           2.先删除缓存,再更新数据库

           3.先更新数据库,再删除缓存

           不会出现先更新缓存再更新数据库,如果更新数据库失败了,那就完了

1.先更新数据库,再更新缓存

        这套方案不太好

         a.线程安全角度

                 (1) 线程A更新了数据库

                 (2) 线程B更新了数据库

                 (3) 线程B更新了缓存

                 (4) 线程A更新了缓存

            这样缓存中就出现了错误的数据库

           b.业务场景

                 如果数据库写的场景比较多,读的场景比较少,就会出现数据库频繁更新,缓存频繁更新可能缓存根本就没有读,这样浪费性能

                   如果写入数据库的值,并不是直接写入缓存的,而是要经过一系列复杂的计算再写入缓存,那么每次写入数据库后,再次计算写入缓存,也是浪费性能的。删除缓存显得更为合适。

 2.先删缓存,再更新数据库

            高并发操作下,依旧会出现问题

            (1) A线程进行写操作,先删除缓存

            (2) B线程发现没有缓存,去数据库读取旧值

            (3) B线程将旧值写入缓存

            (4) A线程更新数据库

            这样,数据库中的数据,又是脏数据了,还有在数据库主从分离情况下,中从没来的及同步,结果把未同步的数据写入到了缓存。这怎么解决

          采用延时双删策略

redis.delKey(key);

db.update(Data);

new Thread(()->{Thread.sleep(1000);redis.delKey(key);}).start();

          在不影响程序响应的情况下,开一个线程去删除缓存,    至于是多少时间后删除,可以自己评估,不一定是1s。

          但是这种情况下,如果第二次删除缓存失败了怎么办?

3.先更新数据库,再删缓存

      这样也有问题

            (1) 缓存刚好失效

            (2) A查数据库得到一个旧值

            (3) B将新值写入数据库

            (4) B删除缓存

             (5) A将旧值写入缓存

           因为数据库的读比写快,所以这种概率比较低正常顺序应该是1 2 5 3 4,但是也是有可能发生的,也可能是查询出来值后,经过一系列计算,又写入缓存的。解决办法还是延时缓存双删,但是和2一样,删除失败了怎么办?

解决方案1

         

数据库与缓存怎样做同步最好_缓存

               1.更新数据库

               2.删除缓存失败

               3.将要删除的key发送到消息队列

               4.消费消息删除key,重试直到成功

 

解决方案2

数据库与缓存怎样做同步最好_数据库与缓存一致方案_02

           

(1)更新数据库数据
(2)数据库会将操作信息写入binlog日志当中
(3)订阅程序提取出所需要的数据以及key
(4)另起一段非业务代码,获得该信息
(5)尝试删除缓存操作,发现删除失败
(6)将这些信息发送至消息队列
(7)重新从消息队列中获得该数据,重试操作。

 

备注说明:上述的订阅binlog程序在mysql中有现成的中间件叫canal,可以完成订阅binlog日志的功能。至于oracle中,博主目前不知道有没有现成中间件可以使用。另外,重试机制,博主是采用的是消息队列的方式。如果对一致性要求不是很高,直接在程序中另起一个线程,每隔一段时间去重试即可,这些大家可以灵活自由发挥,只是提供一个思路。

 

 

参考文献:

​​分布式之数据库和缓存双写一致性方案解析​​

 

          

实时内容请关注微信公众号,公众号与博客同时更新:程序员星星

数据库与缓存怎样做同步最好_数据库_03

 

 

 

上一篇:上传组件Plupload的使用
下一篇:没有了
网友评论