← 全部概念地圖
標準

概念地圖・版本向量

分類:系統

這一頁不只是把一個概念解釋一遍——一張自己畫的結構圖,加上拿台灣情境從頭走一次的實例演練、這個框架什麼時候會騙你,以及它接回 P.A.I.N. 之後長什麼樣。一頁必須交付的三層 →

版本向量:兩份資料誰是誰的祖先

同一位客人的足弓數據,台中門市改過一次,彰化門市也改過一次。晚上同步的時候,系統把兩份都留著——因為它分不出哪一份比較新。版本向量就是為了這一刻設計的:每一份副本各帶一串計數,一個計數位對應一個副本,記著「這一份看過那個副本的第幾次修改」。要比新舊,就把兩串數字逐位比一次。比得出高下,先後才成立;互有大小,這兩份就是並發,系統會誠實地告訴你它排不出來。

三個副本的計數矩陣,與一次分叉之後的合併 上層是一張計數表:三列是台中、彰化、南投三個門市手上那份處方,三欄是這三個門市各自的計數位,三列依序記著〔2 1 0〕、〔1 2 0〕、〔1 1 0〕,台中那列第一欄的 2 與彰化那列第二欄的 2 各被朱紅方框圈起來。下層是分叉圖:起點圓點標〔1 1 0〕,往右上以實線分出〔2 1 0〕、往右下以實線分出〔1 2 0〕,兩支之間有一個朱紅雙向箭頭標著無法比大小,右側再各拉一條虛線往右合併到〔2 2 0〕。 三個門市手上那份處方,各記著一串計數 欄=計數位(誰改的),列=這一份在誰手上 副本 台中改的 彰化改的 南投改的 台中門市 彰化門市 南投門市 2 1 0 1 2 0 1 1 0 〔2 1 0〕 台中手上那份 〔1 2 0〕 彰化手上那份 〔1 1 0〕 南投手上那份 朱紅圈起來的那兩個 2,就是分歧的來源 同一份處方分成兩支,再合起來 無法比大小 〔1 1 0〕 南投那份 台中那份 〔2 1 0〕 〔1 2 0〕 彰化那份 〔2 2 0〕 合併後 每一位都不小於對方, 而且至少一位嚴格大於——才排得出先後 虛線那一段由應用層決定,版本向量只管前面那兩支
表格說誰改過幾次,分叉圖說這兩份為什麼排不出先後。

三個副本的計數,記在哪一格

把三個門市手上那份處方攤開,每一份都帶著同樣長度的一串計數,差別只在數字。

這一份在誰手上版本向量這一份被誰改過跟台中那份比
台中門市〔2 1 0〕台中改過 2 次、彰化改過 1 次基準
彰化門市〔1 2 0〕台中改過 1 次、彰化改過 2 次第一位台中大、第二位彰化大,互有大小,排不出先後
南投門市〔1 1 0〕兩邊各改過 1 次,還停在分叉之前每一位都不大於,是台中那份的祖先
合併之後〔2 2 0〕兩邊的修改都留下來了每一位取兩份的較大值

整張表只有一個地方值得停下來看:第三位永遠是 0。南投門市一次都沒改過這份處方,所以誰的第三位都不會動。計數位的用途只有一個:替每一個可能動手的人留一個獨立的格子——沒動手的人,那一格就一直是零,也一直提醒你這串數字要按誰來讀。

兩個版本能不能排先後,逐位比過就知道

一、先確認每一位記的是誰。矯正鞋墊店那三個門市共用同一份客人的足弓處方,計數位就照門市排:第一位記台中改過幾次、第二位記彰化、第三位記南投。順序一旦在某一台機器上排錯,兩串數字看起來還是兩串數字,比出來的先後卻整個顛倒——而畫面上不會有任何紅字。自問:這串數字的每一位,分別記的是誰?

二、每一位都要比過。台中那份是〔2 1 0〕,彰化那份是〔1 2 0〕。只看第一位,2 大於 1,看起來台中比較新;比到第二位,1 小於 2,結論當場翻掉。門市系統最常見的偷懶做法是拿修改時間當唯一依據,於是彰化那台機器的時鐘慢了四十秒,客人的後跟調整就被台中的前掌調整蓋過去,隔天客人回來說「我明明講過」,沒有人查得到那句話去了哪裡。自問:這兩份向量,我是不是每一位都比過了?

