分布式數據庫模式與反模式

MingTian 8年前發布 | 7K 次閱讀 數據庫

日 前,PingCAP 聯合創始人兼 CTO 黃東旭在「2016中國數據分析師行業峰會(CDAS)」 “數據庫與技術實戰”分論壇上,分享了《分布式數據庫模式與反模式》的主題演講。老規矩,給你們想要的演講實錄。

以下為現場演講實錄:

我們現在正在做一個 OLTP 的數據庫,主要 focus 在大數據的關系型數據庫的存儲和可擴展性,還有關系的模型,以及在線交易型數據庫上的應用。

所以,今天整個數據庫的模式和反模式,我都會圍繞著如何在一個海量的并發,海量的數據存儲的容量上,去做在線實時的數據庫業務的一些模式來講。并從數據庫的開發者角度,來為大家分享怎樣寫出更加適合數據庫的一些程序。

基礎軟件的發展趨勢

一開始我先簡單介紹一下,現在我認為的一些基礎軟件上的發展趨勢。

  • 開源

第一點,開源是一個非常大的趨勢。大家可以看到一些比較著名的基礎軟件,基本都是開源的,比如 Docker,比如 k8s。甚至在互聯網公司里面用的非常多的軟件,像 MySQL、Hadoop 等這種新一代的大數據處理的數據庫等基礎軟件,也大多是開源的。其實這背后的邏輯非常簡單:在未來其實你很難去將你所有的技術軟件都用閉源, 因為開源會慢慢組成一個生態,而并不是被某一個公司綁定住。比如國家經常說去 IOE,為什么?很大的原因就是基本上你的業務是被基礎軟件綁死的,這個其實是不太好的一個事情。而且現在跟過去二十年前不一樣,無論是開源軟件的質量,還是社區的迭代速度,都已經是今非昔比,所以基本上開源再也不是低質低量的代名詞,在互聯網公司已經被驗證很多次了。

  • 分布式

第二,分布式會漸漸成為主流的趨勢。這是為什么?這個其實也很好理解,因為隨著數據量越來越大,大家可以看到,隨著現在的硬件發展,我感覺摩爾定律有漸漸失效的趨勢。所以單個節點的計算資源或者計算能力,它的增長速度是遠比數據的增長速度要慢的。在這種情況下,你要完成業務,存儲數據,要應對這么大的并發,只有一種辦法就是橫向的擴展。橫向的擴展,分布式基本是唯一的出路。scale-up  和 scale-out 這兩個選擇其實我是堅定的站在 scale-out 這邊。當然傳統的關系數據庫都會說我現在用的 Oracle,IBM DB2,他們現在還是在走 scale-up  的路線,但是未來我覺得 scale-out 的方向會漸漸成為主流。

  • 碎片化

第三,就是整個基礎軟件碎片化。現在看上去會越來越嚴重。但是回想在十年前、二十年前,大家在寫程序的時候,我上面一層業務,下面一層數據庫。但是現在你會發現,隨著可以給你選擇的東西越來越多,可以給你在開源社區里面能用到的組件越來越多,業務越來越復雜,你會發現,像緩存有一個單獨的軟件,比如 redis,隊列又有很多可以選擇的,比如說  zeromq, rabbitmq, celery 各種各樣的隊列;數據庫有 NoSQL、HBase,關系型數據庫有 MySQL 、PG 等各種各樣的基礎軟件都可以選。但是就沒有一個非常好東西能夠完全解決自己的問題。所以這是一個碎片化的現狀。

  • 微服務

第四,是微服務的模式興起。其實這個也是最近兩年在軟件架構領域非常火的一個概念。這個概念的背后思想,其實也是跟當年的 SOA 是一脈相承的。就是說一個大的軟件項目,其實是非常難去 handle 復雜度的,當你業務變得越來越大以后,維護成本和開發成本會隨著項目的代碼量呈指數級別上升的。所以現在比較流行的就是,把各個業務之間拆的非常細,然后互相之間盡量做到無狀態,整個系統的復雜度可以控制,是由很多比較簡單的小的組件組合在一起,來對外提供服務的。

