[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fk0dE0PcMxKBAcI72KZOdGUz9Bj34p6qZl32cGngdB-w":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},"b9e28c66-089a-4679-86aa-3631d1705db9","article","MVCC：数据库如何让读写同时发生，却尽量互不阻塞？","mvcc-multiversion-concurrency-control-explained","MVCC 通过保存多个数据版本，让查询读取符合事务规则的快照，从而减少读写之间的阻塞。本文解释版本可见性、旧版本清理、隔离级别和长事务风险，帮助读者理解现代数据库的并发基础。","数据库要同时服务很多读者和写入者，却不能让每一次查询都排队等锁。MVCC，Multiversion Concurrency Control，多版本并发控制，提供了一种直观的办法：不要让所有人盯着同一份正在被修改的纸，而是让读者看到符合事务规则的某个数据快照。\n\n## “多版本”到底是什么\n\n当一行数据被更新时，数据库可以保留旧版本信息，并给新旧版本附带事务可见性标记。一个查询根据自己的快照判断哪些版本可见，因此两个查询可能在同一时刻看到不同但各自一致的结果。\n\nPostgreSQL 文档把 MVCC 描述为一种维护并发一致性的多版本模型：每条 SQL 语句可以看到某个时间点的数据，而不是直接暴露其他事务正在写入的中间状态。读取和写入因此可以减少相互阻塞。\n\n## 它为什么需要清理\n\n旧版本不是永远有用。等到没有活跃事务再需要它们，数据库就要通过 vacuum 或类似机制回收空间、更新统计信息并维护索引。如果清理跟不上，表会膨胀，查询和写入都可能变慢。\n\nMVCC 也不是完全没有锁。更新同一行仍然需要协调，DDL、唯一约束和更高隔离级别也会引入额外冲突。它提供的是一种减少读写冲突的基础模型，不是“所有并发都免费”。\n\n## AI 编程中的实际意义\n\nAI 生成数据库代码时，常常只写出 SQL，却没有说明事务隔离、快照可见性、长事务和清理策略。理解 MVCC 后，开发者会更警惕“查询一直开着不提交”“后台任务持有旧快照”“读到旧数据是否可接受”等问题。\n\n## 读者应该记住\n\nMVCC 用多个版本换取更好的并发读写体验。它让读者看到一致快照，但也带来了版本清理、空间增长和隔离级别选择等成本。\n\n资料：[PostgreSQL：MVCC 简介](https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002F14\u002Fmvcc-intro.html)；[PostgreSQL 并发控制](https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002F18\u002Fmvcc.html)","\u002Fuploads\u002F2026-09-20\u002F807a3645-feec-47b9-8052-c26f2bdea821.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,"PostgreSQL：MVCC 简介","https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002F14\u002Fmvcc-intro.html","published",false,51,0,"2026-09-20T00:00:00.000Z","2026-09-20T03:58:13.519Z",[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"]