当前位置: 首页 > news >正文

文章阅读与实践 - 延迟双删/分库分表/Spring IOC生命周期/Mysql主从一致优化

延迟双删

文章来源:延迟双删如此好用,为何大厂从来不用

大家都推荐“删缓存”,但大厂为啥不用?

数据库改了数据,就顺手把缓存删掉,下次读的时候再从数据库拿,重新写进缓存。这叫 Cache-Aside 模式,听着挺合理,对吧?更高级点的,还有个叫“延迟双删”的招:

  • 第一次:数据改完,立刻删缓存;
  • 第二次:等个1~2秒,再删一次缓存。

是为了防止:改数据的时候,刚好有人在读旧缓存,然后又把旧数据写回去,导致不一致。但是很多公司根本不这么做。

延迟双删的“坑”在哪?—— 缓存穿透 + 数据库爆炸

想象一下这个场景:你删了缓存 → 用户来查 → 缓存没了 → 直接打到数据库 → 数据库压力暴增;如果这时候系统本来就在高并发(比如双十一、抢票),那一瞬间成千上万请求全冲进数据库,直接把你库干趴。这叫“缓存穿透”——本来用缓存是为减轻数据库压力,结果你一删,反而让数据库扛不住了。

而且,延迟时间很难拿捏

  • 等太久:数据不一致的时间就长;
  • 等太短:可能还没改完,第二次删就白删了。

所以,对小系统来说,“延迟双删”还能凑合用;但对大流量系统来说,这招太危险,不能靠它保命。

 Facebook的解法:搞个“租约”机制,像排队领号

Facebook 想了个聪明办法,叫 Lease(租约)机制,你可以理解成“谁先申请,谁才能写”。

  • 当缓存里没数据时,不是直接让所有人去查数据库,而是:
    • 返回一个“token”(令牌),相当于排队号;
    • 只有拿到这个号的人,才能去数据库查数据,并写回缓存;
    • 其他人只能等着,不能乱动。

这就避免了“大家一起查数据库、大家一起写缓存”的混乱场面。用 Redis 实现的话,要点是:

  • 查缓存前,先看有没有“租约”;
  • 写缓存时,要验证自己是不是“持号者”;
  • 改完数据库后,不仅要删缓存,还要把“租约”也删掉。

好处是 - 减少数据库压力,避免多个请求同时重建缓存(惊群效应)。

缺点呢? 实现复杂,要用 Lua 脚本保证原子性; 代码变麻烦,要处理 token、解析返回值等。但对 Facebook 这种量级来说,这点复杂度值得。

 Uber 的做法:加个“版本号”,谁新谁上位

Uber 的思路更直接:给每条数据加个“时间戳”当版本号。每次往缓存写数据前,先比一下版本:

  • 如果你要写的这条数据,比缓存里的“版本旧”,那就别写了;
  • 只有“新版本”才允许写进去。

这样就能防止:A改了数据 → 删缓存 → B读到旧数据 → 写回缓存” 把旧数据又塞回去。

实现方式:

  • 用 Lua 脚本,在 Redis 里存两个 key:
    • 一个是真正的数据;
    • 一个是它的版本号(比如数据库的时间戳);
  • 写缓存时,脚本自动比对版本,只让新的写进去。

Uber还有个叫 CacheFront 的系统,能异步监听数据库变更,自动更新缓存。所以哪怕你不用“删缓存”,它也能保证缓存最终一致。

方案适合场景缺点
删除缓存 + 延迟双删小公司、低流量系统容易穿透缓存,压垮数据库
Facebook 租约机制高并发、怕惊群实现复杂,要改代码逻辑
Uber 版本号机制数据有时间戳,想精准控制要额外存版本,设计稍复杂

分库分表

文章来源:大厂高频面试题:为什么要分库分表?

分表:解决数据量太大的问题

当你发现一张表里的数据太多(比如几千万甚至上亿条),查询变得非常慢,即使你做了优化也不行,这时候就需要考虑分表了。

分表的方法

1.垂直拆分

  • 做什么:把一个有很多列的大表拆成几个小表。
  • 怎么做:遵循“冷热分离”的原则,把常用的、重要的信息放在主表里,不常用的信息放在扩展表里。
  • 举例:用户表可以拆成用户主表(存ID、手机号、密码等常用信息)和用户扩展表(存个人简介、头像、地址等不常用信息)。
  • 注意:最好在设计数据库的时候就想好怎么分,以后再改会很麻烦。

2.水平拆分

  • 做什么:把一个有很多数据的表按照某种规则分成多个结构相同的表。
  • 怎么做
    • 按范围:比如按ID区间分,1-1000万的数据放表1,1001-2000万的数据放表2。
    • 按哈希:对某个关键字段(如用户ID)取哈希值,然后根据哈希值决定数据放到哪个表。这种方式最常用,数据分布比较均匀,但扩容有点复杂。
    • 按时间:比如订单表可以按月或年创建新表,这样管理和查询都很清晰。

3.小结

分表的目的就是让单个表变小,提高查询速度。

分库:解决并发压力太大的问题

什么时候需要分库?

当一个数据库实例的连接数和每秒查询次数(QPS)太高,超过了硬件和MySQL能承受的上限,导致数据库响应慢甚至宕机,这时候就需要分库了。

怎么做分库?

按业务拆分是最推荐的方式。比如一个电商系统可以拆分成用户库、商品库、订单库、支付库。

好处:

  • 资源隔离:一个业务库的高负载不会影响其他业务库。
  • 降低耦合:业务边界清晰,方便独立开发和维护。
  • 提升并发:把访问压力分散到多个数据库实例上,整体处理能力增强。

小结

分库的目的是把访问压力分散到不同的数据库实例上,提高系统的并发处理能力和稳定性。

总结

分表是为了解决数据量太大导致的查询性能问题,分库是为了解决并发压力太大导致的数据库性能瓶颈。在实际应用中,我们常常会结合使用这两种方法,比如先按业务拆分出订单库,再对订单表进行水平拆分。

当然,分库分表也会带来一些新的挑战,比如分布式事务、跨库JOIN查询、全局唯一ID等问题,这些就需要用更复杂的中间件或架构方案来解决了。

Spring IOC的生命周期

文章来源:再探Spring IOC的生命周期

IOC容器的运作过程可以梳理成四个清晰阶段。

第一阶段:准备阶段 - 收集Bean蓝图

就像建房子前先画设计图,Spring在这个阶段:

  • 扫描所有配置(XML、注解、配置类)

  • 解析并创建BeanDefinition(Bean的"身份证")

  • 将这些定义注册到容器工厂中

  • 执行BeanFactoryPostProcessor(可以修改Bean定义的"超级管理员")

关键点:BeanFactoryPostProcessor有优先执行权,可以修改Bean的属性、作用域等元信息,但不能直接操作Bean实例。

第二阶段:创建阶段 - Bean的诞生

这个阶段真正创建Bean实例,步骤很精细:

  1. 实例化(调用构造函数"出生")

  2. 属性填充(注入需要的依赖)

  3. Aware接口回调(让Bean知道自己叫什么、在哪)

  4. 初始化前处理(BeanPostProcessor的before方法)

  5. 执行初始化(InitializingBean接口和@PostConstruct)

  6. 初始化后处理(BeanPostProcessor的after方法)

有趣发现:通过断点追踪,我发现@Async和@Transactional注解的代理对象创建时机不同,说明不同功能的增强处理时机是有差异的。

第三阶段:使用阶段 - Bean的工作期

  • 业务代码通过依赖注入或直接获取使用Bean

  • 懒加载的Bean在第一次被访问时才初始化

  • 这是Bean"奉献价值"的主要阶段

第四阶段:销毁阶段 - Bean的退休

容器关闭时:

  • 先触发@PreDestroy标记的方法

  • 再调用DisposableBean接口的destroy方法

  • 最后释放资源,等待GC回收

web环境特色:在web应用中,容器关闭是通过ServletContextListener监听的,Spring Boot还提供了/actuator/shutdown端点来优雅停机。

实际案例:RocketMQTemplate的销毁

RocketMQTemplate实现了DisposableBean接口,在destroy()方法中优雅地关闭了生产者和消费者,释放了所有资源,这正是生命周期终点处理的完美示例。

总结

Spring IOC生命周期可以简记为:先注册后处理再创建,使用时才初始化,关闭前先销毁。理解这四个阶段及其扩展点,就像掌握了Spring容器的工作节奏,能够更好地在合适时机介入处理,写出更优雅的Spring应用。

Mysql主从一致优化