三、比完之後只有兩種結果。每一位都不小於對方、而且至少一位嚴格大於,先後才排得出來——南投那份〔1 1 0〕對上台中那份〔2 1 0〕就是這樣,三位都不大於、第一位嚴格小於,所以南投手上那份是台中那份的祖先,直接覆蓋掉也不會少東西。台中對上彰化就走到另一種結果:第一位台中大、第二位彰化大,互有大小,這兩份並發。自問:比完的結果是每一位都不小於、而且至少一位嚴格大於,還是互有大小?

四、並發之後,誰來裁決。系統把兩份都攤在櫃檯上,接下來是人的事。矯正鞋墊店可以規定「以最後一次腳型掃描的那一份為準」,也可以規定「兩份的調整項目全部保留、疊起來重做一雙」。規則落得了地,衝突才有出口。沒有那條規則,那兩份就會一直躺在資料庫裡,直到有人臨櫃問起。自問:排不出先後的時候,誰來決定留哪一份?

一家矯正鞋墊訂製店的三個門市,把一次分叉走一遍

實例演練

底下這三家台灣中部的矯正鞋墊訂製店分店,是為了把這個結構講清楚而編出來的示意情境,沒有指涉任何真實公司。

  • 分叉之前:客人的足弓處方在三家分店都是〔1 1 0〕——台中調過一次楔形墊高,彰化調過一次後跟杯深度,三家都同步到了。起點一致,後面才有得比
  • 台中那一手:客人回台中門市反映前掌壓力,師傅把足弓支撐往前移了 3 公釐,台中那份變成〔2 1 0〕。自己那一位加一,是這份副本唯一能動的一位
  • 彰化同一天:客人的太太順路到彰化門市,說鞋子後跟磨腳,師傅把後跟杯放寬,彰化那份變成〔1 2 0〕。兩邊都以為自己手上是最新版
  • 晚上同步:兩份撞在一起。第一位台中大、第二位彰化大——系統掛出並發,兩份都留著。此刻誰都不是誰的祖先
  • 南投那一份:南投分店還停在〔1 1 0〕,三位都不大於台中那份、第一位嚴格小於,於是被台中那份安靜覆蓋。祖先版本可以放心蓋掉
  • 隔天裁決:店長翻出規則——同一雙鞋墊的不同部位互不干涉,兩邊的調整都保留,合併成〔2 2 0〕重做一雙。合併值在每一位取較大的那個

這個場景真正值得記住的接縫在晚上那一次同步。版本向量替店裡做完的事只有一件:把「這兩份撞在一起了」講出來,而且講得毫無疑義——它靠的是逐位比對,時間戳一秒都沒用上。至於後面那句「兩邊的調整都保留」,資料庫一個字都沒說,是店長根據鞋墊的物理結構補上去的。換一家店、換一個部位,那條規則可能就完全相反:同一個後跟杯被兩位師傅各改一次,最後只能留一份。所以真正要盤點的,是你這一行有哪幾種衝突可以自動合、哪幾種一定要人坐下來看。以上為說明結構用的示意情境,僅供理解框架之用。

兩個向量都留著之後,麻煩才開始

  1. 它只回答有沒有衝突,裁決在別處。版本向量偵測得到並發,接著就把球丟回來給你。留哪一份、怎麼合、要不要叫人,全是應用層自己寫的規則。更容易被誤讀的是「新」這個字:它比的是「有沒有看過對方的更新」,跟內容的好壞無關。收得晚的那一份,可能是師傅蹲下來重量了一次才改的;收得早的那一份,可能只是誰在鍵盤上點錯了一格。把先後直接當成品質高下,這個框架幫不上忙。
  2. 副本一多,向量就跟著長。三個門市是三位數字,三千個客戶端就是三千位。每一次同步都要把整串搬過去、逐位比一次,向量本身就變成流量與儲存的負擔——而且是每一筆資料都背一份。所以實務上很少讓每一支手機各佔一位,通常只給伺服器端那幾個節點各一位,客戶端的修改掛在它連上的那個節點底下。這個取捨要在開第一版之前想清楚,事後要改的是資料的形狀。
  3. 副本被移除或重建時,計數位最容易出錯。南投分店收掉了,那一位要不要拿掉?拿掉之後,舊資料裡的第三位跟新資料裡的第三位還對得起來嗎?重開一家分店沿用舊的位置,它會繼承前一家累積的計數,於是一份根本沒人看過的資料,看起來像是已經同步過了。這一類錯誤不會噴例外,只會安靜地讓某些更新被當成祖先蓋掉——上線半年後才有人發現某個門市的修改老是消失。

