文章目录

  • 01、如何理解数据的一致性?
  • 02、使用redis缓存的注意事项?
  • 03、如何更新缓存?
  • 04、组合1:先更新缓存,再更新数据库(双写模式,不推荐)
  • 05、组合2:先删除缓存,再更新数据库(不推荐)
  • 06、组合3:先更新数据库,再更新缓存(不推荐)
  • 07、组合4:先更新数据库,再删除缓存(失效模式,推荐)
  • 08、组合5:先删除缓存,更新数据库,再删除缓存(延时双删模式,推荐)
  • 09、保证数据一致性方案比较
  • 10、删除缓存失败后,如何重试?

01、如何理解数据的一致性?

1)强一致性

如果你的项目对缓存的要求是强一致性的,那么请不要使用缓存。这种一致性级别是最符合用户直觉的,它要求系统写入什么,读出来的也会是什么,用户体验好,但实现起来往往对系统的性能影响大。

2)弱一致性

这种一致性级别约束了系统在写入成功后,不承诺立即可以读到写入的值,也不承诺多久之后数据能够达到一致,但会尽可能地保证到某个时间级别(比如秒级别)后,数据能够达到一致状态。

3)最终一致性

最终一致性是弱一致性的一个特例,系统会保证在一定时间内,能够达到一个数据一致的状态。这里之所以将最终一致性单独提出来,是因为它是弱一致性中非常推崇的一种一致性模型,也是业界在大型分布式系统的数据一致性上比较推崇的模型。一般情况下,高可用只确保最终一致性,不确保强一致性。

02、使用redis缓存的注意事项?

1)我们能放入缓存的数据本就不应该是实时性、一致性要求超高的。如果要求强一致性,就不要使用redis,使用redis代表可以接受最终一致性。遇到实时性、一致性要求高的数据,就应该查数据库,即使慢点。

2)使用redis作为缓存,必须设置过期时间,原因: 即使程序有问题,不能保证redis的最终一致性,key也会自己失效。缓存数据的时候加上过期时间,保证每天拿到当前最新数据即可。

03、如何更新缓存?

更新缓存的步骤特别简单,共两步:更新数据库和更新缓存。但这简单的两步中需要考虑很多问题。

1)先更新数据库还是先更新缓存?更新缓存时先删除还是直接更新?
2)假设第一步成功了,第二步失败了怎么办?
3)假设两个线程同时更新同一个数据,A线程先完成第一步,B线程先完成第二步怎么办?

04、组合1:先更新缓存,再更新数据库(双写模式,不推荐)

假设第二步更新数据库失败了,要求回滚缓存的更新,这时该怎么办呢?

Redis不支持事务回滚,除非采用手工回滚的方式,先保存原有数据,然后再将缓存更新回原来的数据,这种解决方案有些缺陷,这里简单举个例子:

1)原来缓存中的值是a,两个线程同时更新库存。
2)线程A将缓存中的值更新成b,且保存了原来的值a,然后更新数据库。
3)线程B将缓存中的值更新成c,且保存了原来的值b,然后更新数据库。
4)线程A更新数据库时失败了,它必须回滚,那现在缓存中的值更新成什么呢?理论上应该更新成c,因为数据库中的值是c,但是,线程A里面无从获得c这个值。

如果在线程A更新缓存与数据库的整个过程中,先把缓存及数据库都锁上,确保别的线程不能更新,是否可行?当然是可行的。但是其他线程能不能读取?

假设线程A更新数据库失败回滚缓存时,线程C也加入进来,它需要先读取缓存中的值,这时又返回什么值?

看到这个场景,是不是有点儿熟悉?不错,这就是典型的事务隔离级别场景。所以就不推荐这个组合,因为此处只是需要使用一下缓存,而这个组合就要考虑事务隔离级别的一些逻辑,成本太大。

05、组合2:先删除缓存,再更新数据库(不推荐)

使用这种方案,即使更新数据库失败了也不需要回滚缓存。这种做法虽然巧妙规避了失败回滚的问题,却引出了两个更大的问题。

假设线程A先删除缓存,再更新数据库。在线程A完成更新数据库之前,后执行的线程B反而超前完成了操作,线程B此时读取Key发现没有数据后,将数据库中的旧值存放到了缓存中。线程A在线程B都完成后再更新数据库,这样就会出现缓存(旧值)与数据库的值(新值)不一致的问题。

为了解决一致性问题,可以让线程A给Key加锁,因为写操作特别耗时,这种处理方法会导致大量的读请求卡在锁中。以上描述的是典型的高可用和一致性难以两全的问题。

06、组合3:先更新数据库,再更新缓存(不推荐)

同样需要考虑两个问题:

1)假设第一步(更新数据库)成功,第二步(更新缓存)失败了怎么办?

