做国外的批发网站有哪些/广州seo顾问

做国外的批发网站有哪些,广州seo顾问,安康市住房和城乡建设局网站,软件开发和网站建设为什么要保证数据一致性 只要使用redis做缓存,就必然存在缓存和DB数据一致性问题。若数据不一致,则业务应用从缓存读取的数据就不是最新数据,可能导致严重错误。比如将商品的库存缓存在Redis,若库存数量不对,则下单时…

为什么要保证数据一致性

只要使用redis做缓存,就必然存在缓存和DB数据一致性问题。若数据不一致,则业务应用从缓存读取的数据就不是最新数据,可能导致严重错误。比如将商品的库存缓存在Redis,若库存数量不对,则下单时就可能出错,这是不能接受的。

旁路缓存

在使用 Redis 作为缓存时,最常见的缓存模式是 Cache-Aside(旁路缓存)模式因其灵活性、容错性以及与 Redis 的高效配合,适用于大多数业务场景。
Cache-Aside(旁路缓存) 模式,又叫 Lazy-Loading(懒加载)模式,在这种模式下,缓存的读取和写入由应用程序直接管理。

  • 读策略:先查询缓存,若缓存未命中,则从数据库中加载数据并将其存储在缓存中;
  • 写策略:应用程序直接更新数据库,并更新或删除缓存中的相关数据。

旁路缓存因其按需触发、避免冗余操作特点所以被称为懒加载。
旁路缓存模式,存在缓存一致性问题,需要小心处理缓存的清除和更新。

更新数据库并更新缓存

那么如何保证redis缓存和数据库的数据一致性呢?
直观的思维想到的是 更新数据库并更新缓存。

这种直观的做法会有连续两次更新,数据不一致问题。并且这种情况经常出现,所以一般不会采用。
在这里插入图片描述

对于缓存命中率要求很高的场景,我们可以使用更新数据库+更新缓存的方案,因为只要缓存不过期,都是可以命中的。

解决方案:

  • 加分布式锁,丢失一部分性能。
  • 加一个比较短的过期时间,哪怕缓存数据不一致也很快过期。

先删除缓存再更新数据库

请求A想要将数据库的数据修改为21,先去redis中删除了缓存,这个时候请求B来访问查询该数据,发现redis中没有该数据,就去数据库中查询了旧数据并写回到了缓存。这个时候请求A才更新数据库。
此时缓存中的数据为20,而数据库中存储的为21产生了数据不一致。

在这里插入图片描述

可以看到先删除缓存,再更新数据库会在【读+写】并发的时候,会出现数据不一致的问题。

延时双删

那么针对于先删除缓存再更新数据库的方案我们有没有解决方案呢?
就是先删除缓存再更新数据库,然后睡一段时间再删除一次缓存。

延迟一段时间删除的目的是什么?

是让线程B有足够的时间去将(旧值)写回到缓存。确保延迟一段时间后能够将线程B的脏数据删除。也是保证了最终一致性。

但是具体延迟多久,很难评估出来,所以这种方案也只能尽可能保证数据一致。但是在极端情况下,还是会有可能出现数据不一致。

先更新数据库再删除缓存

还是在【读+写】并发的场景下分析,请求A查询数据,发现缓存没有命中,查询数据库为20。此时请求B更新数据库并删除了缓存。最后请求A写回了缓存。请求A写回的是旧数据也就是20。
此时数据库中为21(新值),缓存中是20(旧值)。出现了数据不一致。
在这里插入图片描述

可以看到先更新数据库再删除缓存是有可能发生数据库的,但是这个在实际当中发生的概率很小。

因为缓存的写入通常要远远快于数据库的写入,所以实际当中,很少会出现请求B将数据库更新并且删除缓存后,请求A才写回缓存的情况。

所以先更新数据库再删除缓存的方式是可以实现数据一致性的。但是为了保险起见,给缓存数据加上过期时间。也就是即使出现了以上这种小概率时间,还是可以通过过期时间,来达到一个最终一致性的目的。

删除操作失败

但是在实际开发当中还是会出现问题,如果删除缓存操作失败,那么缓存中就会一直存放脏数据,直到key过期。
在这里插入图片描述

也就是我们要保证更新数据库之后缓存能被成功删除?

对应的就有两种解决方案:

  • 消息队列重试机制
  • 订阅 MySQL binlog日志,更新缓存。

消息队列重试机制

引入消息队列,将第二步操作(删除缓存) 要操作的数据放到消息队列中,由消费者来删除缓存。

  • 如果应用删除缓存失败,可以通过消费者重试机制。设置一个最大的重试次数。如果超过最大重试次数还是失败,那就要向服务层发送报错信息。
  • 如果删除缓存成功后返回ack。避免重复消费。

