| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 大数据 -> 如何解决缓存与数据库不一致? -> 正文阅读 |
|
[大数据]如何解决缓存与数据库不一致? |
目录 一、概要缓存跟数据库不一致,指的是缓存中的数据跟数据库的数据出现了不一致,即其中一方存在脏数据的现象。需要注意的是,只有在对同一条数据并发读写的时候,才可能会出现这种问题。
下面我们详细分析常见的发生缓存与数据库不一致的场景。 二、场景一:先更新数据库,再更新缓存假设有 2 个线程A 、B并发「写」id = 1的user数据,在高并发下可能会发生以下场景:
可以看到,线程B操作数据库和缓存的时间,却要比线程A的时间短,执行时序发生了「错乱」,此时线程B对缓存的更新就被覆盖掉了,最终导致id = 1的用户user的值在缓存中是"李四",在数据库中是"王五",缓存和数据库数据发生不一致。可见,先更新数据库,再更新缓存,当发生「写」并发时,也会存在数据不一致的情况。 大体过程如下图所示: 实际项目中通常不采用这种方式,主要基于如下一些原因:
如上分析的执行时序发生「错乱」,最终这条数据的结果是错误的,缓存跟数据库中其中一方的数据是脏数据。
如果采用先更新数据库,再更新缓存的方式,假如我们的系统写数据比较多,而读操作比较少,那么缓存将会被频繁地更新,这样导致缓存中的数据压根就没被读请求利用上,浪费性能。 三、场景二:先更新缓存,再更新数据库这个比较简单,假设线程A需要写数据,如执行update user set name = '李四' where id = 1,此时线程A先更新缓存数据为"李四",然后更新数据库的时候,抛异常了,失败了,导致缓存更新成功,数据库更新失败,这就造成了两者的不一致,此时如果刚好有一个线程过来读数据:select * from user where id = 1,那么从缓存中读取到的数据就是脏数据。 实际项目中通常不采用更新缓存的方式,而采用删除缓存的方式。
四、场景三:先删除缓存,再更新数据库假设有 2 个线程A 、B并发「读写」id = 1的user数据,可能会发生以下场景:
最终id = 1的用户user的值在缓存中是"张三(旧值)",在数据库中是"李四(新值)",缓存和数据库数据发生不一致。可见,先删除缓存,后更新数据库,当发生「读+写」并发时,还是存在数据不一致的情况。 大体过程如下图所示: 前面已经介绍了先删除缓存,再更新数据库导致数据不一致的场景,那么怎么解决呢?答案是采用 【延时双删策略+缓存超时设置】结合起来。
所有的写操作以数据库为准,只要到达缓存过期时间,则后面的读请求自然会从数据库中读取新值,然后再写入缓存中;
双删,其实就是删两次缓存的意思。延时,指的是在第一次删完缓存后,延迟一段时间,比如5秒或其他时间,然后再进行第二次删除缓存。 主要过程如下:
参考建议: 1、第一次删除缓存可以采用同步方式删除,第二次删除缓存如果读者朋友担心同步删除会影响性能的话,可以采用异步线程去删; 2、很多时候,都是凭借经验大致估算这个延迟时间,例如笔者项目中通常延迟5s后第二次删除缓存,然后配置最大重试次数,确保缓存删除成功,当然只能尽可能地降低不一致的概率,极端情况下还是会发生不一致; 3、如果第二次删除缓存失败了怎么办?当然是不断地循环尝试删除缓存,可以将删除失败的记录发送到消息队列,然后可以不断重试删除,可以配置最大重试次数,配置告警,直到删除成功。 五、场景四:先更新数据库,再删除缓存a、第一种情况: 线程A需要修改数据,update user set name = '李四' where id = 1,数据更新完成后,在删除缓存的时候,数据库宕机或者服务宕机,导致没有删除掉缓存,此时数据库和缓存的数据也会出现不一致,数据库中是新数据,缓存还是旧数据。 b、第二种情况: 假设有 2 个线程A 、B并发「读写」id = 1的user数据,可能会发生以下场景:
最终id = 1的用户user的值在缓存中是"张三(旧值)",在数据库中是"李四(新值)",缓存和数据库数据发生不一致。可见,先更新数据库,再删除缓存,当发生「读+写」并发时,同样也会存在数据不一致的情况。 大体过程如下图所示: 仔细分析一下,这种情况真的会发生么?准确地说,只是理论上会发生,概率很小,仔细想想,数据库的读操作的速度肯定是远快于写操作的,主要是以下原因:
经过前面的分析,我们发现这种方式发生数据不一致的概率相对较少,实际项目中可以采用这种方式,同样可以使用延迟双删策略,但为了保证两步都成功执行,最好再配合「消息队列」或「订阅变更日志」等重试方式,更加可靠。 六、场景五:数据库主从同步导致数据不一致假设有 2 个线程A 、B并发「读写」id = 1的user数据,可能会发生以下场景:
可以看到,由于主从数据库同步的延时,也会导致缓存与数据库数据不一致。 如何解决呢? 同样可以采用延迟双删的方式,只是第二次删除缓存的休眠时间设置为【主从数据库同步的延迟时间 + 几百ms】,当然也可以通过Canal监听从库binlog日志的方式,将数据库变更信息发送到消息队列中,然后我们去监听消息队列,异步删除Redis对应的缓存。 七、总结前面详细介绍了缓存与数据库发生不一致的场景,通常情况下,我们可以选择使用【先删除缓存,再更新数据库】或者【先更新数据库,再删除缓存】其中一种,如果是延迟双删的话,第二次删除缓存尽量采用异步线程池去删,并结合一些重试机制,再加上最大重试次数,尽可能避免缓存与数据库数据发生不一致。 |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/24 5:39:08- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |