MySQL 主从复制延迟排查与优化实战:基于 GTID 的读写分离一致性保障
MySQL 主从复制延迟排查与优化实战:基于 GTID 的读写分离一致性保障从一个“支付后订单不存在”说起某天下午,客服反馈有用户支付成功后,立即在“我的订单”页面查不到刚刚的订单。几分钟后再刷新,订单又出现了。后端同学排查日志:写请求落到了主库,读请求走了从库,但从库还没同步完这笔数据。这就是典型的主从复制延迟导致的读写分离不一致问题。
读写分离能极大提升读扩展能力,但主从延迟像一颗“定时炸弹”。本文结合一次真实的生产排查经历,系统梳理 GTID 机制下的延迟排查方法,并落地一套“延迟阈值切换 + 强制走主库 + 半同步复制”的读写分离一致性保障方案。
1. 主从复制原理与 GTID 机制回顾MySQL 默认异步复制:主库写入 binlog,从库通过 IO 线程拉取 binlog 写入 relay log,再通过 SQL 线程回放。从库回放速度低于主库写入速度时,就产生延迟。
传统基于位点的复制(MASTER_LOG_FILE + MASTER_LOG_POS)在主机切换或拓扑变更时容易错位。GTID(Global Transaction Identifier)为每个事务分配全局唯一 ...
Redis 缓存策略对比实战:Cache Aside、Read/Write Through 与 Write Behind 在 MySQL 高并发读场景下的选型与实现
Redis 缓存策略对比实战:Cache Aside、Read/Write Through 与 Write Behind 在 MySQL 高并发读场景下的选型与实现某电商商品详情接口 QPS 从 200 涨到 5000,MySQL 的 CPU 直接飙到 85%。加了一层 Redis 缓存后,接口响应从 120ms 降到 8ms,MySQL 负载降了 70%。但随之而来的是一个经典问题:缓存和数据库的一致性到底怎么保证?
这篇文章不讨论「缓存的 7 大经典问题」这种大而全的清单,而是聚焦三种缓存读写模式的真实代码实现和选型决策。每个模式我都会给出可直接运行的 Java 代码,并在最后说明它们各自的数据安全边界。
先看一张总览图:
12345678910111213141516┌─────────────────────────────────────────────────────────────────┐│ 三种缓存策略的核心差异 │├──────────────┬───────────── ...
Doris 冷热分层存储实战:基于对象存储的数据生命周期管理与查询性能权衡
Doris 冷热分层存储实战:基于对象存储的数据生命周期管理与查询性能权衡先说一个真实场景:我们有一张订单明细表,每天新增约 2000 万行,数据量以每月 6 亿行的速度增长。业务上,最近 7 天的订单查询非常频繁,需要毫秒级响应;90 天内的数据偶尔会做运营分析;更早的数据基本只有审计和合规需求,一年也查不了几次。如果所有数据都放在 SSD 上,存储成本高得离谱;全部放机械盘又会影响热数据查询。这种场景下,冷热分层几乎是必选项。
Doris 从 1.2 版本开始支持基于对象存储(S3/COS/OBS 等)的冷热分层。核心思路是:热数据留在本地 SSD/HDD,冷数据按照策略自动迁移到对象存储,查询时 Doris 可以透明地读取远程数据。本文基于 Doris 2.0.3 搭建一套完整的冷热分层实战环境,从存储策略配置、分区冷却、查询性能对比到数据保留策略,一步步带你落地。
1. 冷热分层的架构与原理先看一张架构图,直观理解 Doris 冷热分层的工作方式:
12345678+----------------+ +---------------- ...
MySQL InnoDB MVCC 原理与一致性读实战:从 undo log 多版本链到 ReadView 可见性判断
MySQL InnoDB MVCC 原理与一致性读实战:从 undo log 多版本链到 ReadView 可见性判断从一个”诡异”的读取现象说起先看一个在生产环境中经常让开发困惑的场景。
某天,同事跑来问我:”我在事务里更新了一行数据,还没提交,为什么另一个事务读到的还是旧值?是不是 MySQL 有缓存?”
这不是缓存问题,而是 MVCC(Multi-Version Concurrency Control,多版本并发控制) 在起作用。来看一个最小复现:
准备环境:MySQL 8.0,隔离级别为默认的 REPEATABLE READ。
12345678910111213141516-- 会话 ACREATE DATABASE IF NOT EXISTS mvcc_demo;USE mvcc_demo;CREATE TABLE account ( id INT PRIMARY KEY, balance INT NOT NULL, name VARCHAR(50) NOT NULL) ENGINE=InnoDB;INSERT INTO account VALUES (1, ...
后端工程师的下一个瓶颈:不是技术,而是思维模式
后端工程师的下一个瓶颈:不是技术,而是思维模式上周五晚上十一点,我盯着慢查询监控大盘发呆。一个跑了 3.2 秒的 SQL 被我连续优化了四轮——索引重建、子查询拆解、连表顺序调整、分区裁剪——执行时间降到了 0.8 秒。就在我准备合代码关电脑的时候,产品经理发来一条消息:“这个报表接口能不能再快一点?用户说打开要等两秒。”
我看着那行已优化的执行计划,突然意识到一个让人不舒服的事实:我把 SQL 调优到了物理层面的极限,但用户感受到的“慢”和我的“快”根本不在一张坐标图上。工具调优的熟练度越高,这种错位越明显。
当“会调”变成一种惯性过去一年我密集输出了不少 MySQL、Doris、Redis 的实战文章。从执行计划分析到 Buffer Pool 命中率,从 Doris 的物化视图到 Redis 的内存碎片治理,这些文章的阅读反馈都不错,也让我形成了一套固定的解题路径:遇到问题 → 定位瓶颈 → 调参数 / 改写法 / 加缓存 → 验证指标 → 沉淀文章。
这套路径本身没有问题,问题在于它太顺手了。顺手到我开始不自觉地用它解决所有问题——包括那些根本不该用调优来解决 ...
消息队列幂等消费与消息去重实战:基于 MySQL 订单系统的 Redis + RabbitMQ 方案
消息队列幂等消费与消息去重实战:基于 MySQL 订单系统的 Redis + RabbitMQ 方案一、问题场景:一条延迟消息引发的“血案”订单系统里有一个经典场景:用户下单后 30 分钟未支付,系统自动关单并释放库存。
我们采用 RabbitMQ 延迟队列(通过死信交换机 + TTL 实现)来触发关单逻辑。架构如下:
1订单服务 ──发布延迟消息──> RabbitMQ(TTL 30min)──> 死信交换机 ──> 关单队列 ──> 关单消费者
这个方案本身没有问题,但上线后我们遇到了三类典型的重复消费场景:
场景
原因
后果
消息重复投递
网络抖动导致 Producer 重试,RabbitMQ Broker 未收到 ack 但消息已入队
同一条消息被投递两次
消费者重试
关单逻辑执行成功但 ack 超时,Broker 重新投递
关单操作执行两次
并发消费
消费者实例水平扩容,同一消息被不同实例同时拉取(极端情况)
库存重复释放
这些问题的本质是:消息中间件保证的是“至少一次投递”(At-Least-Once),而不保证“恰好一次” ...
Redis 大 Key 与热 Key 治理实战:基于 MySQL 高并发读场景的内存分析与拆分方案
Redis 大 Key 与热 Key 治理实战:基于 MySQL 高并发读场景的内存分析与拆分方案一、问题场景:一场大促引发的缓存危机某电商系统大促期间,商品详情接口 QPS 从日常 500 飙升到 2 万。为了扛住高并发读,团队在 MySQL 前加了 Redis 缓存。起初效果显著,数据库压力骤降,但活动开始一小时后,Redis 延迟出现周期性毛刺,部分请求响应从 2ms 劣化到 200ms,甚至出现超时。监控显示 Redis 实例 CPU 使用率接近 100%,但整体 QPS 并不高。
排查发现两个典型问题:
某个商品详情缓存 value 是一个完整的 JSON,包含商品基础信息、SKU 列表、评论摘要、推荐列表等,序列化后超过 5MB,属于典型的大 Key。
该商品正好是活动主推款,单 key 的 QPS 超过 8000,属于典型的热 Key。
大 Key 导致 Redis 单线程在处理该 key 的读写时占用大量 CPU 时间,热 Key 又使这个操作被高频触发,最终阻塞其他请求。这引发了我们对 Redis 大 Key 与热 Key 的系统性治理。
二、大 Key 与热 K ...
消息队列消息积压治理实战:RabbitMQ 消费者扩容、死信队列与监控告警
消息队列消息积压治理实战:RabbitMQ 消费者扩容、死信队列与监控告警故障复盘:订单状态机“卡死”的深夜凌晨 1 点,大促流量高峰刚刚过去,客服群里开始刷屏:“用户支付成功了,但订单状态一直是‘待支付’!”、“退款流程走不了,用户要投诉。” 打开 RabbitMQ 管理界面,心跳瞬间加速:order.event.queue 队列深度已经突破 300 万,消费者速率只有 200 条/秒,而生产者速率峰值时达到 5000 条/秒。更可怕的是,积压还在以肉眼可见的速度增长。
这是我们团队在订单系统上遇到的一次真实生产事故。系统拓扑很简单:用户支付成功后,支付回调服务将“支付成功”事件投递到 RabbitMQ,订单服务消费该事件,更新订单状态并触发后续流程(库存扣减、积分发放、通知用户)。问题出在订单服务这一环——单条消息处理时间从正常的 50ms 恶化到 2 秒以上,因为数据库查询没走索引,外部物流接口超时未设置合理超时时间,加上预取参数默认值过大,导致消费者不堪重负。
这次故障让我深刻体会到:消息积压不是消息队列的问题,而是消费端系统问题的放大镜。消息队列忠实地把上 ...
Doris Workload Group 资源隔离实战:查询并发控制与 CPU/内存配额调优
Doris Workload Group 资源隔离实战:查询并发控制与 CPU/内存配额调优一、一次大查询引发的线上事故上个月某个周五下午,我们的实时数据看板突然疯狂告警:查询延迟从 200ms 飙升到 8 秒,部分接口直接超时。与此同时,数据团队正在跑一个全量用户行为分析 SQL,扫描了 2 亿行数据,还带了多个 JOIN 和 GROUP BY。
排查发现,Doris 集群的 BE 节点 CPU 全部打满,内存使用率接近警戒线,部分大查询甚至触发了 BE 进程 OOM。小查询(点查、简单聚合)和这个大查询在同一批 BE 上运行,没有任何隔离,资源被大查询全部抢占,导致在线业务雪崩。
这就是典型的资源争抢问题:没有资源隔离时,Doris 中所有查询默认使用同一个资源池,大查询会无限制地消耗 CPU 和内存,拖垮整个集群。
解决思路就是引入 Workload Group(负载组),对不同类型的查询进行资源配额和并发控制。
二、Doris Workload Group 是什么Workload Group 是 Apache Doris 2.0 引入的资源隔离机制,它允许我们将查询按 ...
Redis ZSet 延迟队列实战:MySQL 订单超时关单的可靠实现与重试机制
Redis ZSet 延迟队列实战:MySQL 订单超时关单的可靠实现与重试机制订单超时自动关闭是电商系统里最典型的延迟任务场景:用户下单后如果 30 分钟未支付,系统需要自动取消订单并释放库存。这个需求看似简单,但实现起来要考虑可靠性、幂等性、重试机制和最终一致性。本文以 Spring Boot 3 + MySQL 8 + Redis 7 为技术栈,手写一个基于 Redis ZSet 的延迟队列,完成订单超时关单的完整闭环。
一、为什么不用现成的消息队列?延迟任务实现方案有不少,我们列举最常见的三种:
方案
实现复杂度
可靠性
延迟精度
适用规模
备注
Redis ZSet 轮询
低,自己写轮询
中,需要自行处理 ACK/重试
秒级
中小规模
无额外中间件,运维成本低
Redisson DelayedQueue
低,封装好
高,自带 ACK/重试
毫秒级
中大规模
依赖 Redisson,侵入业务代码
RabbitMQ 延迟插件
中,需要安装插件
高,依赖 MQ 本身
秒级
大规模
需要运维 RabbitMQ,增加系统复杂度
对于创业初期或 ...