这种方案的缺点就是对代码侵入性比较强,需要修改原本的业务代码。

消费者:交换机和队列都持久化防止消息丢失。

@RabbitListener(bindings = @QueueBinding(value = @Queue(name = MqConstant.REDIS_MQ_QUEUE, durable = "true", arguments = @Argument(name = "x-queue-mode", value = "lazy")),exchange = @Exchange(name = MqConstant.REDIS_DELETE_EXCHANGE_NAME, type = ExchangeTypes.DIRECT),key = MqConstant.REDIS_MQ_QUEUE_ROUTING_KEY
))
public void listenRedisDeleteMqMessage( String msg) {log.info("RedisDeleteMqListener:redis删除数据:" + msg);Set keys = redisTemplate.keys(msg);redisTemplate.delete(keys);
}

配置好相关的生产者消费者确认机制,以及消费者失败重试机制,确保消息正确路由和重试。

spring:      rabbitmq:host: 192.168.215.140 # 你的虚拟机IPport: 5672 # 端口virtual-host: /bigevents # 虚拟主机username: big-events # 用户名password: 123321 # 密码publisher-returns: true # 开启publisher return机制listener:simple:retry:enabled: true # 开启消费者失败重试initial-interval: 1000ms # 初始的失败等待时长为1秒multiplier: 1 # 失败的等待时长倍数,下次等待时长 = multiplier * last-intervalmax-attempts: 3 # 最大重试次数stateless: true # true无状态;false有状态。如果业务中包含事务,这里改为falseacknowledge-mode: auto  #消费者自动ack 异常nack

Canal+MQ

先更新数据库,对数据库的写操作都会被记录在binlog日志中。所以我们更新数据库,也就会产生一条对应的变更日志在binlog中。

于是我们可以通过订阅binlog日志,拿到具体要操作的数据,然后再执行删除缓存,阿里巴巴开源的Canal中间件就是基于这个实现的。

Canal模拟MySQL主从复制的交互协议,把自己伪装成一个MySQL的从节点,向MySQL的主节点发送dump命令请求,MySQL收到请求后,就会推送Binlog给Canal,Canal解析binlog字节流后,转换为便于读取的结构化数据。供下游服务订阅使用。
在这里插入图片描述
前面我们讲了MQ的解决方案对原有的业务代码有很强的侵入性。但是这种方案就不会,直接监听binlog,因此我们可以用binlog+MQ的解决方案。既保证了数据的一致性又不会对原有业务代码有侵入。

具体实现

mysql添加用户和权限

CREATE USER canal IDENTIFIED BY 'canal'; # 添加从节点权限
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';FLUSH PRIVILEGES;

修改canal相关配置:

vi canal-server/conf/example/instance.properties

在这里插入图片描述

通过spring定时任务监听canal消息,解析canal的消息,并投递给mq

/*** 每2秒执行一次** @throws Exception*/
@Scheduled(fixedRate = 2 * 1000)
public void run() throws Exception {//进行连接canalConnector.connect();//进行订阅canalConnector.subscribe();int batchSize = 5 * 1024;//获取Message对象Message message = canalConnector.getWithoutAck(batchSize);long id = message.getId();int size = message.getEntries().size();System.out.println("当前监控到的binLog消息数量是:" + size);//判断是否有数据//如果有数据,进行数据解析List<CanalEntry.Entry> entries = message.getEntries();//遍历获取到的Entry集合for (CanalEntry.Entry entry : entries) {System.out.println("----------------------------------------");System.out.println("当前的二进制日志的条目(entry)类型是:" + entry.getEntryType());//如果属于原始数据ROWDATA,进行打印内容if (entry.getEntryType() == CanalEntry.EntryType.ROWDATA) {try {//获取存储的内容CanalEntry.RowChange rowChange = CanalEntry.RowChange.parseFrom(entry.getStoreValue());//打印事件的类型,增删改查哪种 eventTypeSystem.out.println("事件类型是:" + rowChange.getEventType());ArrayList<Long> idList = new ArrayList<>();//打印改变的内容(增量数据)for (CanalEntry.RowData rowData : rowChange.getRowDatasList()) {System.out.println("改变后的数据:" + rowData.getAfterColumnsList());List<Column> after;if (rowChange.getEventType().equals(CanalEntry.EventType.INSERT)) {after = rowData.getAfterColumnsList();} else if (rowChange.getEventType().equals(CanalEntry.EventType.DELETE)) {after = rowData.getBeforeColumnsList();} else {//updateafter = rowData.getAfterColumnsList();}for (Column column : after) {if (column.getName().equals("id")) {sendMessageToMq(column.getValue());}}}} catch (Exception e) {e.printStackTrace();}}}//消息确认已经处理了canalConnector.ack(id);
}

