MySQL 与 Redis 数据一致性保证方案
MySQL 与 Redis 数据一致性保证方案讲述链条延迟双删问题 → 缓存击穿场景 → 热点数据三级防护 → Canal + Binlog 方案 → 最终一致性实现 核心表述MySQL 与 Redis 保证一致性的传统方案是延迟双删:先删除缓存,更新数据库,延迟 500ms 后再删除缓存。但这个方案在热点数据场景下存在缓存击穿问题:延迟期间缓存为空,大量请求同时发现缓存 MISS,全部打到数据库造成压力。 针对热点数据,我们采用三级防护体系。Level 1 使用本地缓存 Caffeine 防止 Redis 过载;Level 2 是 Redis 分布式缓存;Level 3 是数据库查询,加分布式锁 Redisson RLock 防止击穿,使用 tryLock 尝试加锁最多等待 100ms,抢到锁的线程负责查询数据库并重建缓存,其他线程等待后重试。重建缓存时设置随机过期时间防止缓存雪崩,并缓存空对象防止缓存穿透。 更优雅的方案是 Canal + Binlog。Canal Server 伪装成 MySQL Slave 订阅 Binlog,解析 INSERT/UPDATE...
MySQL 分库分表扩容方案:固定槽位 + 双写迁移
MySQL 分库分表扩容方案:固定槽位 + 双写迁移讲述链条逻辑分片与物理节点解耦 → 槽位计算与路由 → 双写迁移四阶段 → 与 Redis Cluster 对比 核心表述MySQL 分库分表扩容采用固定逻辑分片 + 映射表方案,设计思路与 Redis Cluster 类似。预先定义 1024 或 4096 个逻辑槽位(2 的幂次),通过 userId % 1024 计算槽位,然后查询配置中心的映射表找到物理数据库。逻辑分片号永远不变,扩容时只改变槽位到物理库的映射关系。 假设从 4 库扩容到 8 库,初始映射 [0-255]→DB_0、[256-511]→DB_1、[512-767]→DB_2、[768-1023]→DB_3。扩容后调整为 [0-127]→DB_0、[128-255]→DB_4、[256-383]→DB_1、[384-511]→DB_5,以此类推。旧库继续保留,只是槽位重新分配。 双写迁移四阶段: 阶段 1 - 双写期:新增数据同时写旧库(主)和新库(异步),新库写入失败则加入重试队列,后续补偿。读请求优先读新库,新库没有则降级读旧库(兜底),旧库有数据则异步...
Redis 切片集群扩容:从 100 到 1000 节点的无感迁移
Redis 切片集群扩容:从 100 到 1000 节点的无感迁移讲述链条Gossip 协议维护槽位路由表 → CRC16 计算槽位(16384个) → MOVED/ASK 重定向机制 → 槽位迁移流程 → 与一致性 Hash 对比 核心表述Redis Cluster 从 100 扩展到 1000 节点可以实现无感迁移,核心在于它采用了固定槽位 + 映射表的设计。集群中固定有 16384 个 Hash Slot,通过 CRC16(key) % 16384 计算槽位,然后查询槽位路由表找到对应节点。节点数和槽位数解耦,扩容时只需重新分配槽位映射关系即可。 节点间通过 Gossip 协议维护槽位路由表,每个节点都记录所有槽位的分配情况。客户端可以连接集群中任意节点,如果访问的 key 不在当前节点,会返回 MOVED 重定向(包含槽位和目标地址),客户端更新本地缓存并重新发送请求。 槽位迁移时,源节点标记为 MIGRATING,目标节点标记为 IMPORTING,迁移期间如果 key 未迁移则正常处理,如果 key 已迁移则返回 ASK 临时重定向(客户端先发送 ASKIN...
分布式事务选型:从 2PC 到 Seata 的完整方案
分布式事务选型:从 2PC 到 Seata 的完整方案讲述链条2PC/3PC 原理与缺陷 → TCC 手动补偿 → SAGA 长事务 → Seata AT 自动补偿 → 生产环境决策树 核心表述分布式事务分为**强一致性(CP)和最终一致性(AP)**两大类。强一致性包括 2PC/3PC、XA 协议和 Seata AT 模式;最终一致性包括 TCC、SAGA、事务消息(RocketMQ)。 2PC(两阶段提交) 分为准备阶段和提交阶段。准备阶段 TC(Transaction Coordinator)向所有 RM(Resource Manager)发送 Prepare,RM 执行本地事务并锁定资源但不提交,返回 YES。提交阶段 TC 收到全部 YES 后发送 Commit,RM 提交本地事务。致命问题:同步阻塞性能差(RT 高),TC 单点故障导致资源锁死,阶段二 TC 故障可能部分 RM 提交部分未提交造成数据不一致。 3PC(三阶段提交) 增加 CanCommit 询问阶段(不锁定资源)和超时机制,RM 超时未收到 DoCommit 自动提交降低阻塞,但依...
Spring AOP 面向切面编程深度解析
Spring AOP一、AOP 基础概念AOP 就是面向切面编程,本质上是对面向对象的补充,毕竟是切面对象,AOP 可以有两种实现方法,一种是动态代理,一种是使用 Spring 来实现,Spring 的话重在会用注解,这里详细讲讲前者,如果是动态代理实现 AOP 的话首先要定义接口,毕竟这样才方便使用动态代理对象执行方法。 二、代理模式代理模式分为两种,动态代理和静态代理。动态代理是一种在运行时生成代理对象的技术,主要是生成一个增强之后的原始对象,当然还有控制访问的作用。而且动态代理有两种,一种是基于反射的 JDK 动态代理,一种是基于继承的 cglib 动态代理,当然还有静态代理,不过没有动态代理灵活。 三、JDK 动态代理实现要点JDK 动态代理的实现有三个关键点,目标接口,目标对象实现目标接口,代理对象实现目标接口,而且代理对象和目标对象是平级关系,关键类有两个,分别是 Proxy 和 InvocationHandler 类,然后就是调用 Proxy 的静态方法 newProxyInstance 创建新的代理实例,接着在 InvocationHandler 中重写 invo...
Spring Bean 生命周期深度解析
Spring Bean 生命周期生命周期完整流程1️⃣ Bean 的创建(实例化)步骤 1:实例化对象 Spring 启动,扫描或加载配置,查找并加载需要被 Spring 管理的 bean,进行 Bean 的实例化,doCreateBean() → createBeanInstance() 实例化对象。 2️⃣ 依赖注入(populateBean)步骤 2:属性赋值 Bean 实例化后对 Bean 的属性进行赋值(依赖注入),populateBean 阶段完成依赖注入,尚未执行初始化逻辑。 3️⃣ Aware 接口回调步骤 3:BeanNameAware 如果 Bean 实现了 BeanNameAware 接口的话,Spring 将 Bean 的 Id 传递给 setBeanName()方法。 步骤 4:BeanFactoryAware 如果 Bean 实现了 BeanFactoryAware 接口的话,Spring 将调用 setBeanFactory()方法,将 BeanFactory 容器实例传入。 步骤 5:ApplicationContextAware 如果 Bean ...
Spring 事务失效场景深度解析
Spring 事务失效一、底层原理Spring 事务是 AOP 实现的,通过 TransactionInterceptor 包装目标方法,生成代理对象(JDK 动态代理 / CGLIB),方法调用时先经过拦截器,根据事务回滚默认规则,决定是否开启事务、提交或回滚。 方法调用时先经过拦截器,拦截器就是 Spring 为 @Transactional 创建的 TransactionInterceptor。它被织入代理对象的拦截器链里,JDK 动态代理通过 InvocationHandler,CGLIB 通过 MethodInterceptor,调用代理对象方法时会先执行 TransactionInterceptor,然后再执行目标方法。 TransactionInterceptor.invoke() 本身只负责事务逻辑,它位于代理对象的拦截器链中。方法调用时先进入拦截器链,每个拦截器依次调用 invoke(),TransactionInterceptor 内部直接调用 method.invoke(target, args) 是为了执行链中下一个环节或最终目标方法。拦截器链的...
Spring 循环依赖问题深度解析
Spring 循环依赖什么是循环依赖循环依赖指的是两个类中的属性相互依赖对方:例如 A 类中有 B 属性,B 类中有 A 属性,从而形成了一个依赖闭环。 循环依赖的三种情况循环依赖问题在 Spring 中主要有三种情况: 第一种:通过构造方法进行依赖注入时产生的循环依赖问题 构造器注入类似原因:创建对象必须传完整依赖,提前暴露也无法注入 → 无法解决。 第二种:通过 setter 方法进行依赖注入且是在多例(原型)模式下产生的循环依赖问题 原型 Bean 循环依赖无法解决,因为容器不会缓存半初始化对象,每次 getBean 都创建新实例,无法注入引用。容器不缓存 Bean,单例 Bean 会放入 singletonObjects(一级缓存),原型 Bean 不会放入一级缓存,更不会维护二级缓存(earlySingletonObjects)或三级缓存(singletonFactories),所以每次获取原型 Bean,Spring 都是 “全新创建 + 注入 + 初始化”,容器不会提前暴露半初始化对象。 第三种:通过 setter 方法进行依赖注入且是在单例模式下产生的循环依赖问题...
Spring IOC 容器深度解析
Spring IOC一、基础介绍什么是 IOCIOC:Inversion Of Control,即控制反转,是一种设计思想。在传统的 Java SE 程序设计中,我们直接在对象内部通过 new 的方式来创建对象,是程序主动创建依赖对象而在 Spring 程序设计中,IOC 是有专门的容器去控制对象。所谓控制就是对象的创建、初始化、销毁。 创建对象: 原来是 new 一个,现在是由 Spring 容器创建。 初始化对象: 原来是对象自己通过构造器或者 setter 方法给依赖的对象赋值,现在是由 Spring 容器自动注入。 销毁对象: 原来是直接给对象赋值 null 或做一些销毁操作,现在是 Spring 容器管理生命周期负责销毁对象。 控制反转的含义IOC 解决了繁琐的对象生命周期的操作,解耦了我们的代码。所谓反转:其实是反转的控制权,前面提到是由 Spring 来控制对象的生命周期,那么对象的控制就完全脱离了我们的控制,控制权交给了 Spring 。这个反转是指:我们由对象的控制者变成了 IOC 的被动控制者。 依赖注入(DI)依赖注入(DI)是实现这种技术的一种方式。传统开...
Spring 微服务组件深度解析
Spring 微服务组件 微服务组件 注册中心 注册中心是微服务架构最核心的组件。它起到的作用是对新节点的注册与状态维护,解决了「如何发现新节点以及检查各节点的运行状态的问题」。微服务节点在启动时会将自己的服务名称、IP、端口等信息在注册中心登记,注册中心会定时检查该节点的运行状态。注册中心通常会采用心跳机制最大程度保证已登记过的服务节点都是可用的。 配置中心 配置中心主要解决了「如何集中管理各节点配置文件的问题」,在微服务架构下,所有的微服务节点都包含自己的各种配置文件,如 jdbc 配置、自定义配置、环境配置、运行参数配置等。要知道有的微服务可能可能有几十个节点,如果将这些配置文件分散存储在节点上,发生配置更改就需要逐个节点调整,将给运维人员带来巨大的压力。配置中心便由此而生,通过部署配置中心服务器,将各节点配置文件从服务中剥离,集中转存到配置中心。一般配置中心都有 UI 界面,方便实现大规模集群配置调整。 负载均衡 简单轮询 将请求按顺序分发给后端服务器上,不关心服务器当前的状态,比如后端服务器的性能、当前的负载。 加权轮询 根据服务器自身的性能给服务器设置不同...