因为缓存不是主流程,数据库才是,所以不会因为更新缓存失败而回滚第一步对数据库的更新。此时一般采取的做法是重试机制,但重试机制如果存在延时还是会出现数据库与缓存不一致的情况,不好处理。

2)假设两个线程同时更新同一个数据,线程A先完成了第一步,线程B先完成了第二步怎么办?

线程A把值更新成a,线程B把值更新成b,此时数据库中的最新值是b,因为线程A先完成了第一步,后完成第二步,所以缓存中的最新值是a,数据库与缓存的值还是不一致,这个逻辑还是有问题的。

因此,也不建议采用这个组合。

07、组合4:先更新数据库,再删除缓存(失效模式,推荐)

它能解决组合3的第二个问题吗?

假设两个线程同时更新同一个数据,线程A先完成第一步,线程B先完成第二步怎么办?

线程A把值更新成a,线程B把值更新成b,此时数据库中的最新值是b,因为线程A先完成了第一步,所以第二步谁先完成已经不重要了,因为都是直接删除缓存数据。这个问题解决了。

它能解决组合3的第一个问题吗?假设第一步成功,第二步失败了怎么办?

这种情况的出现概率与组合3相比明显低不少,因为删除比更新容易多了。虽然这个组合方案不完美,但出现一致性问题的概率较低。

除了组合3会碰到的问题,还会碰到别的问题吗?

假设线程A要更新数据,先完成第一步更新数据库,在线程A删除缓存之前,线程B要访问缓存,那么取得的就是旧数据。这是一个小小的缺陷。

那么,以上问题有办法解决吗?

08、组合5:先删除缓存,更新数据库,再删除缓存(延时双删模式,推荐)

先进行缓存清除,再执行update,最后(延迟N秒)再执行缓存清除。(延迟N秒)的时间要大于一次写操作的时间。

public void write(String key,Object data){redis.delKey(key);db.updateData(data);Thread.sleep(500);redis.delKey(key);}

这个方案其实和先更新数据库,再删除缓存差不多,因为还是会出现类似的问题:假设线程A要更新数据库,先删除了缓存,这一瞬间线程C要读缓存,先把数据迁移到缓存;然后线程A完成了更新数据库的操作,这一瞬间线程B也要访问缓存,此时它访问到的就是线程C放到缓存里面的旧数据。

不过该方案出现类似问题的概率更低,因为要刚好有3个线程配合才会出现问题。但是相比于组合4,该方案规避了第二步删除缓存失败的问题——先删除缓存,再更新数据库,假设它的第三步“再删除缓存”失败了,也没关系,因为缓存已经删除了。

09、保证数据一致性方案比较

其实没有一个组合是完美的,它们都有读到脏数据(这里指旧数据)的可能性,只不过概率不同。根据以上分析,组合5相对来说是比较好的选择。

不过这个组合也有一些问题要考虑,具体如下:

1)删除缓存数据后变相出现缓存击穿,此时该怎么办?使用缓存击穿的解决方案。

2)删除缓存失败如何重试?删除缓存失败后重试,这个重试可以做得复杂一点,也可以做得简单一点。简单一点就是使用try…catch…,假设删除缓存失败了,在catch里面重试一次即可;复杂一点就是使用一个异步线程不断重试,甚至用到MQ。不过这里没有必要大动干戈。而且异步重试的延时大,会带来更多的读脏数据的可能性。所以仅仅同步重试一次就可以了。

3)不可避免的脏数据问题。虽然这个问题在组合5中出现的概率已经大大降低了,但是还是有的。关于这一点就需要与业务沟通,毕竟这种情况比较少见,可以根据实际业务情况判断是否需要解决这个瑕疵。

总结:

无论是双写模式还是失效模式,都会导致缓存的不一致问题。即多个实例同时更新会出事。怎么办?

1)数据不一致性问题,这只是暂时性的脏数据问题,但是在数据稳定,缓存过期以后,又能得到最新的正确数据,所以缓存的所有数据都必须加上过期时间,数据过期下一次查询触发主动更新。缓存数据+过期时间也足够解决大部分业务对于缓存的要求。

2)读写数据的时候,加上分布式的读写锁。通过加锁保证并发读写,写写的时候按顺序排好队。读读无所谓。所以适合使用读写锁。(业务不关心脏数据,允许临时脏数据可忽略)

3)经常写,经常读的数据不要使用缓存。

10、删除缓存失败后,如何重试?

基于MQ的可靠性消息通信,具体步骤为:

1)把要删除的缓存值或者是要更新的数据库值暂存到消息队列MQ中
2)当删除缓存值或者是更新数据库值成功时,把这些值从消息队列中去除,以免重复操作。
3)当删除缓存值或者是更新数据库值失败时,执行失败策略,重试服务从消息队列中重新读取这些值,然后再次进行删除或更新。
4)删除或者更新失败时,需要再次进行重试,重试超过的一定次数。向业务层发送报错信息。