消费者监听mq,最后删除缓存。

@Slf4j
@Component
@AllArgsConstructor
public class CanalMqListener {private final RedisTemplate redisTemplate;@RabbitListener(bindings = @QueueBinding(value = @Queue(name = MqConstant.CANAL_MQ_QUEUE, durable = "true", arguments = @Argument(name = "x-queue-mode", value = "lazy")),exchange = @Exchange(name = MqConstant.CANAL_MQ_EXCHANGE_NAME, type = ExchangeTypes.DIRECT),key = MqConstant.CANAL_MQ_EXCHANGE_NAME))public void listenCanalMqMessage( String msg) {log.info("listenCanalMqMessage:redis变更数据:" + msg);try {Set keys = redisTemplate.keys(msg);redisTemplate.delete(keys);} catch (Exception e) {throw new RuntimeException(e);}}
}

总结

[图片]

解决方案

延时双删

优点:

  • 无需引入中间件
  • 实现简单,代码改动小

缺点:

  • 延迟时间不好控制
  • 极端情况会出现数据不一致

消息队列

优点:

  • 确保缓存被删除

缺点:

  • 需要引入消息队列,增加了系统复杂度。
  • 延迟稍高(对于能接受一定延迟的场景使用)
  • 对原有业务代码有侵入

Canal+MQ

优点:

  • 确保缓存被删除
  • 直接监听binlog,对原有代码无侵入

缺点:

  • 需要引入多个中间件对团队运维能力有较高要求
  • 延迟稍高

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

19.哈希表的实现

1.哈希的概念 哈希(hash)⼜称散列&#xff0c;是⼀种组织数据的⽅式。从译名来看&#xff0c;有散乱排列的意思。本质就是通过哈希函数把关键字Key跟存储位置建⽴⼀个映射关系&#xff0c;查找时通过这个哈希函数计算出Key存储的位置&#xff0c;进⾏快速查找。 1.2.直接定址法…

IoTDB TTL不生效

问题 时序数据库 IoTDB 1.3.0 版本数据库的 TTL 设置为两天&#xff0c;show databases details 看到设置也是正确的&#xff0c;怎么还是可以查到好几天前的数据&#xff1f;因为有很多不活跃的测点&#xff0c;所以专门设置了两天过期&#xff0c;有什么办法可以自动清理呢&…

iStoreOS软路由对硬盘格式化分区(转化ext4)

一、为什么要格式化分区&#xff1f; 格式化硬盘分区是软路由安装或配置过程中的重要步骤&#xff0c;主要用于清除旧数据、优化文件系统、确保系统稳定性和兼容性。 二、通过iStoreOS硬盘格式化步骤 使用场景&#xff1a;Docker迁移到外置移动硬盘为例&#xff0c;考虑兼容现…

打造用户认证系统,构筑信息安全防线

在当今的数字化时代&#xff0c;信息安全和用户隐私保护变得越来越重要。用户身份认证是确保信息安全的第一道防线。通过验证用户身份&#xff0c;可以防止未经授权的访问和数据泄露。它有助于保护用户的个人信息、账户资金和其他敏感数据。此外&#xff0c;用户身份认证还可以…

Axure项目实战:智慧城市APP(一)(动态面板、拖动效果)

亲爱的小伙伴&#xff0c;在您浏览之前&#xff0c;烦请关注一下&#xff0c;在此深表感谢&#xff01; 课程主题&#xff1a;智慧城市APP便民服务平台 主要内容&#xff1a;完整智慧APP原型设计 应用场景&#xff1a;各类政务型、B端APP均可参考 案例展示&#xff1a;&…

MySQL 入门大全:数据类型

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…

Java 记忆链表,LinkedList 的升级版

文章目录 记忆链表 MemoryLinkedList实战源代码 众所周知&#xff0c;ArrayList 和 LinkedList 是 Java 集合中两个基本的数据结构&#xff0c;对应数据结构理论中的数组和链表。但在这两个数据结构&#xff0c;开发者们通常使用 ArrayList&#xff0c;而不使用 LinkedList。JD…

《白帽子讲 Web 安全》之开发语言安全深度解读

目录 引言 1.PHP 安全 1.1变量覆盖 1.2空字节问题 1.3弱类型 1.4反序列化 1.5安全配置 2Java 安全 2.1Security Manager 2.2反射 2.3反序列化 3Python 安全 3.1反序列化 3.2代码保护 4.JavaScript 安全 4.1第三方 JavaScript 资源 4.2JavaScript 框架 5.Node.…

鸿蒙HarmonyOS NEXT应用崩溃分析及修复

