[{"data":1,"prerenderedAt":818},["ShallowReactive",2],{"all-learn-concepts":3},[4,450],[5],{"id":6,"title":7,"body":8,"date":388,"description":389,"domain":390,"domainKey":391,"extension":392,"featured":171,"fullName":209,"interaction":14,"maturity":393,"mentalModel":394,"meta":395,"navigation":171,"neighbors":396,"ogImage":426,"path":427,"published":171,"robots":426,"seo":428,"shortName":429,"sitemap":430,"sources":431,"stem":444,"tags":445,"translationKey":14,"updated":388,"__hash__":449},"learnEn/learn/optimistic-concurrency.md","Optimistic Concurrency",{"type":9,"value":10,"toc":378},"minimal",[11,15,19,22,27,35,48,56,60,63,84,125,128,139,143,150,183,190,194,272,275,283,286,290,293,310,313,317,320,323,327,353,357,374],[12,13,7],"h1",{"id":14},"optimistic-concurrency",[16,17,18],"p",{},"Optimistic Concurrency Control (OCC) solves a deceptively simple problem: two people can read the same record, make different changes, and accidentally erase each other's work.",[16,20,21],{},"Its defining move is not to lock the record while everyone thinks. It lets work proceed, then validates the writer's original assumption at the moment of commitment.",[23,24,26],"h2",{"id":25},"the-failure-it-prevents-lost-update","The failure it prevents: lost update",[16,28,29,30,34],{},"Suppose Alice and Bob both read document ",[31,32,33],"code",{},"#42"," at version 7.",[36,37,38,42,45],"ul",{},[39,40,41],"li",{},"Alice changes the title and saves first. The database advances to version 8.",[39,43,44],{},"Bob is still editing the old version 7. He changes the owner field and submits the whole object.",[39,46,47],{},"Without concurrency control, Bob's stale object can silently restore the old title.",[16,49,50,51,55],{},"That is a ",[52,53,54],"strong",{},"lost update",". The dangerous part is not that two people read together. It is that Bob's write was accepted even though the premise behind it was stale.",[23,57,59],{"id":58},"the-mechanism","The mechanism",[16,61,62],{},"OCC normally has three stages:",[64,65,66,72,78],"ol",{},[39,67,68,71],{},[52,69,70],{},"Read"," the business data and a concurrency token such as a version number or Entity Tag (ETag).",[39,73,74,77],{},[52,75,76],{},"Work"," locally without holding a long-lived exclusive lock.",[39,79,80,83],{},[52,81,82],{},"Validate and write"," as one atomic operation. If the token still matches, commit and advance it. If not, report a conflict.",[85,86,91],"pre",{"className":87,"code":88,"language":89,"meta":90,"style":90},"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","",[31,92,93,101,107,113,119],{"__ignoreMap":90},[94,95,98],"span",{"class":96,"line":97},"line",1,[94,99,100],{},"UPDATE documents\n",[94,102,104],{"class":96,"line":103},2,[94,105,106],{},"SET title = 'Q3 Growth Plan',\n",[94,108,110],{"class":96,"line":109},3,[94,111,112],{},"    version = version + 1\n",[94,114,116],{"class":96,"line":115},4,[94,117,118],{},"WHERE id = 42\n",[94,120,122],{"class":96,"line":121},5,[94,123,124],{},"  AND version = 7;\n",[16,126,127],{},"One affected row means version 7 was still current. Zero affected rows means the record changed after it was read.",[16,129,130,131,134,135,138],{},"The comparison and write must be atomic. A separate ",[31,132,133],{},"SELECT"," followed later by an unconditional ",[31,136,137],{},"UPDATE"," leaves a Time of Check to Time of Use (TOCTOU) race between the two statements.",[23,140,142],{"id":141},"the-same-idea-in-http","The same idea in HTTP",[16,144,145,146,149],{},"Hypertext Transfer Protocol (HTTP) exposes this pattern with Entity Tag (ETag) and ",[31,147,148],{},"If-Match",":",[85,151,155],{"className":152,"code":153,"language":154,"meta":90,"style":90},"language-http shiki shiki-themes dracula","GET /documents/42\nETag: \"v7\"\n\nPUT /documents/42\nIf-Match: \"v7\"\n","http",[31,156,157,162,167,173,178],{"__ignoreMap":90},[94,158,159],{"class":96,"line":97},[94,160,161],{},"GET /documents/42\n",[94,163,164],{"class":96,"line":103},[94,165,166],{},"ETag: \"v7\"\n",[94,168,169],{"class":96,"line":109},[94,170,172],{"emptyLinePlaceholder":171},true,"\n",[94,174,175],{"class":96,"line":115},[94,176,177],{},"PUT /documents/42\n",[94,179,180],{"class":96,"line":121},[94,181,182],{},"If-Match: \"v7\"\n",[16,184,185,186,189],{},"If the resource is no longer version 7, the server can reject the write with ",[31,187,188],{},"412 Precondition Failed",". The condition is evaluated where the write happens, not guessed by the client from an earlier read.",[23,191,193],{"id":192},"optimistic-versus-pessimistic","Optimistic versus pessimistic",[195,196,197,213],"table",{},[198,199,200],"thead",{},[201,202,203,207,210],"tr",{},[204,205,206],"th",{},"Dimension",[204,208,209],{},"Optimistic Concurrency Control",[204,211,212],{},"Pessimistic Concurrency Control",[214,215,216,228,239,250,261],"tbody",{},[201,217,218,222,225],{},[219,220,221],"td",{},"Default assumption",[219,223,224],{},"Conflicts are uncommon",[219,226,227],{},"Conflicts are likely or very costly",[201,229,230,233,236],{},[219,231,232],{},"Control point",[219,234,235],{},"Validate at commit",[219,237,238],{},"Lock or queue before work",[201,240,241,244,247],{},[219,242,243],{},"No-conflict cost",[219,245,246],{},"Little waiting",[219,248,249],{},"Lock and waiting overhead remain",[201,251,252,255,258],{},[219,253,254],{},"Conflict cost",[219,256,257],{},"Retry, merge, or discard work",[219,259,260],{},"Usually wait rather than redo",[201,262,263,266,269],{},[219,264,265],{},"Common risks",[219,267,268],{},"Retry storms, livelock, poor conflict UX",[219,270,271],{},"Deadlocks, timeouts, lower throughput",[16,273,274],{},"A useful approximation is:",[85,276,281],{"className":277,"code":279,"language":280,"meta":90},[278],"language-text","optimistic cost ≈ validation + conflict probability × redo cost\npessimistic cost ≈ locking + waiting + deadlock/timeout handling\n","text",[31,282,279],{"__ignoreMap":90},[16,284,285],{},"Real systems often mix them: optimistic editing for ordinary records, short locks or queues for a few hot resources, and idempotency keys around external side effects.",[23,287,289],{"id":288},"conflict-is-part-of-the-protocol","Conflict is part of the protocol",[16,291,292],{},"Detecting a conflict is only half the design. The product must decide what happens next:",[36,294,295,298,301,304,307],{},[39,296,297],{},"reject and ask the user to refresh;",[39,299,300],{},"re-read and retry a deterministic operation;",[39,302,303],{},"merge independent fields;",[39,305,306],{},"show a three-way merge;",[39,308,309],{},"serialize a hot resource through a queue or short transaction.",[16,311,312],{},"Automatic retry also needs idempotency, bounded retries, and backoff with jitter. Retrying an operation that sends money, email, or third-party requests can otherwise duplicate the side effect.",[23,314,316],{"id":315},"where-it-fits","Where it fits",[16,318,319],{},"OCC is strongest when reads dominate writes, conflicts are rare, users may edit for a long time, and retry or merge is affordable.",[16,321,322],{},"It degrades when many requests fight over one hot record, conflicts invalidate expensive work, or a business invariant spans records that a single version token cannot protect.",[23,324,326],{"id":325},"what-it-is-not","What it is not",[36,328,329,335,341,347],{},[39,330,331,334],{},[52,332,333],{},"Not Last Write Wins."," OCC makes stale writes visible instead of silently accepting the last arrival.",[39,336,337,340],{},[52,338,339],{},"Not Multi-Version Concurrency Control (MVCC)."," MVCC mainly answers which version a reader sees; OCC answers whether a writer's premise is still current.",[39,342,343,346],{},[52,344,345],{},"Not Compare-and-Swap (CAS)."," CAS is an atomic primitive that can implement the broader OCC strategy.",[39,348,349,352],{},[52,350,351],{},"Not Optimistic User Interface."," Optimistic UI changes perceived latency; OCC protects concurrent correctness.",[23,354,356],{"id":355},"five-things-to-keep","Five things to keep",[64,358,359,362,365,368,371],{},[39,360,361],{},"OCC means “do not block first; validate the premise at commit.”",[39,363,364],{},"The check and write must be atomic.",[39,366,367],{},"Version, ETag, and CAS are carriers or mechanisms, not the complete policy.",[39,369,370],{},"A conflict needs a designed exit: reject, retry, merge, or serialize.",[39,372,373],{},"Choose between waiting and rework based on contention and failure cost, not ideology.",[375,376,377],"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":90,"searchDepth":103,"depth":103,"links":379},[380,381,382,383,384,385,386,387],{"id":25,"depth":103,"text":26},{"id":58,"depth":103,"text":59},{"id":141,"depth":103,"text":142},{"id":192,"depth":103,"text":193},{"id":288,"depth":103,"text":289},{"id":315,"depth":103,"text":316},{"id":325,"depth":103,"text":326},{"id":355,"depth":103,"text":356},"2026-07-16","Work in parallel; reject stale writes before they overwrite newer changes.","Software systems","software-systems","md","growing","Let people work in parallel; when someone commits, verify that the assumptions they started from are still true.",{},[397,401,406,411,416,421],{"name":398,"fullName":212,"category":399,"summary":400},"PCC","strategy","Acquire exclusive access before doing the critical work, trading retries for waiting and lock management.",{"name":402,"fullName":403,"category":404,"summary":405},"MVCC","Multi-Version Concurrency Control","storage model","Keep multiple versions so readers can see a consistent snapshot without blocking writers.",{"name":407,"fullName":408,"category":409,"summary":410},"CAS","Compare-and-Swap / Compare-and-Set","atomic primitive","Replace a value only when it still equals the expected value; a common building block for OCC.",{"name":412,"fullName":413,"category":414,"summary":415},"Isolation","Transaction Isolation","semantics","Defines what concurrent transactions may observe and which anomalies the database prevents.",{"name":417,"fullName":418,"category":419,"summary":420},"Idempotency","Idempotent Operation","retry safety","Makes a repeated request produce the intended effect once, which is essential before automatic retries.",{"name":422,"fullName":423,"category":424,"summary":425},"OT / CRDT","Operational Transformation / Conflict-free Replicated Data Type","merge model","Preserves concurrent intent by transforming or converging operations instead of simply rejecting one writer.",null,"/learn/optimistic-concurrency",{"title":7,"description":389},"OCC",{"loc":427},[432,435,438,441],{"title":433,"url":434},"RFC 9110 · HTTP Semantics: If-Match","https://www.rfc-editor.org/rfc/rfc9110.html#name-if-match",{"title":436,"url":437},"PostgreSQL · Concurrency Control","https://www.postgresql.org/docs/current/mvcc.html",{"title":439,"url":440},"Microsoft · Handling Concurrency Conflicts in EF Core","https://learn.microsoft.com/en-us/ef/core/saving/concurrency",{"title":442,"url":443},"AWS · Optimistic locking with version number","https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/BestPractices_OptimisticLocking.html","learn/optimistic-concurrency",[446,447,448],"concurrency","databases","distributed-systems","JJ1NPrTjvToRfI9O9OMD4s_CRQGYU-aJ2Um7nvpJgz8",[451],{"id":452,"title":453,"body":454,"date":388,"description":772,"domain":773,"domainKey":391,"extension":392,"featured":171,"fullName":774,"interaction":14,"maturity":775,"mentalModel":776,"meta":777,"navigation":171,"neighbors":778,"ogImage":426,"path":803,"published":171,"robots":426,"seo":804,"shortName":429,"sitemap":805,"sources":806,"stem":812,"tags":813,"translationKey":14,"updated":388,"__hash__":817},"learnZh/zh/learn/optimistic-concurrency.md","乐观并发控制",{"type":9,"value":455,"toc":762},[456,458,461,464,468,474,485,492,495,498,515,539,542,551,555,561,585,591,594,664,667,673,676,679,682,699,702,705,708,711,714,740,743,760],[12,457,453],{"id":453},[16,459,460],{},"Optimistic Concurrency Control（OCC，乐观并发控制）解决的是一个很容易被低估的问题：两个人读到了同一份记录、分别做了修改，然后在不知情的情况下抹掉了对方的工作。",[16,462,463],{},"它最关键的动作不是在大家思考和编辑时锁住记录，而是在真正提交的那一刻验证：写入者开始工作时所依赖的前提，现在是否仍然成立？",[23,465,467],{"id":466},"它要阻止的失败lost-update","它要阻止的失败：Lost Update",[16,469,470,471,473],{},"假设 Alice 和 Bob 都读取了文档 ",[31,472,33],{}," 的 version 7：",[36,475,476,479,482],{},[39,477,478],{},"Alice 修改标题并率先保存，数据库前进到 version 8。",[39,480,481],{},"Bob 仍在编辑旧的 version 7。他修改 owner，然后把整个旧对象提交回来。",[39,483,484],{},"如果系统直接接受 Bob 的写入，Alice 的新标题就可能悄悄消失。",[16,486,487,488,491],{},"这叫 ",[52,489,490],{},"Lost Update（丢失更新）","。危险的不是两个人同时读取，而是 Bob 的写入所依赖的前提已经过期，系统却仍然允许它成功。",[23,493,494],{"id":494},"它怎样工作",[16,496,497],{},"OCC 通常分成三个阶段：",[64,499,500,505,510],{},[39,501,502,504],{},[52,503,70],{},"：读取业务数据，同时带走 version、Entity Tag（ETag，实体标签）等并发 token。",[39,506,507,509],{},[52,508,76],{},"：在本地编辑或计算，不长期持有排他锁。",[39,511,512,514],{},[52,513,82],{},"：把验证与写入做成一次原子操作。token 仍一致就提交并递增；不一致就报告冲突。",[85,516,517],{"className":87,"code":88,"language":89,"meta":90,"style":90},[31,518,519,523,527,531,535],{"__ignoreMap":90},[94,520,521],{"class":96,"line":97},[94,522,100],{},[94,524,525],{"class":96,"line":103},[94,526,106],{},[94,528,529],{"class":96,"line":109},[94,530,112],{},[94,532,533],{"class":96,"line":115},[94,534,118],{},[94,536,537],{"class":96,"line":121},[94,538,124],{},[16,540,541],{},"影响 1 行代表 version 7 仍然有效；影响 0 行代表这份记录在读取之后已经被别人修改。",[16,543,544,545,547,548,550],{},"比较与写入必须是原子的。如果先单独 ",[31,546,133],{},"，过一会儿再无条件 ",[31,549,137],{},"，两步之间仍然存在 Time of Check to Time of Use（TOCTOU，检查时刻到使用时刻）race。",[23,552,554],{"id":553},"http-里的同一个思想","HTTP 里的同一个思想",[16,556,557,558,560],{},"Hypertext Transfer Protocol（HTTP，超文本传输协议）通过 Entity Tag（ETag，实体标签）和 ",[31,559,148],{}," 表达同样的协议：",[85,562,563],{"className":152,"code":153,"language":154,"meta":90,"style":90},[31,564,565,569,573,577,581],{"__ignoreMap":90},[94,566,567],{"class":96,"line":97},[94,568,161],{},[94,570,571],{"class":96,"line":103},[94,572,166],{},[94,574,575],{"class":96,"line":109},[94,576,172],{"emptyLinePlaceholder":171},[94,578,579],{"class":96,"line":115},[94,580,177],{},[94,582,583],{"class":96,"line":121},[94,584,182],{},[16,586,587,588,590],{},"如果资源已经不是 version 7，服务端可以返回 ",[31,589,188],{},"。最终条件由真正执行写入的服务端验证，而不是让客户端根据更早的一次读取自行猜测。",[23,592,593],{"id":593},"乐观与悲观",[195,595,596,607],{},[198,597,598],{},[201,599,600,603,605],{},[204,601,602],{},"维度",[204,604,209],{},[204,606,212],{},[214,608,609,620,631,642,653],{},[201,610,611,614,617],{},[219,612,613],{},"默认判断",[219,615,616],{},"冲突不常发生",[219,618,619],{},"冲突很可能发生，或代价极高",[201,621,622,625,628],{},[219,623,624],{},"控制时机",[219,626,627],{},"提交时验证",[219,629,630],{},"开始工作前加锁或排队",[201,632,633,636,639],{},[219,634,635],{},"无冲突成本",[219,637,638],{},"等待很少",[219,640,641],{},"仍然承担锁与等待",[201,643,644,647,650],{},[219,645,646],{},"冲突成本",[219,648,649],{},"重试、合并或丢弃工作",[219,651,652],{},"通常等待，而不是返工",[201,654,655,658,661],{},[219,656,657],{},"常见风险",[219,659,660],{},"Retry storm、livelock、糟糕的冲突体验",[219,662,663],{},"Deadlock、timeout、吞吐下降",[16,665,666],{},"可以用一个粗略模型理解：",[85,668,671],{"className":669,"code":670,"language":280,"meta":90},[278],"乐观成本 ≈ 验证成本 + 冲突概率 × 重做成本\n悲观成本 ≈ 加锁成本 + 等待成本 + 死锁 / 超时处理成本\n",[31,672,670],{"__ignoreMap":90},[16,674,675],{},"真实系统经常混合使用：普通记录用乐观编辑；少数热点资源使用短锁或队列；外部副作用再配合 idempotency key（幂等键）。",[23,677,678],{"id":678},"冲突本身就是协议的一部分",[16,680,681],{},"发现冲突只完成了一半。产品还必须决定接下来怎样退出：",[36,683,684,687,690,693,696],{},[39,685,686],{},"拒绝并要求用户刷新；",[39,688,689],{},"重新读取后重试一个确定性操作；",[39,691,692],{},"合并互不冲突的字段；",[39,694,695],{},"展示三方合并；",[39,697,698],{},"通过队列或短事务把热点资源串行化。",[16,700,701],{},"自动重试还需要 Idempotency（幂等性）、有限重试次数，以及带 jitter（抖动）的 backoff（退避）。如果一次操作涉及转账、发邮件或第三方请求，盲目重试可能重复执行副作用。",[23,703,704],{"id":704},"它适合在哪里",[16,706,707],{},"OCC 最适合读多写少、冲突罕见、用户编辑时间长，而且重试或合并成本可控的场景。",[16,709,710],{},"当大量请求争抢同一个热点记录、冲突会让昂贵工作全部作废，或者业务 invariant（不变量）跨越了单个 version token 无法保护的多条记录时，它会明显退化。",[23,712,713],{"id":713},"它不是什么",[36,715,716,722,728,734],{},[39,717,718,721],{},[52,719,720],{},"不是 Last Write Wins。"," OCC 会让过期写入显性失败，而不是静默接受最后到达的人。",[39,723,724,727],{},[52,725,726],{},"不是 Multi-Version Concurrency Control（MVCC，多版本并发控制）。"," MVCC 主要回答读者应该看见哪个版本；OCC 回答写者的前提是否仍然有效。",[39,729,730,733],{},[52,731,732],{},"不是 Compare-and-Swap（CAS，比较并交换）。"," CAS 是可以实现 OCC 策略的一种原子原语。",[39,735,736,739],{},[52,737,738],{},"不是 Optimistic User Interface（乐观式界面）。"," Optimistic UI 优化感知速度；OCC 保护并发正确性。",[23,741,742],{"id":742},"最后记住五件事",[64,744,745,748,751,754,757],{},[39,746,747],{},"OCC 的核心是“不预先阻塞，在提交时验证前提”。",[39,749,750],{},"检查与写入必须原子化。",[39,752,753],{},"version、ETag、CAS 是载体或机制，不是完整策略。",[39,755,756],{},"冲突必须有被设计过的出口：拒绝、重试、合并或串行化。",[39,758,759],{},"应该根据争用程度与失败成本，在“等待”和“返工”之间做选择，而不是把某一种方案当成信仰。",[375,761,377],{},{"title":90,"searchDepth":103,"depth":103,"links":763},[764,765,766,767,768,769,770,771],{"id":466,"depth":103,"text":467},{"id":494,"depth":103,"text":494},{"id":553,"depth":103,"text":554},{"id":593,"depth":103,"text":593},{"id":678,"depth":103,"text":678},{"id":704,"depth":103,"text":704},{"id":713,"depth":103,"text":713},{"id":742,"depth":103,"text":742},"允许并行工作，在提交时拒绝会覆盖新修改的旧版本。","软件系统","Optimistic Concurrency Control · 乐观并发控制","持续生长","先让大家并行工作；有人提交时，再验证他开始工作时所依赖的前提是否仍然成立。",{},[779,783,787,791,795,799],{"name":398,"fullName":780,"category":781,"summary":782},"Pessimistic Concurrency Control · 悲观并发控制","策略","在关键工作开始前先取得排他访问权，用等待和锁管理换取更少的返工。",{"name":402,"fullName":784,"category":785,"summary":786},"Multi-Version Concurrency Control · 多版本并发控制","存储模型","保留多个数据版本，让读者看到一致快照，并减少读取者与写入者互相阻塞。",{"name":407,"fullName":788,"category":789,"summary":790},"Compare-and-Swap / Compare-and-Set · 比较并交换","原子原语","只有当前值仍等于 expected 时才替换，是实现 OCC 的常见底层积木。",{"name":412,"fullName":792,"category":793,"summary":794},"Transaction Isolation · 事务隔离","事务语义","决定并发事务能够观察什么，以及数据库会阻止哪些并发异常。",{"name":417,"fullName":796,"category":797,"summary":798},"Idempotent Operation · 幂等操作","重试安全","让重复请求仍只产生预期的一次效果，是自动重试之前必须解决的问题。",{"name":422,"fullName":800,"category":801,"summary":802},"Operational Transformation / Conflict-free Replicated Data Type · 操作转换 / 无冲突复制数据类型","合并模型","通过转换或收敛并发操作来保留多方意图，而不是简单拒绝其中一个写入者。","/zh/learn/optimistic-concurrency",{"title":453,"description":772},{"loc":803},[807,809,810,811],{"title":808,"url":434},"RFC 9110 · HTTP Semantics：If-Match",{"title":436,"url":437},{"title":439,"url":440},{"title":442,"url":443},"zh/learn/optimistic-concurrency",[814,815,816],"并发控制","数据库","分布式系统","GS35W-UDG5_AnaLJKmdDFXak2tRE0-LyPXPmDJDUpU0",1784230124769]