[{"data":1,"prerenderedAt":444},["ShallowReactive",2],{"learn-concept-/zh/learn/optimistic-concurrency":3},{"id":4,"title":5,"body":6,"date":379,"description":380,"domain":381,"domainKey":382,"extension":383,"featured":167,"fullName":384,"interaction":385,"maturity":386,"mentalModel":387,"meta":388,"navigation":167,"neighbors":389,"ogImage":420,"path":421,"published":167,"robots":420,"seo":422,"shortName":423,"sitemap":424,"sources":425,"stem":438,"tags":439,"translationKey":385,"updated":379,"__hash__":443},"learnZh/zh/learn/optimistic-concurrency.md","乐观并发控制",{"type":7,"value":8,"toc":369},"minimal",[9,12,16,19,24,32,45,53,56,59,80,121,124,135,139,146,179,186,189,267,270,278,281,284,287,304,307,310,313,316,319,345,348,365],[10,11,5],"h1",{"id":5},[13,14,15],"p",{},"Optimistic Concurrency Control（OCC，乐观并发控制）解决的是一个很容易被低估的问题：两个人读到了同一份记录、分别做了修改，然后在不知情的情况下抹掉了对方的工作。",[13,17,18],{},"它最关键的动作不是在大家思考和编辑时锁住记录，而是在真正提交的那一刻验证：写入者开始工作时所依赖的前提，现在是否仍然成立？",[20,21,23],"h2",{"id":22},"它要阻止的失败lost-update","它要阻止的失败：Lost Update",[13,25,26,27,31],{},"假设 Alice 和 Bob 都读取了文档 ",[28,29,30],"code",{},"#42"," 的 version 7：",[33,34,35,39,42],"ul",{},[36,37,38],"li",{},"Alice 修改标题并率先保存，数据库前进到 version 8。",[36,40,41],{},"Bob 仍在编辑旧的 version 7。他修改 owner，然后把整个旧对象提交回来。",[36,43,44],{},"如果系统直接接受 Bob 的写入，Alice 的新标题就可能悄悄消失。",[13,46,47,48,52],{},"这叫 ",[49,50,51],"strong",{},"Lost Update（丢失更新）","。危险的不是两个人同时读取，而是 Bob 的写入所依赖的前提已经过期，系统却仍然允许它成功。",[20,54,55],{"id":55},"它怎样工作",[13,57,58],{},"OCC 通常分成三个阶段：",[60,61,62,68,74],"ol",{},[36,63,64,67],{},[49,65,66],{},"Read","：读取业务数据，同时带走 version、Entity Tag（ETag，实体标签）等并发 token。",[36,69,70,73],{},[49,71,72],{},"Work","：在本地编辑或计算，不长期持有排他锁。",[36,75,76,79],{},[49,77,78],{},"Validate and write","：把验证与写入做成一次原子操作。token 仍一致就提交并递增；不一致就报告冲突。",[81,82,87],"pre",{"className":83,"code":84,"language":85,"meta":86,"style":86},"language-sql shiki shiki-themes dracula","UPDATE documents\nSET title = 'Q3 Growth Plan',\n    version = version + 1\nWHERE id = 42\n  AND version = 7;\n","sql","",[28,88,89,97,103,109,115],{"__ignoreMap":86},[90,91,94],"span",{"class":92,"line":93},"line",1,[90,95,96],{},"UPDATE documents\n",[90,98,100],{"class":92,"line":99},2,[90,101,102],{},"SET title = 'Q3 Growth Plan',\n",[90,104,106],{"class":92,"line":105},3,[90,107,108],{},"    version = version + 1\n",[90,110,112],{"class":92,"line":111},4,[90,113,114],{},"WHERE id = 42\n",[90,116,118],{"class":92,"line":117},5,[90,119,120],{},"  AND version = 7;\n",[13,122,123],{},"影响 1 行代表 version 7 仍然有效；影响 0 行代表这份记录在读取之后已经被别人修改。",[13,125,126,127,130,131,134],{},"比较与写入必须是原子的。如果先单独 ",[28,128,129],{},"SELECT","，过一会儿再无条件 ",[28,132,133],{},"UPDATE","，两步之间仍然存在 Time of Check to Time of Use（TOCTOU，检查时刻到使用时刻）race。",[20,136,138],{"id":137},"http-里的同一个思想","HTTP 里的同一个思想",[13,140,141,142,145],{},"Hypertext Transfer Protocol（HTTP，超文本传输协议）通过 Entity Tag（ETag，实体标签）和 ",[28,143,144],{},"If-Match"," 表达同样的协议：",[81,147,151],{"className":148,"code":149,"language":150,"meta":86,"style":86},"language-http shiki shiki-themes dracula","GET /documents/42\nETag: \"v7\"\n\nPUT /documents/42\nIf-Match: \"v7\"\n","http",[28,152,153,158,163,169,174],{"__ignoreMap":86},[90,154,155],{"class":92,"line":93},[90,156,157],{},"GET /documents/42\n",[90,159,160],{"class":92,"line":99},[90,161,162],{},"ETag: \"v7\"\n",[90,164,165],{"class":92,"line":105},[90,166,168],{"emptyLinePlaceholder":167},true,"\n",[90,170,171],{"class":92,"line":111},[90,172,173],{},"PUT /documents/42\n",[90,175,176],{"class":92,"line":117},[90,177,178],{},"If-Match: \"v7\"\n",[13,180,181,182,185],{},"如果资源已经不是 version 7，服务端可以返回 ",[28,183,184],{},"412 Precondition Failed","。最终条件由真正执行写入的服务端验证，而不是让客户端根据更早的一次读取自行猜测。",[20,187,188],{"id":188},"乐观与悲观",[190,191,192,208],"table",{},[193,194,195],"thead",{},[196,197,198,202,205],"tr",{},[199,200,201],"th",{},"维度",[199,203,204],{},"Optimistic Concurrency Control",[199,206,207],{},"Pessimistic Concurrency Control",[209,210,211,223,234,245,256],"tbody",{},[196,212,213,217,220],{},[214,215,216],"td",{},"默认判断",[214,218,219],{},"冲突不常发生",[214,221,222],{},"冲突很可能发生，或代价极高",[196,224,225,228,231],{},[214,226,227],{},"控制时机",[214,229,230],{},"提交时验证",[214,232,233],{},"开始工作前加锁或排队",[196,235,236,239,242],{},[214,237,238],{},"无冲突成本",[214,240,241],{},"等待很少",[214,243,244],{},"仍然承担锁与等待",[196,246,247,250,253],{},[214,248,249],{},"冲突成本",[214,251,252],{},"重试、合并或丢弃工作",[214,254,255],{},"通常等待，而不是返工",[196,257,258,261,264],{},[214,259,260],{},"常见风险",[214,262,263],{},"Retry storm、livelock、糟糕的冲突体验",[214,265,266],{},"Deadlock、timeout、吞吐下降",[13,268,269],{},"可以用一个粗略模型理解：",[81,271,276],{"className":272,"code":274,"language":275,"meta":86},[273],"language-text","乐观成本 ≈ 验证成本 + 冲突概率 × 重做成本\n悲观成本 ≈ 加锁成本 + 等待成本 + 死锁 / 超时处理成本\n","text",[28,277,274],{"__ignoreMap":86},[13,279,280],{},"真实系统经常混合使用：普通记录用乐观编辑；少数热点资源使用短锁或队列；外部副作用再配合 idempotency key（幂等键）。",[20,282,283],{"id":283},"冲突本身就是协议的一部分",[13,285,286],{},"发现冲突只完成了一半。产品还必须决定接下来怎样退出：",[33,288,289,292,295,298,301],{},[36,290,291],{},"拒绝并要求用户刷新；",[36,293,294],{},"重新读取后重试一个确定性操作；",[36,296,297],{},"合并互不冲突的字段；",[36,299,300],{},"展示三方合并；",[36,302,303],{},"通过队列或短事务把热点资源串行化。",[13,305,306],{},"自动重试还需要 Idempotency（幂等性）、有限重试次数，以及带 jitter（抖动）的 backoff（退避）。如果一次操作涉及转账、发邮件或第三方请求，盲目重试可能重复执行副作用。",[20,308,309],{"id":309},"它适合在哪里",[13,311,312],{},"OCC 最适合读多写少、冲突罕见、用户编辑时间长，而且重试或合并成本可控的场景。",[13,314,315],{},"当大量请求争抢同一个热点记录、冲突会让昂贵工作全部作废，或者业务 invariant（不变量）跨越了单个 version token 无法保护的多条记录时，它会明显退化。",[20,317,318],{"id":318},"它不是什么",[33,320,321,327,333,339],{},[36,322,323,326],{},[49,324,325],{},"不是 Last Write Wins。"," OCC 会让过期写入显性失败，而不是静默接受最后到达的人。",[36,328,329,332],{},[49,330,331],{},"不是 Multi-Version Concurrency Control（MVCC，多版本并发控制）。"," MVCC 主要回答读者应该看见哪个版本；OCC 回答写者的前提是否仍然有效。",[36,334,335,338],{},[49,336,337],{},"不是 Compare-and-Swap（CAS，比较并交换）。"," CAS 是可以实现 OCC 策略的一种原子原语。",[36,340,341,344],{},[49,342,343],{},"不是 Optimistic User Interface（乐观式界面）。"," Optimistic UI 优化感知速度；OCC 保护并发正确性。",[20,346,347],{"id":347},"最后记住五件事",[60,349,350,353,356,359,362],{},[36,351,352],{},"OCC 的核心是“不预先阻塞，在提交时验证前提”。",[36,354,355],{},"检查与写入必须原子化。",[36,357,358],{},"version、ETag、CAS 是载体或机制，不是完整策略。",[36,360,361],{},"冲突必须有被设计过的出口：拒绝、重试、合并或串行化。",[36,363,364],{},"应该根据争用程度与失败成本，在“等待”和“返工”之间做选择，而不是把某一种方案当成信仰。",[366,367,368],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":86,"searchDepth":99,"depth":99,"links":370},[371,372,373,374,375,376,377,378],{"id":22,"depth":99,"text":23},{"id":55,"depth":99,"text":55},{"id":137,"depth":99,"text":138},{"id":188,"depth":99,"text":188},{"id":283,"depth":99,"text":283},{"id":309,"depth":99,"text":309},{"id":318,"depth":99,"text":318},{"id":347,"depth":99,"text":347},"2026-07-16","允许并行工作，在提交时拒绝会覆盖新修改的旧版本。","软件系统","software-systems","md","Optimistic Concurrency Control · 乐观并发控制","optimistic-concurrency","持续生长","先让大家并行工作；有人提交时，再验证他开始工作时所依赖的前提是否仍然成立。",{},[390,395,400,405,410,415],{"name":391,"fullName":392,"category":393,"summary":394},"PCC","Pessimistic Concurrency Control · 悲观并发控制","策略","在关键工作开始前先取得排他访问权，用等待和锁管理换取更少的返工。",{"name":396,"fullName":397,"category":398,"summary":399},"MVCC","Multi-Version Concurrency Control · 多版本并发控制","存储模型","保留多个数据版本，让读者看到一致快照，并减少读取者与写入者互相阻塞。",{"name":401,"fullName":402,"category":403,"summary":404},"CAS","Compare-and-Swap / Compare-and-Set · 比较并交换","原子原语","只有当前值仍等于 expected 时才替换，是实现 OCC 的常见底层积木。",{"name":406,"fullName":407,"category":408,"summary":409},"Isolation","Transaction Isolation · 事务隔离","事务语义","决定并发事务能够观察什么，以及数据库会阻止哪些并发异常。",{"name":411,"fullName":412,"category":413,"summary":414},"Idempotency","Idempotent Operation · 幂等操作","重试安全","让重复请求仍只产生预期的一次效果，是自动重试之前必须解决的问题。",{"name":416,"fullName":417,"category":418,"summary":419},"OT / CRDT","Operational Transformation / Conflict-free Replicated Data Type · 操作转换 / 无冲突复制数据类型","合并模型","通过转换或收敛并发操作来保留多方意图，而不是简单拒绝其中一个写入者。",null,"/zh/learn/optimistic-concurrency",{"title":5,"description":380},"OCC",{"loc":421},[426,429,432,435],{"title":427,"url":428},"RFC 9110 · HTTP Semantics：If-Match","https://www.rfc-editor.org/rfc/rfc9110.html#name-if-match",{"title":430,"url":431},"PostgreSQL · Concurrency Control","https://www.postgresql.org/docs/current/mvcc.html",{"title":433,"url":434},"Microsoft · Handling Concurrency Conflicts in EF Core","https://learn.microsoft.com/en-us/ef/core/saving/concurrency",{"title":436,"url":437},"AWS · Optimistic locking with version number","https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/BestPractices_OptimisticLocking.html","zh/learn/optimistic-concurrency",[440,441,442],"并发控制","数据库","分布式系统","GS35W-UDG5_AnaLJKmdDFXak2tRE0-LyPXPmDJDUpU0",1784272893167]