Mysql Slow Query Filesort Optimization

IM 消息拉取接口的 SQL 优化记录 1. 背景与问题 在开发 IM 系统的“拉取历史消息”功能时,我发现当单张消息表(messages)的数据量达到百万级,且某个热点会话(conversation_id)拥有超过 10 万条消息时,接口响应出现明显卡顿。 环境信息: 数据库:MySQL 8.0+ (InnoDB) 数据量:单表 100 万行,大会话 10 万条信息 场景:用户查看最新的 20 条消息(Top-N 查询) 慢查询 SQL: SQLSELECT * FROM messages WHERE conversation_id = 'chat_hot' ORDER BY seq DESC LIMIT 20; 2. 排查过程 2.1 慢日志抓取 开启 MySQL 慢查询日志(log_output=FILE, long_query_time=0.1),捕获到该语句的执行情况: TEXT# Query_time: 0.148528 Lock_time: 0.000003 Rows_sent: 20 Rows_examined: 151676 异常点:为了返回 20 条数据,MySQL 实际扫描了 15 万行数据。扫描/返回比极低,说明索引效率存在严重问题。 2.2 执行计划分析 (EXPLAIN) 执行 EXPLAIN 查看当前索引使用情况: ...

December 28, 2025

Kafka Design Philosophy

深入理解 Apache Kafka 设计哲学 前言 Kafka 是一个分布式流处理平台,但它更像是 分布式提交日志 而非传统的消息队列。这种设计哲学贯穿了 Kafka 的每一个核心组件。本文将深入剖析 Kafka 的设计精髓。 一、存储层设计:为磁盘而生 1.1 日志结构存储 Kafka 采用 只追加写(Append-Only) 的日志结构,这带来了几个关键优势: 特性 传统数据库 (B-Tree) Kafka (Log) 写入复杂度 O(log N) O(1) 读取复杂度 O(log N) O(1) 磁盘访问模式 随机 I/O 顺序 I/O 适合场景 随机查询 流式处理 1.2 顺序 I/O 的威力 对于磁盘来说,O(log N) 并不等同于"接近常数时间"。每次磁盘寻道约需 10ms,且无法并行执行。即使少量随机访问也会导致性能急剧下降。 PLAIN顺序写磁盘: ~600MB/s 随机写磁盘: ~100KB/s 差距: 约 6000 倍! 1.3 拥抱操作系统 Page Cache Kafka 不在 JVM 堆内存中维护缓存,而是将数据直接写入操作系统的 Page Cache: graph TD subgraph JVM [JVM 进程] Logic[业务逻辑 不缓存数据] end subgraph Kernel [操作系统内核空间] PC(Page Cache 利用空闲内存 / 自动管理) FS[(磁盘文件系统)] end Logic -->|直接写入| PC PC -.->|异步刷盘 / 后写技术| FS style JVM fill:#f9f9f9,stroke:#333,stroke-width:2px style Kernel fill:#e1f5fe,stroke:#0277bd,stroke-width:2px style PC fill:#b3e5fc,stroke:#0277bd这样做的好处: ...

December 24, 2025

Rockscache Consistency

深入解析 RocksCache:如何优雅地解决缓存与数据库一致性问题 本文深入剖析 RocksCache 的设计思想与核心实现,带你理解这个首创的缓存一致性解决方案。 前言 在分布式系统中,缓存是提升性能的利器,但也是一致性问题的重灾区。你是否曾经遇到过这样的困扰: 明明更新了数据库,为什么缓存里还是旧数据? 用了「先更新DB,再删缓存」策略,为什么还是会出现不一致? 如何在保证一致性的同时,还能保持高性能? 今天介绍的 RocksCache,是一个来自 DTM Labs 的开源项目,它通过一套精巧的设计,在不引入版本号的前提下,优雅地解决了缓存与数据库的一致性难题。 一、经典的缓存一致性问题 1.1 常见的缓存策略 最常用的缓存管理策略是 Cache-Aside(旁路缓存): TEXT读取:先查缓存 → 缓存命中则返回 → 未命中则查DB → 写入缓存 → 返回 更新:更新DB → 删除缓存 这个策略看似简单,却隐藏着一个致命的并发问题。 1.2 并发场景下的数据不一致 考虑以下时序: TEXT时间 →→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→ 线程A(读请求): 查DB(v1) ─────────────────────────────→ 写缓存(v1) (网络延迟) 线程B(写请求): 更新DB(v2) → 删除缓存 问题:线程A 查询到 v1 后,发生了网络延迟。此时线程B 完成了更新并删除缓存。但线程A 的写缓存操作在删除之后执行,导致缓存中存储了旧值 v1。 这就是著名的 “删除后写入” 问题,常规的「先更新DB再删缓存」策略无法解决。 1.3 传统解决方案的局限 方案 描述 缺点 延迟双删 删除缓存后,延迟一段时间再删一次 延迟时间难以确定,仍有不一致窗口 版本号 每条数据带版本号,写入时比较版本 侵入业务,改造成本高 分布式锁 读写都加锁 性能差,热点数据成为瓶颈 订阅 binlog 通过 Canal 等订阅 DB 变更 架构复杂,延迟较高 有没有一种方案,既能保证一致性,又不侵入业务,还能保证高性能? ...

December 23, 2025

K8s Troubleshooting

前言 最近在本地虚拟机环境(CentOS 7)搭建 Kubernetes 集群运行微服务 Demo 时,遇到一个非常诡异的“灵异事件”。 起因:由于宿主机休眠,我挂起(Suspend)了一段时间虚拟机。 现象:恢复运行后,集群状态看起来一切正常(Node Ready,Pod Running),但访问 NodePort 暴露的服务时,直接报 502 Bad Gateway。 排查过程极其曲折,从 HTTP 协议一路查到 Linux 内核参数,最终发现是操作系统在网络重置时的“安全机制”坑了 Kubernetes。本文记录了完整的排查思路,希望能帮大家避坑。 🕵️‍♂️ 第一阶段:表象排查(Application Layer) 首先,我尝试访问前端服务: BASHcurl -I http://192.168.6.141:30007/ # HTTP/1.1 502 Bad Gateway 初步分析: 502 通常意味着网关(Service/Kube-proxy)找不到后端 Pod,或者连接被拒绝。 检查 Pod 状态:kubectl get pods -A 显示所有 Pod 均为 Running 且 Ready (1/1)。应用没挂。 检查 Service 关联:kubectl get ep frontend 显示 Endpoints 存在且 IP 正确(如 10.244.104.23:8080)。 看日志:查看 Frontend Pod 日志,没有报错,甚至显示 Server started。 奇怪点:应用活着,配置没动过,重启前好好的,恢复后就断了。 ...

December 23, 2025