這個服務看上去非常美妙,一會兒會說有什么問題。最典型的問題就是,當你的上層業務都拆成無狀態的小服務以后,你會發現原有的邏輯需要有狀態的存儲服務的時候你是沒法拆的。我所有的業務都分成一小塊,每一小塊都是自己的數據庫或者數據存儲。比如說一個簡單的 case,我每一個小部分都需要依賴同一個用戶信息服務,這個信息服務會變成整個系統的一個狀態集中的點,如果這個點沒有辦法做彈性擴展或者容量擴展的話,就會變成整個系統很致命的單點。

所以現在整個基礎軟件的現狀,特別在互聯網行業是非常典型的幾個大的趨勢。我覺得大概傳統行業跟互聯網行業整合,應該在三到五年,這么一個時間。所以互聯網行業遇到的今天,可能就是傳統行業,或者其他的行業會遇到的明天。所以,通過現在整個互聯網里面,在數據存儲、數據架構方面的一些比較新的思想,我們就能知道如何去做這個程序的設計,應對明天數據的量級。

現有存儲系統的痛點

其實今天主要的內容是講存儲系統,存儲系統現在有哪些痛點?其實我覺得在座的各位應該也都能切身的體會到。

  • 彈性擴展

首先,大數據量級下你如何實現彈性擴展?因為我們今天主要討論的是 OLTP ,是在線的存儲服務,并不是離線分析的服務。所以在線的存儲服務,它其實要做到的可用性、一致性,是要比離線的分析業務強得多的。但是在這種情況下,你們怎樣做到業務無感知的彈性擴展,你的數據怎么很好的滿足現有的高并發、大吞吐,還有數據容量的方案。

  • 可用性

第二,在分布式的存儲系統下,你的應用的可用性到底是如何去定義,如何去保證?其實這個也很好理解,因為在大規模的分布式系統里面,任何一個節點,任何一個數據中心或者支架都有可能出現硬件的故障,軟件的故障,各種各樣的故障,但這個時候你很多業務是并沒有辦法停止,或者并沒有辦法去容忍 Down time 的。所以在一個新的環境之下,你如何對你系統的可用性做定義和保證,這是一個新的課題。一會兒我會講到最新的研究方向和成果。

  • 可維護性

第三,對于大規模的分布式數據庫來說它的可維護性,這個怎么辦?可維護性跟單機的系統是明顯不同的,因為單機的數據庫,或者傳統的單點的數據庫,它其實做到主從,甚至做到一主多從,我去維護 master ,別讓它掛掉,這個維護性主要就是維護單點。在一個大規模的分布式系統上,你去做這個事情是非常麻煩的。可以簡單說一個案例,就是 Google 的 Spanner。Spanner 是 Google 內部的一個大規模分布式系統,整個谷歌內部只部署了一套,在生產環節中只部署了一套。這一套系統上有上萬甚至上數十萬的物理節點。但是整個數據庫的維護團隊,其實只有很小的一組人。想像一下,上十萬臺的物理節點,如果你要真正換一塊盤、做一次數據恢復或者人工運維的話,這是根本不可能做到的事情。但是對于一個分布式系統來說,它的可維護性或者說它的維護應該是轉嫁給數據庫自己。

  • 開發復雜度