文章来源:美团一面:Mysql主从一致性问题如何优化?四种优化方案分享

问题根源:主从延迟

  • 主库写完了,从库还没同步完,中间有个时间差。

  • 如果你在这段时间内读从库,就会读到“旧数据”。

方案一:接受延迟,不管了

  • 做法:不做特殊处理,允许短暂的不一致。

  • 比喻:就像发朋友圈,你发了但别人晚几秒才看到,没关系。

  • 适用:点赞数、浏览量、非核心数据。

  • 优点:简单、快。

  • 缺点:可能读到旧数据。

方案二:半同步复制

  • 做法:主库写完后,等至少一个从库确认收到日志,再告诉客户端“写好了”。

  • 比喻:你发通知后,必须等一个人回复“收到”,你才放心。

  • 适用:对数据可靠性要求高,能接受稍微慢一点的写入。

  • 优点:数据更安全,一致性更好。

  • 缺点:写操作变慢。

方案三:所有读都走主库

  • 做法:不管什么情况,读写全部走主库。

  • 比喻:所有问题都直接找老板,不找秘书。

  • 适用:金融、订单等绝对不能读旧数据的场景。

  • 优点:绝对一致。

  • 缺点:主库压力大,得加缓存缓解。

方案四:智能判断读主还是读从

  • 做法

    • 写的时候,在缓存(如Redis)里打个标记(例如:这个数据刚改过,5秒后过期)。

    • 读的时候,先查标记:

      • 有标记 → 读主库(保证读到最新的)

      • 没标记 → 读从库(减轻主库压力)

  • 比喻:你刚更新了简历,HR 会直接找你确认最新情况;过了一段时间,就直接看档案了。

  • 适用:既要一致性又要性能的场景。

  • 优点:灵活、智能、兼顾性能与一致。

  • 缺点:系统变复杂,要写更多代码。

http://www.xdnf.cn/news/1427041.html

相关文章:

  • 一文读懂 LoRaWAN A、B、C类的区别及应用
  • 用 PyTorch 实现食品图像分类:从数据预处理到模型训练与预测
  • Linux电脑怎样投屏到客厅的大电视?支持远程投屏吗?
  • 从Java全栈到前端框架:一场真实的技术面试实录
  • 《Vue进阶教程》(7)响应式系统介绍
  • iOS15如何绕过MDM锁?详细图文教程教你搞定
  • 滚珠导轨在工业制造领域如何实现高效运行?
  • 网络传输的实际收发情况及tcp、udp的区别
  • 电子电气架构 --- 当前企业EEA现状(上)
  • 云计算学习笔记——Linux系统网络配置与远程管理(ssh)篇
  • Java搭建高效后端,Vue打造友好前端,联合构建电子采购管理系统,实现采购流程电子化、自动化,涵盖采购全周期管理,功能完备,附详细可运行源码
  • Node.js 命令行交互王者:inquirer 模块实战指南
  • Pytorch Yolov11目标检测+window部署+推理封装 留贴记录
  • WPF迁移avalonia之图像处理(一)
  • 基于SpringBoot的古典舞在线交流平台
  • C++革命性新特性:默认实例导出(exportDefault)让单例模式变得无比简单!
  • Redis 缓存雪崩实战:从监控告警到3层防护的完整修复
  • Docker镜像指南:从核心命令到离线迁移实战
  • ⸢ 肆 ⸥ ⤳ 默认安全:安全建设方案 ➭ a.信息安全基线
  • 工业视觉光源选色指南:白光通用、蓝光显瑕疵、红光能穿透,看完直接用
  • 《水浒智慧》(第一部:梁山头领那些事儿)读书笔记
  • 【面试场景题】外卖平台如何扛住高峰期上千qps订单查询流量
  • 【硬件测试】基于FPGA的16PSK+卷积编码Viterbi译码硬件片内测试,包含帧同步,信道,误码统计,可设置SNR
  • 数据结构之单链表的应用(一)
  • 服务器CPU飙高?排查步骤与工具推荐
  • 一、Scala 基础语法、变量与数据类型
  • 智能化企业级CRM系统开发实战:飞算JavaAI全流程体验
  • 苹果内部 AI聊天机器人“Asa”曝光,为零售员工打造专属A
  • vscode炒股插件-韭菜盒子AI版
  • Coze源码分析-工作空间-资源查询-前端源码