Redis - 更新数据时如何保证MySQL和Redis中的数据一致性?相关推荐

  1. mysql并发_MySQL并发更新数据时的处理方法

    UPDATE是否会加锁? SQL语句为如下时,是否会加锁? UPDATE table1 SET num = num + 1 WHERE id=1; 答案是不会 实际上MySQL是支持给数据行加锁(In ...

  2. 如何保证MySQL和Redis的数据一致性?

    " 今天给大家剖析一下工作中常见的 MySQL 和 Redis 数据一致性问题. 图片来自 Pexels 什么是数据的一致性 一致性就是数据保持一致,在分布式系统中,可以理解为多个节点中数据 ...

  3. 如何保证MySQL和Redis的数据一致性?10张图带你搞定!

    导语 | 本文的主要思路是首先带大家认识了解MySQL和Redis的数据一致性情况,然后进行反推不一致的情况,从而进行探究单线程中的不一致的情况.同时探究多线程中的不一致的情况,拟定数据一致性策略. ...

  4. asscess 一条记录更新数据_一条MySQL更新语句是怎么执行的?

    流程图 这是在网上找到的一张流程图,写的比较好,大家可以先看图,然后看详细阅读下面的各个步骤. 执行流程: 1.连接验证及解析 客户端与MySQL Server建立连接,发送语句给MySQL Serv ...

  5. oracle更新数据没反应,ORACLE更新数据时如果有就更新没有就插入

    SQL写法: begin update table_name set salary = 10000 where emp_id = 5; if sql%notfound then insert into ...

  6. 更新数据时redis缓存与数据库数据不一致的问题

    最初级的缓存不一致问题及解决方案 问题:先修改数据库,再删除缓存.如果删除缓存失败了,那么会导致数据库中是新数据,缓存中是旧数据,数据就出现了不一致. 解决思路:先删除缓存,再修改数据库.如果数据库修 ...

  7. 向mysql表中插入/更新数据时自动添加创建时间/更新时间

    目录 处理方式一:修改数据库字段类型 测试插入数据: 测试更新数据: 处理方式二:配置生成策略 [MySQL版本] 8.x 处理方式一:修改数据库字段类型 自动添加创建时间: 将字段类型设置为TIME ...

  8. 更新数据时,MySQL的聚簇索引是如何变化的?

    文章已收录在我的 GitHub 仓库,欢迎Star/fork: Java-Interview-Tutorial 听说点赞.评论.收藏的人长得都很好看哦. 搜索一个主键id对应的行,先去顶层的索引页88 ...

  9. php两周能学会,php,mysql_php可以按天连续去两周的更新数据吗,php,mysql - phpStudy

    php可以按天连续去两周的更新数据吗 use_id reg_time 1 2015-12-01 2 2015-12-02 3 2015-12-02 4 2015-12-03 5 2015-12-03 ...

最新文章

  1. df命令、du命令、磁盘分区(fdisk命令)
  2. LinkedBlockingQueue 注记
  3. QML和C ++之间的数据类型转换---枚举
  4. 【转】iOS-生成Bundle包-引入bundle-使用bundle
  5. IOS上 关于状态栏的相关设置(UIStatusBar)和preferredStatusBarStyle不执行问题
  6. python关于文件的编程题_《Python编程》源代码文件
  7. vue怎么获取用户的位置经纬度_vue 实现Web端的定位功能 获取经纬度
  8. php如何设计自定义模型,php – Kohana ORM模型中的自定义方法
  9. mysqlfront连接MySQL错误,使用MySQL-Front连接mysql的时候发生的错误解决方法
  10. Linux内核分析第一周——计算机是如何工作的
  11. JS实现类似QQ好友头像hover时显示资料卡的效果
  12. vCenter 6.0 vsca 安装遇到的一些小问题
  13. 结构光相移法-多频外差原理+实践(上)
  14. Code[VS]1997 守卫者的挑战
  15. Linux系统U盘怎么格式化,u盘怎么格式化各系统教程
  16. 论文笔记Doubly Stochastic Variational Inference for Deep Gaussian Processes深度高斯过程的双重随机变分推理
  17. Hulu:视频广告系统中的算法实践
  18. mac连接wifi无ip/无法访问网络
  19. 键盘定位板图纸_聊聊机械键盘的结构。
  20. Java中for循环的用法(亲测)

热门文章

  1. 基于Python实现的残缺图像检索SDK
  2. c#取小数点后三位_VB.NetC#-取小数点后几位但不四舍五入的方法
  3. java能长高吗_怎样可以让青少年长高?
  4. python二维列表
  5. wavework.m
  6. php 内容输出到html,PHP怎么输出HTML代码内容
  7. 休闲解谜游戏:Squiggle Drop Mac
  8. cache--存储器系统
  9. java 地图报表_Echarts实现定位-高德-天地图
  10. css计算获得剩下的宽度和高度