還有,就是對于一個分布式數據庫來說,它在開發業務的時候復雜度是怎么樣的。大家其實可能接觸的比較多的,像 Hbase、 Cassandra、Bigtable 等這種開源的實現,像 NoSQL 數據庫它其實并沒有一個很好的 cross-row transaction 的 support。另外,對于很多的 NoSQL 數據庫并沒有一個很好的 SQL 的 interface,這會讓你寫程序變得非常麻煩。比如說對于一些很普通的業務,一個表,我需要去 select from table,然后有一個fliter 比如一個條件大于 10,小于 100,這么簡單的邏輯,如果在 HBase 上去做的話,你要寫十行、二十行、三十行;如果你在一個關系的數據庫,或者支持 SQL 的數據庫,其實一行就搞定了。其實這個對于很多互聯網公司來說,在過去的幾年之內基本上已經完成了這種從 RDBMS 到 NoSQL 的改造,但是這個改造的成本和代價是非常非常高的。比如我原來的業務可能在很早以前是用 MySQL 已經寫的穩定運行好久了,但是隨著并發、容量、可擴展性的要求,我需要遷移 Bigtable、Hbase、Cassandra、MongoDB 這種 NoSQL 數據庫上,這時基本上就要面臨代碼的完整重寫。這個要放在互聯網公司還可以,因為它們有這樣的技術能力去保證遷移的過程。反正我花這么多錢,招這么牛的工程師,你要幫我搞定這個事情。但是對于傳統的行業,或者傳統的機構來說,這個基本上是不可能的事情。你不可能讓他把原來用 Oracle 用SQL 的代碼改成 NoSQL 的 code。

因為 NoSQL 很少有跨行事務,首先你要做一個轉賬,你如果不是一個很強的工程師,你這個程序基本寫不對,這是一個很大的問題。這也是為什么一直以來像這種 NoSQL 的東西并沒有很好的在傳統行業中去使用的一個最核心的原因,就是代價實在太大。

存儲系統的擴展模型

所以其實在去講這些具體到底該怎么解決,或者未來數據庫會是什么樣的之前,我想簡單講一下擴展的模型。對于一個關系型數據庫也好,對于存儲的系統本身也好,它的擴展模型有哪些。

  • Sharding 模式

第一種模式是 Sharding 模式。如果在座的各位有運維過線上的 MySQL 的話,對這個模型會非常熟悉。最簡單的就是分庫、分表加中間件,就是說我不同的業務可能用不同的庫,不同的表。當一個單表太大的時候,我通過一些 Cobar、Mycat 等這樣的數據庫中間件來去把它分發到具體的數據庫的實例上。這種模型是目前用的最普遍的模型,它其實也解決了很大部分的問題。為什么這十年在關系型數據庫上并沒有很好的擴展方案,但是大家看上去這種業務還沒有出現死掉的情況,就是因為后面有各種各樣 Sharding 的中間件或者分庫分表這種策略在硬扛著。像這種中間件 Sharding 第一個優勢就是實現非常簡單。你并不需要對數據庫內做任何的改造,你也并不需要去比如說從你原來的 SQL 代碼轉到 NoSQL 的代碼。

但是它也有自己的缺點。首先,對你的業務層有很強的侵入性。 這是沒有辦法的,比如你想用一個中間件,你就需要給它指定一個 Sharding key。另外,原來比如你的業務有一些 join ,有一些跨表跨行的事務,像這種事務你必須得改掉,因為很多中間件并沒有辦法支持這個跨 shard 的分布式 join。

第二個比較大的缺陷是它的分片基本是固定的,自動化程度、擴展性都非常差, 你必須得有一個專職的 DBA 團隊給你的 MySQL 或者 PG 的  Sharding 的集群去做運維。我之前在豌豆莢做過一段時間 MySQL cluster  的分片的維護工作。當時我記得是一個 16 個節點的 MySQL 的集群,我們需要擴展到 32 個節點的規模,整整提前演練了一個月,最后上線了一個禮拜。上線那個禮拜,晚上基本上沒有辦法睡覺,所以非常痛苦。

再說一個 Google 的事情,Google 在剛才我說的 Spanner 和 F1 這兩個數據庫沒有上線之前,Google 的廣告系統的業務是由 100 多個節點的 MySQL 的集群對外提供服務的。如 Google 這么牛的公司,在維護一百多個節點的 MySQL Sharding 的數據庫的時候,都已經非常痛苦,寧可重新去寫一個數據庫,也不想去維護這個 datebase cluster。其實大家可以看到,像這種 Sharding 的方案,它比較大的問題就是它的維護代價或者維護集群的復雜度,并不是隨著節點數呈線性增長,而是隨著節點的增加非線性的增長上去。比如你維護 2 個節點的還好,維護 4 個節點的也還可以,但是你維護 16 個、64 個、128 個基本就是不可能的事情。

