10 July 2010

前幾個月為了 scale 我們的資料庫,我們轉向 NoSQL 這個新領域求助,而最後,我選擇了 Apache Cassandra。現在它安穩地服務了兩個月有餘,暫時還沒遇到大問題。 資料庫 Scalability 這個題材我初次碰到,當初自然作了一些功課,尤其是對 Consistency 的意義重新了解。趁我還有一些記憶,用自己的話重新解釋一遍,強化記憶和概念。

Consistency (一致性) 這個字太泛稱了,可以用在不同的題材之下。這裡只討論資料庫,那就估且稱作 資料一致性 吧。資料一致性,是在一致什麼玩意呢?Relational Database Management System,RDBMS,通常滿足 ACID 的要求。ACID 的性質就包含了 Consistency 的定義:

  • Atomicity:一個 transaction 中,資料不是全部變更成功,就是全部失敗
  • Consistency:每一個 transaction 完成後,資料都必須永遠符合 schema 的規範,例如 schema 中所定義的資料格式 (type)、資料限制 (constraint)、資料間的 referential integrity... 等等
  • Isolation:同時進行的 transaction 間,互相不影響。
  • Durability:當 transaction 完成後,它所做的資料異動永遠存在,不論未來是否出現任何軟體或硬體方面的故障。

ACID 的各個性質句句不離 transaction 啊!不過,讓我們先拋開它,來看看 ACID 中的 C。老實說這裡 C 的定義挺陜隘的,它只要求資料要符合 schema, 然後就沒了。如果要選個更確切的字來描述這樣的性質,我寧可選 Valid,或是 Integrity 這樣的字眼,而不是 Consistency。當初為什麼選 Consistency ? 猜想是為了湊單字,湊個漂亮的 ACID (酸) 吧。有獨無偶,NoSQL 界也湊了個單字叫 BASE (鹼) - Basically Available Soft-state Eventually Consistency 這幾個字湊的太明顯了,逐字解釋它反而變得沒有意義,不過它還是帶出了 Eventually Consistency - 最終一致性。

Eventually Consistency,這字眼出現在各個討論 scalability 的部落格裡,這裡的 Consistency,又是指什麼? 這個問題的答案,要再往前追溯,追溯這些專家們,他們面臨了什麼問題。

他們面臨了 Scalability 的問題 (廢話...) ,再白話一點說,他們發現一台機器跑不動 (廢話的平方...)。 哈,很廢話沒錯,但它的背景和時代有關。RDBMS 擁有一段很長的歷史,它所著重的,都是讀取的效能和 transaction,因為過去只需要這兩種特性就可以滿足大部份的需求。 在這個前提下,要保有 ACID 性質還不算難。 但是,現在我們已經經歷過所謂的 web 2.0,進入了 Social Network (facebook) , 進入了 Cloud (google/amazon)。估且不論 web 2.0 和 cloud 的確切定義是什麼, 它們的共同特徵是用戶產生大量的資料,用戶不是單純的讀取資訊了,他們變成資訊貢獻者,他們產生了大量的 寫入。這時 RDBMS 垮了,一台撐不住這麼大量的寫入, 專家們只好死命的升級硬體, 但是量還是太大了,硬體趕不上了。這時馬上轉念:"那麼多裝個幾台試試吧"。然而,結果就像老闆一向以為加員工就可以加快專案進度一樣... 你知道的。

為了應付大量了寫入,工程師裝了兩台 database,這兩台的資料都一樣,所以可以分散資料的讀寫,降低系統負荷,也大大降低服務被中斷的機會。但事情從這時開始變複雜啦:

  • 用戶 A 寫入一筆,100 元到第一台,但用戶 A 後來卻向第二台讀同一筆,用戶 A 自然是氣的直跳腳,狂打客服!
  • 用戶 A 寫入一筆,100 元到第一台,用戶 B 同時也寫了入同一筆,200元到第二台。請問哪一台才是對的?
  • 用戶 A 寫入一筆,100 元到第一台,系統很聰明地在背後將這一筆同步到第二台,但不巧這時第二台剛好掛了。一小時後,第二台救回來了。但不見的變更怎麼補救呢?
  • ...etc

