redis:四、双写一致性的原理和解决方案(延时双删、分布式锁、异步通知MQ/canal)、面试回答模板

双写一致性

场景导入

如果现在有个数据要更新,是先删除缓存,还是先操作数据库呢?当多个线程同时进行访问数据的操作,又是什么情况呢?

以先删除缓存,再操作数据库为例

多个线程运行的正常的流程应该如下:
线程1先访问数据,它首先删除缓存,然后更新数据库。之后线程2来查询缓存,未命中后查询数据库,随后写入缓存。
也就是说,线程1负责删除缓存并更新数据库,线程2负责查询数据库并写入缓存。
在这里插入图片描述

但如果线程2在线程1还未更新数据库的时候,就查询数据库了,那么就会出问题。
在这里插入图片描述

以先操作数据库,再删除缓存为例。

正常流程应该如下:
线程2负责更新数据库并删除缓存。线程1负责查询数据库,并写入缓存。
其实也可以说

线程1负责更新数据库并删除缓存。线程2负责查询数据库,并写入缓存。

我把上面的“以先删除缓存,再操作数据库为例”搬过来了,可以对比一下。

也就是说,线程1负责删除缓存并更新数据库,线程2负责查询数据库并写入缓存。

发现就是前半部分顺序颠倒了而已。
在这里插入图片描述
同样的,

但如果线程1在线程2还更新数据库的时候,就查询数据库了,那么就会出问题。

我同样把上面的异常情况搬过来对比。

但如果线程2在线程1还未更新数据库的时候,就查询数据库了,那么就会出问题。

在这里插入图片描述

定义

当修改了数据库的数据也要同时更新缓存的数据,缓存和数据库的数据要保持一致

解决方案

能保障强一致性:延时双删、分布式锁
不能保障强一致性,只能保障最终的一致性:异步通知

延时双删(强一致性)

延时双删就是正常删除缓存、修改数据库后还要延时一会再次删除缓存。
在这里插入图片描述
因为从上面的场景导入,我们发现,无论是先删除缓存还是先修改数据库,都会有数据不一致,即脏数据的风险。

再次把先删除缓存,再修改数据库的异常流程图拿过来,我们发现整个流程走完后,线程1更新数据库,拿到的是正确的值。而线程2拿到的是错误的值,这时只要我们以数据库为主,删除缓存,再写入数据库的值,那么就能拿到正确的值。

在这里插入图片描述
此外,延时一会是因为一般数据库都是主从分离,读写分离的。延时是为了让主库有时间通知到从库,所有数据库的更新操作全部走完。
延时双删极大程度上避免了脏数据的风险,但因为有延时的存在,延时时间不好控制,所以也不能说百分百避免。

分布式锁(强一致性)

互斥锁

直接加互斥锁能保障数据的强一致性,但是性能较低。此时我们就需要优化一下互斥锁。因为存入缓存的数据,一般都是读多写少。为此我们引入两个单独的锁,分别叫共享锁和排他锁。

共享锁/读锁

共享锁,又叫读锁(readLock),加锁之后,其他线程可以共享读操作。

排他锁/独占锁

排他锁,又叫独占锁(writeLock),加锁之后,阻塞其他线程读和写操作。

混合使用的流程和代码

在这里插入图片描述
我们想要拿到共享锁或者排他锁,都需要先拿到读写锁。
通过固定代码可以拿到读写锁。

RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("ITEM_READ_WRITE_LOCK");

随后分别拿到共享锁和排他锁。(注意两个锁需要是同一把读写锁)

RLock readLock = readWriteLock.readLock();
RLock writeLock = readWriteLock.writeLock();

读操作的代码

public void getById(Integer id){
  RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("ITEM_READ_WRITE_LOCK");
  RLock readLock = readWriteLock.readLock();
  try{
    readLock.lock();
    System.out.println("readLock...");
    Item item = (Item) redisTemplate.opsForValue().get("item"+id);
    if(item != null){
      return item;
    }
    item = new Item(id, "华为手机", "华为手机", 5999.00);
    redisTemplate.opsForValue().set("item"+id, item);
    return item;
  }finally{
    readLock.unlock();
  }
}

写操作的代码

public void updateById(Integer id){
  RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("ITEM_READ_WRITE_LOCK");
  RLock writeLock = readWriteLock.writeLock();
  try{
    writeLock.lock();
    System.out.println("writeLock...");
    Item item = new Item(id, "华为手机", "华为手机", 5299.00);
    try{
      Thread.sleep(10000);
    }catch(InterruptedException e){
      e.printStackTrace();
    }
    redisTemplate.delete("item"+id);
  }finally{
    writeLock.unlock();
  }
}

异步通知