第三就是 一些復雜的查詢優化,并沒有辦法在中間件這一層,去幫你產生一個足夠優化的執行計劃,因此, 對于一些復雜查詢來說,Sharding 的方案是沒法做的。 所以對你的業務層有很高的要求。

這是一種思路,是目前來說互聯網公司里邊用的最多的一種 MySQL 或者 PG 這種關系型數據庫的擴展方案。

  • Region Base 模型

第二種擴展模型是 Region Base。這張圖是我項目里面扒出來的圖。

它整個思路有點像 Bigtable,它相當于把底下的存儲層分開,數據在最底層存儲上已經沒有表、行這樣結構的劃分,每一塊數據都由一個固定的 size 比如 64 M、128 M 連續的 Key-value pairs 組成。其實這個模型背后最早的系統應該是谷歌在 06 年發表的 Bigtable 這篇論文里面去描述的。這個模型有什么好處呢?一是它能真正實現這種彈性的擴展。第二個,它是一個真正高度去中心化。去中心化這個事情,對于一個大的 Cluster 來說是一個非常重要的特性。

還有一個優勢,在 KV 層實現真正具有一定的自動 Failover 的能力。 Failover指的是什么呢?比如說在一個集群比較大的情況下,或者你是一個 cluster ,你任何一個節點,任何一個數據損壞,如果能做到業務端的透明,你就真正實現了 Auto-Failover 的能力。其實在一些對一致性要求不那么高的業務里面,Auto-Failover 就是指, 比如在最簡單的一個 MySQL 組從的模型里,當你的組掛掉了以后,我監控的程序自動把 slave  提上來,這也是一種 Failover 的方式。但是這個一致性或者說數據的正確性并不能做到很好的保證。你怎么做到一致性的 Auto-Failover,其實背后需要做非常非常多的工作。

這是 Region 模型的一些優勢。 但是它的劣勢也同樣明顯,這種模型的實現非常復雜。 我一會兒會說到背后的關鍵技術和理論,但是它比起寫中間件真的復雜太多了。你要寫一個能用的 MySQL 或者 PG 的中間件,可能只需要一兩個工程師,花一兩周的時間就能寫出一個能用的數據庫中間件;但是你如果按照這個模型做一個彈性擴展的數據庫的話,你的工作量就會是數量級的增加。

第二個劣勢就是它業務層的兼容性。 像 Region Base 的模型,最典型的分布式存儲系統就是 HBase。HBase 它對外的編程接口和 SQL 是千差萬別,因為它是一個 Key Value 的數據庫。你的業務層的代碼兼容性都得改,這個對于一些沒有這么強開發能力的用戶來說,是很難去使用的,或者它說沒有 SQL 對于用戶端這么友好。

可用性級別  

我一會兒會講一下,剛才我們由 Region Base 這個模型往上去思考的一些東西,在此之前先說一些可用性。高可用。其實說到高可用這個詞,大多數的架構師都對它非常熟悉。我的系統是高可用的,任何一個節點故障業務層都不受影響,但是真的不受影響嗎?我經過很多的思考得到的一個經驗就是主從的模型是不可能保證同時滿足強一致性和高可用性的。可能這一點很多人覺得,我主從,我主掛了,從再提起來就好,為什么不能保護這個一致性呢?就是因為在一個集群的環境下,有一種故障叫腦裂。腦裂是什么情況?整個集群是全網絡聯通的,但是出現一種情況,就是我只是在集群內部分成了兩個互不聯通的一個子集。這兩個子集又可以對外提供服務,其實這個并不是非常少見的狀況,經常會發生。像這種情況,你貿然把 slave 提起來,相當于原來的 master 并沒有完全的被 shutdown,這個時候兩邊可能都會有讀寫的情況,造成數據非常嚴重的不一致,當然這個比較極端了。所以你會發現阿里或者說淘寶,年年都在說我們有異地多活。但是去年甚至前幾個月,杭州阿里的數據中心光纖被挖斷,支付寶并沒有直接切到重復層,而是寧可停止服務,完全不動,也不敢把 slave 數據中心提起來。所以其實任何基于主從模型的異地多活方案都是不行的。這個問題有沒有辦法解決呢?其實也是有的。