鸿蒙HarmonyOS NEXT应用崩溃分析及修复 如何保证应用的健壮性&#xff0c;其中一个指标就是看崩溃率&#xff0c;如何降低崩溃率&#xff0c;就需要知道存在哪些崩溃&#xff0c;然后对症下药&#xff0c;解决崩溃。那么鸿蒙应用中存在哪些崩溃类型呢&#xff1f;又改如何解决…

第六届机电一体化技术与智能制造国际学术会议(ICMTIM 2025)

重要信息 4月11-13日 南京江北新区工业大学亚朵酒店 www.icmtim.org&#xff08;点击了解参会投稿等&#xff09; 简介 由南京工业大学主办&#xff0c;南京工业大学电气工程与控制科学学院、中国矿业大学、黑龙江大学、江苏省自动化学会承办的第六届机电一体化技术…

INT202 Complexity of Algroithms 算法的复杂度 Pt.2 Search Algorithm 搜索算法

文章目录 1.树的数据结构1.1 有序数据(Ordered Data)1.1.1 有序字典&#xff08;Ordered Dictonary&#xff09;1.1.1.1 排序表&#xff08;Sorted Tables&#xff09; 1.2 二分查找&#xff08;Binary Search&#xff09;1.2.1 二分查找的时间复杂度 1.3 二叉搜索树&#xff0…

【AVRCP】蓝牙链路控制器(LC)与AVRCP互操作性要求深度解析

目录 一 、Link Controller&#xff08;LC&#xff09;概述 1.1 LC的定义与功能 1.2 LC在蓝牙技术中的重要性 二、Link Controller&#xff08;LC&#xff09;互操作性要求 2.1 互操作性要求概述 2.2 物理层互操作性要求 2.3 链路管理互操作性要求 2.4 其他互操作性要求…

高级背景抠图工具(python)

这是一个专业的图像背景处理工具,基于Python开发,主要功能包括:1. 智能背景去除 - 使用rembg库的深度学习模型自动识别并移除图片背景。 2. 背景自定义 - 支持纯色背景替换,保留透明通道(Alpha通道)。3. 高级参数调节 - 提供5种专业级图像处理参数。4. 实时预览 - 双窗口…

快速入手-基于Django的主子表间操作mysql(五)

1、如果该表中存在外键&#xff0c;结合实际业务情况&#xff0c;那可以这么写&#xff1a; 2、针对特殊的字典类型&#xff0c;可以这么定义 3、获取元组中的字典值和子表中的value值方法 4、对应的前端页面写法

网络运维学习笔记(DeepSeek优化版) 021 HCIA-Datacom新增知识点03园区网典型组网架构及案例实战

文章目录 园区网典型组网架构及案例实战1 园区网定义2 园区网络典型架构3 各层级协议与技术4 项目生命周期管理5 小型园区网络设计框架5.1 组网方案设计5.2 IP地址规划5.3 园区内部的路由设计5.4 NAT设计5.5 WLAN设计5.6 安全设计5.7 运维管理设计 6 小型园区的实施方案与运维手…

1.8 函数的连续性和间断点

1.连续的定义 2.间断点的定义 3.间断点的分类

基于Arm GNU Toolchain编译生成的.elf转hex/bin文件格式方法

基于Arm GNU Toolchain编译生成的.elf转hex/bin文件格式方法 已经弃用的版本&#xff08;Version 10.3-2021.10&#xff09;&#xff1a;gcc-arm-none-eabi&#xff1a;https://developer.arm.com/downloads/-/gnu-rmArm GNU Toolchain当前版本&#xff1a;https://developer.a…

失败的面试经历(ʘ̥∧ʘ̥)

一.面向对象的三大特性 1.封装&#xff1a;将对象内部的属性私有化&#xff0c;外部对象不能够直接访问&#xff0c;但是可以提供一些可以使外部对象操作内部属性的方法。 2.继承&#xff1a;类与类之间会有一些相似之处&#xff0c;但也会有一些异处&#xff0c;使得他们与众…

SpringBoot的启动原理?

大家好&#xff0c;我是锋哥。今天分享关于【SpringBoot的启动原理&#xff1f;】面试题。希望对大家有帮助&#xff1b; SpringBoot的启动原理&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Spring Boot的启动原理主要是通过 SpringApplication 类来…

Springboot的jak安装与配置教程

目录 Windows系统 macOS系统 Linux系统 Windows系统 下载JDK&#xff1a; 访问Oracle官网或其他JDK提供商网站&#xff0c;下载适合Windows系统的JDK版本。网站地址&#xff1a;Oracle 甲骨文中国 | 云应用和云平台点击进入下滑&#xff0c;点击进入下载根据自己的系统选择&…