[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fEAz4ewlWBCaUM0qdddh8eXZmMYOxZGQMuF_8_U1u_iE":3},{"item":4,"related":48},{"id":5,"type":6,"title":7,"slug":8,"summary":9,"body":10,"coverUrl":11,"productScreenshots":12,"productLinks":13,"authorName":14,"authorUrl":15,"authorSubject":16,"category":17,"tags":22,"sourceLabel":39,"sourceName":40,"sourceUrl":41,"status":42,"seoTitle":7,"seoDescription":9,"canonicalUrl":39,"isFeatured":43,"sno":44,"sortOrder":45,"publishedAt":46,"updatedAt":47,"createdAt":47},"74199187-0495-4196-a10c-6c66c471d04a","article","LSM-tree：为什么高写入数据库要先记下来，再慢慢整理？","lsm-tree-log-structured-merge-explained","LSM-tree 先把写入放入内存结构和顺序日志，再通过后台 Compaction 合并成有序文件，以此降低随机写成本。本文解释 memtable、SST、WAL、读放大和写放大，说明 RocksDB 一类存储引擎的设计取舍。","高写入量数据库通常不急着把每一条新数据都插入一个巨大的有序索引。那样会产生大量随机写入。LSM-tree，Log-Structured Merge-tree，日志结构合并树，选择先把新写入放到内存结构和顺序日志里，之后再把数据批量整理成有序文件。\n\n## 写入路径是怎样的\n\n以 RocksDB 的典型结构为例，新写入先进入 memtable，同时可以写入 WAL。memtable 达到一定大小后，被刷新成排序好的 SST 文件。后台线程再把多层文件合并、压缩和清理旧版本，这个过程通常称为 compaction。\n\n它的优势是把很多小的随机写，转化为更适合存储设备的顺序写和批量合并。对日志、事件、时序数据和键值写入密集型负载，这种设计很有吸引力。\n\n## 为什么读和后台整理会变复杂\n\n一次读取可能需要查询内存表和多个 SST 文件。系统会用 Bloom Filter、索引块和缓存减少不必要的磁盘访问。后台 compaction 还会占用 I\u002FO 与 CPU，如果整理跟不上写入，文件层数、读放大和写放大都会增加，甚至出现写入停顿。\n\n因此 LSM-tree 不是“写入快就完事”。工程师需要在写放大、读放大、空间放大、压缩策略和延迟稳定性之间做取舍。不同业务的读写比例、键分布和删除模式，都会影响最合适的配置。\n\n## 它和 WAL 是什么关系\n\nWAL 主要解决崩溃恢复，LSM-tree 主要解决高写入下的数据组织。两者经常一起出现，但不是同一个概念：日志保证“已经接受的变化有迹可循”，LSM 层次负责“如何把变化整理成可查询的持久结构”。\n\n## AI 编程的启发\n\n当 AI 建议把所有数据都写进缓存或队列时，LSM-tree 提醒我们必须考虑后续整理成本。快速接收只是第一阶段，后台压缩、恢复、查询和空间回收同样属于系统设计。\n\n## 读者应该记住\n\nLSM-tree 的核心是“先顺序接收，再批量合并”。它把写入性能换成后台整理和读取复杂度，是许多现代键值存储引擎的底层骨架。\n\n资料：[RocksDB Overview](https:\u002F\u002Fgithub.com\u002Ffacebook\u002Frocksdb\u002Fwiki\u002FRocksDB-Overview)；[LSM-tree 原始论文](https:\u002F\u002Fdb.cs.berkeley.edu\u002Fcs286\u002Fpapers\u002Flsm-acta1996.pdf)","\u002Fuploads\u002F2026-09-20\u002Fa4d6ac29-84ce-43e7-abdd-177b324da0c2.jpg",[],[],"Foundit","https:\u002F\u002Ffoundit.cn","foundit-ai-editorial",{"id":18,"name":19,"slug":20,"description":21},"6179d3b6-dc34-4483-9ded-3cd9f1b37a47","科普","abbreviation","介绍各领域新兴概念",[23,27,31,35],{"id":24,"name":25,"slug":26},"a202d639-99a6-488a-a712-4d4c6ffd7e15","开发","dev",{"id":28,"name":29,"slug":30},"7c76bfc2-f80f-4ee0-a95d-27bd8708b434","技术","slug",{"id":32,"name":33,"slug":34},"4c2bbea6-eab7-40a8-8447-1de478ff7749","分析","analyse",{"id":36,"name":37,"slug":38},"144abe77-0dc6-4f66-a176-20bddb1c0bfa","编程","coding",null,"RocksDB Overview","https:\u002F\u002Fgithub.com\u002Ffacebook\u002Frocksdb\u002Fwiki\u002FRocksDB-Overview","published",false,56,0,"2026-09-20T00:00:00.000Z","2026-09-20T03:58:17.176Z",[49,57,65],{"id":50,"type":6,"title":51,"slug":52,"summary":53,"coverUrl":54,"authorName":14,"sno":55,"publishedAt":46,"createdAt":56},"d0700249-defa-43a1-a66a-4455c6889072","ABI：为什么源码能编译，二进制却不能互相调用？","application-binary-interface-abi-explained","ABI 是二进制世界的调用合同，规定参数传递、对象布局、符号命名和异常处理。本文区分 ABI 与 API，解释动态库、C++ 兼容性和跨语言绑定为什么不能只看函数签名。","\u002Fuploads\u002F2026-09-20\u002F33666a48-4e8f-42a3-9ffd-f4c490498689.jpg",42,"2026-09-20T03:58:01.058Z",{"id":58,"type":6,"title":59,"slug":60,"summary":61,"coverUrl":62,"authorName":14,"sno":63,"publishedAt":46,"createdAt":64},"c9f4e936-9533-4b9c-a1de-f03ef09fed37","WAL：为什么数据库要先写日志，再写真正数据？","write-ahead-logging-wal-database-explained","WAL 要求描述数据变化的日志先于数据页持久化，让数据库可以延迟刷写并在崩溃后通过重放恢复。本文用账本和收据解释 REDO、检查点、复制与持久性设置的关系。","\u002Fuploads\u002F2026-09-20\u002F6260c953-345b-4c9d-b3d6-6202c7e5539c.jpg",43,"2026-09-20T03:58:15.082Z",{"id":66,"type":6,"title":67,"slug":68,"summary":69,"coverUrl":70,"authorName":14,"sno":71,"publishedAt":46,"createdAt":72},"41d50774-50df-4e2f-b1b3-ab6d9f329726","Backpressure：生产者太快时，系统怎样不被数据淹没？","backpressure-reactive-streams-explained","Backpressure 让下游处理能力反过来影响上游生产速度，避免异步流水线靠无限缓存硬撑。本文用水管和阀门解释响应式流、需求信号、数据丢弃与容量设计，也说明它和普通限流的区别。","\u002Fuploads\u002F2026-09-20\u002F35c47792-3d4b-46ae-9375-c68e4c53330e.jpg",45,"2026-09-20T03:58:08.835Z"]