還是說到 Google,我認為它才是全世界最大的數據庫公司,因為它有全世界最大的數據量。你從來沒有聽說過 Google 哪一個業務因為哪一個數據中心光纖挖斷,哪一個磁盤壞了而對外終止服務的,幾乎完全沒有。因為 Google 的存儲系統大多完全拋棄了基于主從的一致性模型。它的所有數據都不是通過主從做復制的,而是通過類似 Raft 或者 Paxos 這種分布式選舉的算法做數據的同步。這個算法的細節不展開了,總體來說是一個解決在數據的一致性跟自動的數據恢復方面的一個算法。同時,它的 latency 會比多節點強同步的主從平均表現要好的一個分布式選舉的算法。

在 Google 內部其實一直用的 Paxos,它最新的 Spanner 數據庫是用 Paxos 做的 replication 。在社區里面,跟 Paxos 等價的一個算法就是 Raft。Raft 這個算法的性能以及可靠性都是跟 Paxos 等價的實現。這個算法就不展開了。我認為這才是新一代的強一致的數據庫應該使用的數據庫復制模型。

分布式事務  

說到事務。對于一個數據庫來說,我要做傳統的關系型數據庫業務,事務在一個分布式環境下,并不像單機的數據庫有這么多的方法,這么多的優化。其實在分布式事務這個領域只有一種方法,并且這么多年了從分布式事務開始到現在,在這個方法上并沒有什么突破,基本只有一條出路就是兩階段提交。其實可以看一下 Google 的系統。對于我們做分布式系統的公司來說,Google 就是給大家帶路的角色。Google 最新的數據庫系統上它使用的分布式事務的方法仍然是兩階段提交。其實還有沒有什么優化的路呢?其實也是有的。兩階段提交最大的問題是什么呢?一個是延遲。因為第一階段先要把數據發過去,第二階段要收到所有參與的節點的 response 之后你才能去 commit 。這個過程,相當于你走了很多次網絡的 roundtrip,latency  也會變得非常高。所以其實優化的方向也是有的,但是你的 latency 沒法優化,只能通過吞吐做優化,就是 throughput 。比如說我在一萬個并發的情況下,每個用戶的  latency 是 100  毫秒,但是一百萬并發,一千萬并發的時候,我每個用戶的 latency 還可以是 100 毫秒,這在傳統的單點關系型數據庫上,是沒有辦法實現的。第二就是去中心化的事務管理器。另外沒有什么東西是銀彈,是包治百病的,你要根據你的業務的特性去選擇合適的一致性算法。

NewSQL  

其實剛剛這些 pattern 會發展出一個新的類別,我們能不能把關系數據庫上的一些 SQL、Transaction 跟 NoSQL 跟剛才我說到的 Region Base 的可擴展的模型融合起來。這個思想應該是在 2013 年左右的時候,學術界提出來比較多的東西,NewSQL。NewSQL 首先要解決 Scalability 的問題, 剛給我們說過 scalability 是一個未來的數據庫必須要有的功能,第二個就是 SQL,SQL 對于業務開發者來說是很好的編程的接口。第三,ACID Transaction,我希望我的數據庫實現轉帳和存錢這種強一致性級別的業務。第四,就是整個 cluster 可以支持無窮大的數據規模,同時任何數據節點的宕機、損壞都需要集群自己去做監控,不需要 DBA 的介入。

案例:Google Spanner / F1  