上述是一些可能遇到的問題,光是用想的就夠煩的了,資料要怎麼在多台間同步,並達到一致呢?ok, 我思考到這裡, 才發現原來專家們掛在嘴邊的 Consistency 是這個意思 -- 多台主機間,任何時間點,同一筆資料的一致性。跟 ACID 的 C 定義還差真遠, 先入為主的思考方式真是害死人啦。想通了這點,我才看懂 CAP 理論 是在講什麼。 CAP 理論中提到,下面的三點我們只能同時滿足兩點:

  • Consistency:多台主機間,任何時間點,同一筆資料的一致性
  • Availability:任何時間點資料都可以讀寫
  • Partition Tolerance:主機間出現斷層的容忍性,也就是主機間可不可以有一段時間不能相連。(例如網路斷線)

ps. 上面三點是我自己的見解,真正的解釋請見上面提供的連結。

為什麼只能同時滿足兩點?而實際上,我們只有 CP 和 AP 兩種可以選擇,沒有系統可以滿足 CA。 先來解釋 CP 好了,第一點 Consistency:我希望所有主機的資料一致,不會有人讀到舊的資料。 那麼,我必須每次寫入時,所有相關的主機都要同步更新,一台都不能漏。接下來是滿足第三點 Partition: 我希望系統可以容忍主機間發生斷層。網路斷線或某台主機掛點是很常見的,可以容忍這類錯誤的系統才是務實的作法。 好了,又要保持資料能同步更新,又要允許主機間中斷,那只好一旦發生中斷時,就停止資料的更新,直到主機回復後,才允許服務繼續。 換句話說,用戶就看到錯誤啦,而且還要等上一段時間。因此第二點 Availability 就不能滿足了。

好,再來看看 AP,我希望系統可以容忍主機間斷層 (Partition),又要發生斷層時,還讓用戶繼續讀寫資料 (Availability)。那還能怎麼辦? 只能讓用戶讀到不一致的資料囉:用戶寫資料到第一台,但系統無法同步到那個掛點的第二台,為了不讓服務中斷,我們只能回應用戶服務已成功。 之後第二台救回來了,用戶跑去讀第二台,他就會看到過期的資料。所以 Consistency 就無法滿足了。

那 CA 呢?滿足 CA 的條件是矛盾的,如果系統不能容忍斷層 (放棄 P),那麼等於是宣告了只能有一台主機,只有一台主機是可以滿足 Consistency 的。 但 Availability 就不可能了,只有一台主機是不可能達到不間斷的服務。

剛才的 AP 系統,我們說用戶可能會讀到過期的資料,那麼,我們何不盡可能的修復第二台的資料,讓資料的不一致性減到最低呢?這樣的概念就是 Eventually Consistency -- 資料最終會達到一致。用戶是可能讀到錯的資料,但下次再讀,就是對的了。 不是所有日常生活中的電腦系統,都一定要絕對的一致性才能使用。舉兩個例子,例如我們設計了一個類似的 email 系統,像是一般常見的站內信。 A 寄信給 B,資料寫到時第一台成功了,但同步到第二台卻失敗了,B 這時跑去看他的 in-box ,第一次看沒有,因為他連到的是第二台,後來再 refresh 一次, 他就看到了,因為系統已修復了第二台的錯誤。refresh 後才看到新信不是天天都在經歷嗎?沒什麼問題啊!另外一個例子就比較常見了,就是 DNS 系統,DNS 更新時是漸漸擴散到所有的主機,最後才達到一致,二十年來都是這樣運作的,有時的確有些不便,但是整體來說非常的成功,是 internet 的基石之一。

CP 與 AP 雖然只能選擇一種,但不代表一套軟體只能支援一種,例如 Cassandra 可以支援 CP,也可以支援 AP,端看軟體設定而定 (另外一個 NoSQL: Riak 聽說也是如此), 另外,對於 Eventually Consistency 常見的誤解是:"感覺上隨時都有可能出現不一致的資料"。其實不然,當主機都是好好時,放在 Cassandra 這類資料庫的資料是保持一致的, 只有當異常發生時,這段時間受影響的資料才會退回 Eventually Consistency。主機間發生斷層是不可預期的,但不代表這會是常態,系統大部份的資料,在大部份的時間裡都是保持一致的。


回響

可以用 Tag <I>、<B>,程式碼請用 <PRE>