异步通知的也有两个主流方案:MQ、Canal
在这里插入图片描述


在这里插入图片描述
canal的方案对于业务代码几乎是零侵入的。

面试回答模板

redis为缓存,mysql的数据如何与redis进行同步呢?

背熟以下回答,大概用时1min。

这个要看业务需求,如果要求数据的强一致性,那么一般使用读写锁来实现。读写锁是一种分布式锁机制,里面包括两种锁,一个叫共享锁,在读的时候添加共享锁,可以保证读读不互斥,读写互斥。一个叫排他锁,在写的时候添加排他锁,可以保证读写都互斥,避免脏数据的风险。
如果不要求数据的强一致性,那么就可以用基于MQ或者canal中间件的异步通知,来实现redis和mysql的双写一致性。

你的项目中用到了redis,那你mysql的数据如何与redis进行同步呢?

——————————————强一致性策略——————————————————

以我最近做的这个项目为例,里面有xxxx(根据自己的简历上写)的功能,需要让数据库与redis高度保持一致,因为要求时效性比较高,我们当时采用的读写锁保证的强一致性。
读写锁是一种分布式锁机制,里面包括两种锁,一个叫共享锁,在读的时候添加共享锁,可以保证读读不互斥,读写互斥。一个叫排他锁,在写的时候添加排他锁,可以保证读写都互斥,避免脏数据的风险。这里面需要注意的是读方法和写方法上需要使用同一把锁才行。

面试官:那这个排他锁是如何保证读写、读读互斥的呢?

候选人:其实排他锁底层使用也是setnx,保证了同时只能有一个线程操作锁住的方法

面试官:你听说过延时双删吗?为什么不用它呢?

候选人:延迟双删,如果是写操作,我们先把缓存中的数据删除,然后更新数据库,最后再延时删除缓存中的数据,其中这个延时多久不太好确定,在延时的过程中可能会出现脏数据,并不能保证强一致性,所以没有采用它。

——————————————非强一致性策略——————————————————

面试官:redis做为缓存,mysql的数据如何与redis进行同步呢?(双写一致性)

候选人:嗯!就说我最近做的这个项目,里面有xxxx(根据自己的简历上写)的功能,数据同步可以有一定的延时(符合大部分业务)

我们当时采用的阿里的canal组件实现数据同步:不需要更改业务代码,部署一个canal服务。canal服务把自己伪装成mysql的一个从节点,当mysql数据更新以后,canal会读取binlog数据,然后在通过canal的客户端获取到数据,更新缓存即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/251027.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Android动画(二)——补间动画

目录 介绍 Xml文件定义View动画 补充 alpha_animation.xml(透明度) rotate_animation.xml(旋转) scale_animation.xml(伸缩) translate_animation.xml (平移) group_animation.…

〖大前端 - 基础入门三大核心之JS篇(55)〗- 内置对象

说明:该文属于 大前端全栈架构白宝书专栏,目前阶段免费,如需要项目实战或者是体系化资源,文末名片加V!作者:哈哥撩编程,十余年工作经验, 从事过全栈研发、产品经理等工作,目前在公司…

结构体基础全家桶(2)结构体指针

目录 指向结构体类型数据的指针: 指向结构体变量的指针: 创建: 应用: 注意事项: 指向结构体数组的指针 创建: 应用: 注意: 用结构体变量和指向结构体的指针做函数的参数 …

【Linux】文件系统、文件系统结构、虚拟文件系统

一、文件系统概述 1. 什么是文件系统?2. 文件系统(文件管理系统的方法)的种类有哪些?3. 什么是分区?4. 什么是文件系统目录结构?5. 什么虚拟文件系统Virtual File System ?6. 虚拟文件系统有什…

selenium 与 chromedriver安装

本文章向大家介绍selenium 安装与 chromedriver安装,主要包括selenium 安装与 chromedriver安装使用实例、应用技巧、基本知识点总结和需要注意事项供大家参考。 一、安装selenium 1、Selenium简介 Selenium是一个Web的自动化测试工具,最初是为网站自动化测试而开…

IDEA配置一个新项目

git clone xxxxx 下载项目主分支 git checkout xxx 切换到需要开发的分支上 配置maven仓库 在File下的Settings中设置maven仓库 配置maven仓库的文件夹 配置好maven后,项目中会出现一个红色的pom.xml文件,右击文件,点击…,pom…

RabbitMq基本使用

目录 SpringAMQP1.准备Demo工程2.快速入门1.1.消息发送1.2.消息接收1.3.测试 3.WorkQueues模型3.1.消息发送3.2.消息接收3.3.测试3.4.能者多劳3.5.总结 SpringAMQP 将来我们开发业务功能的时候,肯定不会在控制台收发消息,而是应该基于编程的方式。由于R…