有沒有這樣的系統?其實有的。剛才一直提到 Google 的 Spanner 系統。Spanner 系統是在 2012 年底于 OSDI  的會議上發布了論文; F1 這篇論文在 2013 年的 VLDB 發布的,去描述了整個 Google 內部的分布式關系型數據庫的實現。首先,根據 Spanner 的論文 Spanner 和  F1 在生產環境只有一個部署,上萬物理節點遍布在全球各種數據中心內,通過 Paxos 進行日志復制。第二,整個架構是無狀態的 SQL 層架在一個 NoSQL 的基礎之上。第三,它對外提供的是一個跨行事務的語義,這個跨行事務是透明的跨行事務,我不需要對我的業務層做修改,它是通過 一個硬件支持的 Truetime API,GPS 時鐘和原子鐘去實現事務的隔離級別。這個系統是最早用來支撐 Google 的在線廣告業務,在線廣告業務大家知道,其實對于扣費、廣告計費、點擊記錄,廣告活動等一致性的級別要求非常高。首先這個錢不能多扣,也不能少扣,實時性要求非常高。而且業務邏輯相當復雜,這個時候需要一個 SQL 數據庫,需要一個支持事務的數據庫,需要一個支持 Auto-Failover 的數據庫,所以就有了 Google Spanner/F1 這個系統。

典型業務場景  

最典型的業務場景是什么呢?對于這種高吞吐、大容量的數據量級對于 NoSQL 系統來說,典型的應用的 Pattern 就是高吞吐大容量,還有就是 Workload 相對比較分散的情況。比較典型的反例是秒殺,秒殺這個場景其實非常不適合這種 NewSQL。另外就是一致性、可用性和 latency 之間怎么做取舍。在這種分布式數據庫上面,第一個要丟棄的就是延遲,在這么大規模的量級上做強一致性,延遲是首先是不能保證的。你去看谷歌的 F1 和 Spanner 的論文里面,它提到它們的 latency 基本都是 10 毫秒、20 毫秒、甚至50 毫秒、100 毫秒的量級,但是它并不會太關心 latency 。因為它必須保證通過 Paxos 去做跨機房、多機房的復制,光速你肯定沒法超越,所以這個時間是省不了的,它整個系統是面向吞吐設計的,而不是面向低延遲設計的,但是它要求非常強的一致性。

  • MySQL Sharding

一個典型的場景就是替換 MySQL Sharding 的業務。它的業務典型的特點,就是高吞吐,海量并發的小事務,并不是特別大的 transection,模型也相對簡單,沒有復雜的 join 的程序。但是痛點剛才提到了非常明顯,首先就是對于 MySQL Sharding 方案 scale 能力很差,對于表結構變更的方案并沒有太多;再者,cross shard transaction,目前 Sharding 的方案并沒有辦法很好支持,但 NewSQL 面向的場景和 MySQL Sharding 面向的場景是非常的像的。

  • Cross datacenter HA

第二種場景是 Cross datacenter HA(跨數據中心多活),這種場景簡直是數據開發者追求的圣杯。因為像 Oracle、DB2文,它在最關鍵,最核心的業務上,比如說像銀行這種業務,它必須要求實現這種跨數據中心的高可用。但是在一個分布式的數據庫上,或者說是 Open-source solution 里,有沒有人有辦法實現這個 Cross datacenter HA 呢?完全沒有。目前來說并沒有任何一個數據庫能去解決這個問題。因為剛才提到了主從的模型根本是不可靠的。像這種業務的數據極端的重要,任何一點數據的丟失都不能容忍。另外即使整個數據中心宕機也不能影響線上的業務,很典型的像支付寶,這種涉及錢相關的,甚至有些比如你的銀行卡,你肯定不能容忍說你吃完飯,告訴你不好意思數據中心掛了,你的錢我刷不了。但是并沒有開源的數據庫能解決這個問題,如果你真的自己做同步復制的方案的話,特別兩地三中心的情況,你請求的延遲是取決于離你最遠的數據中心,所以大部分業務來說延遲過大。 另外就是這些系統重度依賴人工運維。我認為一旦任何系統有人工的介入一定是會出錯的。因為人是最難自動化的一個因素。

反模式  

  • 濫用傳統關系模型