分不出先後的那兩份,才是真正的問題

痛點視角

它給的答案短得不像答案。

《痛點》關心的是誰在痛、痛在哪一刻。版本向量把這件事往前推了一格:它先確保「有人在痛」這個訊號不會被靜靜蓋掉。客人在彰化說後跟磨腳,那句話變成一次修改、一個計數位加一;只要沒有人拿祖先版本覆蓋它,這句話就一直在系統裡等人處理。這張圖畫得出兩份資料之間有沒有先後,畫不出哪一份該留下來——後面那個判斷要回到人身上:這位客人真正在意的是前掌壓力,是後跟磨腳,還是兩件事都要。P.A.I.N. 的四個字裡,版本向量守得住的只有「誰在痛」這個訊號的完整,剩下三個字得有人去問。今晚打開手上任何一份被改過兩次的檔案,問一句它到底知不知道自己被改過幾次——答案多半藏在版本欄位旁邊那一小格,花五分鐘把它翻出來,你就知道自己的系統有沒有在記帳。

常見問題

版本向量跟向量時鐘差在哪裡?

兩者的比法一模一樣——逐位比、互有大小就是並發。差別在「一位代表什麼」。向量時鐘的每一位是某個行程送出過幾個事件,量的是事件之間有沒有因果先後;版本向量的每一位是某個副本改過這份資料幾次,量的是同一份資料的幾個版本誰是誰的祖先。同一套算法掛在不同的東西上,於是能回答的問題也就不一樣。實務上做資料同步的系統多半用後者,因為它的向量跟著「一筆資料」走,不必替整個系統維護一份全域的事件帳。

副本上千個,向量會不會太長?

會,而且長得比想像中快。常見的收法有兩種:一是不讓每個客戶端各佔一位,只給伺服器端那幾個節點各一位,客戶端的寫入算在它連上的節點頭上;二是替向量加一個修剪機制,把長久沒動的位置整批壓掉。第二種要特別小心,被剪掉的那一位再度出現時,系統會把它當成從零開始的新副本,於是一份其實很舊的資料看起來像沒衝突。修剪的門檻要抓得比「最長離線時間」還寬,才不會在出差回來的那台筆電上出事。

偵測到並發之後,系統應該留哪一份?

版本向量不回答這個問題,這一步永遠是應用層的決定。常見的三種收法:把兩份都呈現出來讓使用者選,最誠實,代價是打擾人;寫死一條決勝規則(例如節點編號大的贏),最省事,代價是一定會丟掉某一邊的修改;或者把資料模型拆成互不干涉的欄位,兩邊各改各的欄位就能自動疊起來,最漂亮,代價是要在設計的時候就想清楚哪些部位真的互不影響。三種都合法,選錯的成本會在客訴裡出現。

兩份的向量完全一樣,代表內容一定一樣嗎?

在所有寫入都走同一條路的前提下是。版本向量只認「有沒有經過它記帳」這件事,繞過它的修改一律看不到——有人直接下 SQL 改資料庫、有人還原了一份備份、有人在時鐘重設之後重寫了一次,這些都不會讓任何一位加一。於是兩份內容不同、向量相同,系統會判它們是同一版,安靜地留下其中一份。所以真正該守的前提只有一句:所有修改都必須從同一個入口進來。

同一套逐位比對的算法掛在事件上量的是另一件事——想看它怎麼排出事件之間的因果先後,往向量時鐘那一張;至於「一次寫入要幾份才算數、讀回來要湊幾份才敢信」,那是法定人數交集在管的事。

發現引用或事實有誤?請寫信回報,我會依更正政策修正。