ArrayList与LinkLIst

ArrayList 在Java中,ArrayList是java.util包中的一个类,它实现了List接口,是一个动态数组,可以根据需要自动增长或缩小。下面是ArrayList的一些基本特性以及其底层原理的简要讲解: ArrayList基本特性: 动…

科大讯飞(深圳)测开面试真题

一面(测试组长面) 1、上家公司项目以及团队的规模是怎么样的? 2、你负责的项目整体的流程是怎么样的? 3、自动化实施过程中,是如何和业务测试进行沟通的? 4、在上家公司你已经是专职做自动化了&#xf…

会声会影怎么使用? 会声会影2024快速掌握入门技巧

一听说视频剪辑我们就不由得联想到电影、电视等一些高端的视频剪辑技术,大家都觉得视频剪辑是一个非常复杂而且需要很昂贵的设备才可以完成的技术活,这对很多“门外汉”来说都可望而不可及。实际上,使用会声会影剪辑视频不仅是很多人都可以操…

《面向机器学习的数据标注规程》摘录

说明:本文使用的标准是2019年的团体标准,最新的国家标准已在2023年发布。 3 术语和定义 3.2 标签 label 标识数据的特征、类别和属性等。 3.4 数据标注员 data labeler 对待标注数据进行整理、纠错、标记和批注等操作的工作人员。 【批注】按照定义…

【话题】低代码123

目录 一、什么是低代码 二、低代码的优缺点 三、你认为低代码会替代传统编程吗? 四、有哪些低代码工具和框架 4.1 国外的平台 4.2 国内的平台 五、未来的软件研发 低代码,听着就过瘾的一个词。而且不是无代码,这说明,低代码…

GoogLeNet(pytorch)

亮点与创新: 1. 引入Inception基础结构 2. 引入PW维度变换卷积,启迪后续参数量的优化 3. 丢弃全连接层,使用平均池化层(大大减少模型参数) 4. 添加两个辅助分类器帮助训练(避免梯度消失,用于…

方舟无限ARX-5臂的奇异验证

事情起因是,某技术人员号称这款机械臂无奇异点,博主当场一个【黑人问号脸】。 既然是串联臂,大概很难做到无奇异点~ 为了反驳,博主建模简单分析了下,偏置参数随便写了个,具体验证程序见文末。 clear,clc,…

共同编辑文档功能实现(websocket)

目录 前言 websocket封装 wangeditor下载 共同编辑文档代码实现 HTML样式部分 JS部分 css部分 前言 功能:实现文档共同编辑功能,可以实时接收到其他人的信息 思路:先调用接口获取相应的数据进行渲染,然后通过webSocket建…

C#基础知识 - 基本语法篇

C#基础知识-基本语法篇 第2节 C#基本语法2.1 C#程序结构2.2 C# 结构解析2.3 命名空间及标识符、关键字2.3.1 别名的使用2.3.2 标识符2.3.3 C#关键字 更多C#基础知识详解请查看:C#基础知识 - 从入门到放弃 第2节 C#基本语法 2.1 C#程序结构 “Hello, World”程序历…

棋牌的电脑计时计费管理系统教程,棋牌灯控管理软件操作教程

一、前言 有的棋牌室在计时的时候,需要使用灯控管理,在开始计时的时候打开灯,在结账后关闭灯,也有的不需要用灯控,只用来计时。 下面以 佳易王棋牌计时计费管理系统软件为例说明: 软件试用版下载或技术支…

Java架构师系统架构高可用维度分析

目录 1 导语2 可用性介绍3 本地高可用-集群、分布式4 本地高可用-数据逻辑保护5 异地容灾-双活、两地三中心6 异地容灾-DRP规划&BCP业务连续性7 多活和妥协方案8 高可用流程9 总结想学习架构师构建流程请跳转:Java架构师系统架构设计 1 导语 Java架构师在进行系统架构设…

ELk(七)—部署Nginx

目录 部署Nginxfilebeat启动Nginx模块Module对nginx模块配置进行修改修改nginx-log.yml配置文件 部署Nginx 下面是nginx的安装脚本,里面的参数可以根据实际需要进行修改。 #!/bin/bash#新建一个文件夹用来存放下载的nginx源码包mkdir -p /opt/nginx cd /opt/nginx…

Android Studio 软件如何将系统自带的标题栏隐藏

目录 一、实现效果 二、开发环境 三、实现方法 ①首先创建一个新的项目 ②打开你需要隐藏标题栏的Activity ③我们看下正常的显示效果 ④然后在onCreate中进行代码编写 ⑤点击运行查询看效果 三、Android Studio 模板 一、实现效果 二、开发环境 三、实现方法 在Andro…