對于這種大規模的分布式 NewSQL 來說,比較大的反模式就是,大量的使用存儲過程 foreign  key,視圖等操作。因為首先存儲過程是沒有辦法 scale 的。另外,大表與大表的 JOIN ,在線上的 OLTP 數據庫上最大的開銷并不是優化器的開銷,而是網絡通信的開銷。比如兩個表去做一個笛卡兒積,算起來可能并不是這么慢,但是要把數據一條一條在網絡中傳輸代價是非常大的,這種情況下用 OLAP 的數據庫是比較適合。

  • 沒有利用好并發

剛才我一直強調對于新型的 OLTP 或者分布式關系型數據庫來說,并發或者說吞吐才是應該追求的優化點。在架構設計時,不應該把對延遲非常敏感的業務去使用分布式數據庫來實現。所以其實在延遲跟吞吐之間,數據庫永遠去選擇吞吐。所以寫程序會遇到的一個很典型的問題,就是我的查詢是一行一行,上下之間有這種強依賴關系的模式。其實在這種模式下,對于分布式關系型數據庫來說是非常不合適的。所以你應該用并發的思想去做,比如說我的請求,我可以把吞吐打到整個集群上,我的 Database,我的 cluster 會自動 balance 一些 workload,如果一行行查詢之間是有依賴的話,那么每一條查詢之間的 latency 是會疊加起來。所以這個其實并不是一個太好的 pattern 。

  • 不均勻的設計

數據庫其實經常會被大家濫用。比如說我看到很多傳統行業數據庫使用的方法,其實真的是非常痛苦。無論什么業務我全都直接上 Oracle,不管這個業務是否真的需要 Database 來去支持;比如有人用 MySQL 做計數器,有些有 MySQL 做隊列或者做秒殺,這個并不是太 scale 的東西。特別像秒殺的業務,特別高的并發打到同一個 key 上,workload 沒辦法去分拆到好多個節點來幫你去處理,所以整個分布式的集群就會退化成一個單點,這是非常不合理的。第二,當你的索引設計的不太好的時候,涉及到的過多全表掃描是非常不劃算的。剛剛也說過,在一個分布式集群里面,最大的開銷是網絡的開銷,你做全表掃描的時候并不是在一臺機器上,而是在好多機器上做全表掃描,同時經過很大的網絡傳輸,當你的索引設計不合理的時候會出現這樣的問題。還有是過多的無效索引,會導致整個系統在寫入的時候有延遲也好或者吞吐也好,會變得更慢。主要注意的就是不要存在任何可以把你的系統退化成單點的機會。

  • 錯誤的一致性模型

還有一種最容易犯的問題,就是在沒有好好思考你的業務適合的場景情況下,就去使用很強的一致性模型。因為當你要求一個非常高的強一致性的時候,你的分布式事務的 latency 一定比不這么強的一致性的業務要高得多的。其實,很多業務并不需要那么強的一致性,我的數據庫雖然給你了這個能力,但如果你去濫用的話,你會說這個數據庫好慢,或者為什么我這個請求100 毫秒才給我返回。但其實很多場景下,你并不是這么強的依賴強一致性 。這個跟數據庫本身提不提供這個機制是沒有關系的。作為一個 Database 開發者來說,我還是需要給你能實現這個功能的機制,但是不要濫用。

另外根據你業務的沖突的頻繁程度選擇不同的鎖策略。你知道這個業務就是一個很高的沖突的場景,比如說可能像類似發工資,一個集團的賬號發到成千上萬的小賬號。如果你是一個樂觀事務的話,可能涉及到的沖突就會很大。因為所有的事務的發起者都要去修改這個公共賬號,這種情況下一般可以使用悲觀事務,因為你已經預知到你業務的 conflict 級別會很高。但當我知道我的業務的沖突很小,我要追求整個系統的吞吐,我可能會選用樂觀的事務的模型。

Q&A

提問:您好,我簡單問一下對于秒殺這個場景,什么樣的數據庫設計是合適的?

黃東旭:其實秒殺這個業務是一個比較系統的工程,并不是一個簡單的我用什么數據庫就可以做的東西。我之前給一些朋友做秒殺的架構設計的時候,從最上層,甚至你可能從 JS 這邊就要去做排隊,一層一層的排隊。比如在緩存這邊我可以用 redis,我可以用一些緩存的數據庫再去進行二級的排隊,因為每一次排隊你都會丟掉很多,最后到底下的隊列,在數據庫這一層,比如庫存就十個,你只要在前面放十個進來就行了。就是說,你在上層把流量用排隊的方式做更好。最典型的例子就是大家看 12306 賣火車票的時候經常會有排隊。基本沒有什么數據庫能去應對,像淘寶,像京東這種秒殺的業務,它從上到下是整個系統的過程。謝謝。

提問:您好,我問一下,你說的是分布式的 OLTP 數據庫,歷史數據怎么清理,如果后續還需要分析數據的話。

黃東旭:其實在谷歌 Spanner 包括像 TiDB 、TiKV 這個系統之上,是通過一種 MVCC 的技術,每一條數據都會有一個版本號。比如說你不停的 update,它可能會產生好多版本。我背后會有一個 GC 的邏輯,去選定一個 safe point,在這個 safe point 之前的所有數據全都刪掉也好,還是挪到冷的存儲上也好,我們是通過這種方式實現的。另外隨著數據量的膨脹,整個系統設計的時候,我們是通過 Raft 算法,比如說這個數據慢慢長大,我會把它切成兩塊,再慢慢 Auto-balance 到其它的節點上,然后新的那一塊會再長大......這相當于一個細胞一樣,一個細胞分裂成兩個細胞,成千上萬的細胞會均勻的分布在整個集群里面。如果你要刪數據可以直接刪,如果你不想刪的話,你的數據可以全都留下,你自己隨便往集群里增加新的節點。這個集群很大,它會自動幫你把數據均勻的分到這兒。所有的歷史數據是通過 GC 的模塊把歷史的版本拿出來,丟到冷的存儲上。

在 Google 幾乎是完全不刪數據的,因為存儲的成本是很低的,但是數據未來說不定什么時候就用了。大概是這樣,我需要有一個辦法能把它存下來。

提問:當前做的交易要使用歷史的參考數據,可能量不是很大,有這樣的例子嗎?

黃東旭:當然有。像 MVCC 在訪問歷史,它其實提供了一個 Lock-free 的 Snapshot Read, 它在讀歷史版本的時候是不會讀線上的正在進行的讀寫事務,所以它是有一個無所讀的機制,這也是為什么它要去采用每一個數據都要有版本號的機制去實現。它是有這個需求,而且在實現的時候也非常漂亮,不會阻塞其他的請求。

提問:你們的 TiDB 是怎么考慮用 MySQL 或者 PG 的?當時是怎么考慮的?

黃東旭:首先我們沒有用 MySQL 或者 PG,剛才說的 TiDB 這個模型是谷歌的 Spanner 跟 F1 的模型。用傳統的單機數據庫做改造的話,有一個比較大的誤區,整個 SQL 的查詢優化器跟存儲的數據結構,并不知道你底層是分布式的存儲,它還是假設你生成 SQL 的方案都是單機的。比如最簡單的例子,我需要去 count *,如果是一個單機的 MySQL 優化器,沒有建索引的話會一行一行把數據拉過來計一個數,這樣算下去;但是如果對一個分布式系統來說,我只要把 count * 這個邏輯推到所有存儲表的數據節點上,算法再 reduce 回來就可以,更像一個 Mapreduce 這種分布式框架的 SQL 優化器。如果你沒有從底到上完整的去實現 Database 的話,你很難對分布式的場景或者分布式數據存儲的這些性質來去對數據庫做優化。沒有辦法,這是一條很難的道路,但是我們也得去下走。

 

 

 

 

來自:http://mp.weixin.qq.com/s?__biz=MzI3NDIxNTQyOQ==&mid=2247484004&idx=1&sn=36d30d0446f7ca16eb9ca7dced100ffc&scene=19#wechat_redirect

 

 本文由用戶 MingTian 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!