1. 程式人生 > >也許 MySQL 適合 Uber,但它不一定適合你

也許 MySQL 適合 Uber,但它不一定適合你

【伯樂線上導讀】:Markus Winand 是資料庫專家,著有《SQL Performance Explained》一書。2013 年 Uber 從 MySQL 遷移到 PostgreSQL,而在 2016 年 8 月,Uber 工程部落格發文稱他們從 PostgreSQL 遷回 MySQL。當時在業內引發熱議,Markus 寫下了這篇文章來回應。

你可能聽說了 Uber 從 PostgreSQL 遷移到 MySQL 。這對於他們來說,或許是一個好的遷移,但是這未必適合你。且聽我說來。

2016 年 8 月,Uber 發表了一篇名為《為什麼 Uber 工程從 PostgreSQL 遷移到了 MySQL

》的文章。我並沒有立即閱讀原文,因為我的內心深處告訴我應該做一些本地的改進。在如此做的過程中,我的郵箱塞滿了問題,比如“難道 PostgreSQL 真的有這麼差勁嗎?”我知道  PostgreSQL 並沒有如此差勁,因此這些郵件使我想知道,原文到底寫了些什麼鬼玩意。這是一篇企圖理解 Uber 的文章。

在我看來,Uber 的文章基本上在說他們發現 MySQL 比 PostgreSQL 更適合他們的環境。然而,原文在傳遞這一訊息時卻表現的非常差勁。舉例來說,原文並沒有寫“  PostgreSQL 在 update-heavy 的使用場景下有一些侷限”,而是寫到“它對於寫入(write)來說是一個非常低效的架構”。當你沒有 updata-heavy 的使用場景時,不必擔心 Uber 的文章中描述的問題。

在本文中,我將會解釋:①為什麼我認為 Uber 的文章並不能作為我們在選擇資料庫的一個普適性建議,②為什麼 MySQL 仍舊適用於 Uber,③為什麼這樣的成功遷移案例可能會引起更多的問題,而不是擴充套件了資料庫。

在更新方面的問題

Uber 的文章所出現的第一個問題是, 在更新表中的行時,PostgreSQL 通常需要更新表上的所有索引,原文對這方面做了大篇幅的描述,但細節又不夠充分。另一方面,對於具有 InnoDB 的 MySQL 來說,只需要更新那些包含有更新列的索引。在更新改變了非索引列之時(原文中的「Write Amplification」部分),PostgreSQL 的方法需要佔用更多的磁碟 IO。如果這對 Uber 來說是一個大問題,那這些更新對於他們來說,或許是其整個工作量中的很大一部分。

然而,我有一些猜測,是 Uber 文章並沒有提到的。原文並沒有提及 PostgreSQL 的 Heap-Only-Tuples( HOT )。從 PostgreSQL 原始碼來看,HOT 對於一些特殊場景非常有用,即“當一個元組進行了重複更新時,不需要對它的索引列進行更新”。在這個場景中,如果新的行版本能夠和之前的版本儲存在同一個頁面中,PostgreSQL 能夠做出一些不需要觸及任何索引的更新。後一種情況可以使用  fillfactor 來進行調諧。假設 Uber 的工程認為 HOT 不能夠解決他們的問題,是因為這些頻繁的更新影響了不止一個索引列。

這個假設也在文章中的下列語句得到了支援:“如果我們有一張定義了十幾個索引的表,那麼只覆蓋了一個指標的領域的更新就必須涉及到整整12個指標來反映新的行中的 ctid ”。它明確地指出“只覆蓋了一個指標,”只有一個索引這是一種極端情況,否則 PostgreSQL 的 HOT 是可以解決這一問題的。

【旁註:我比較關心他們所擁有的索引的數量能否減少— 是我的一個挑戰。無論如何,對於那些使用的很少但在使用時又非常重要的索引來說,這是完全有可能的。】

看起來他們似乎執行著許多更新,這些更新改變了不止一個索引列,但這和表中所有索引相比來說,這算不上啥。如果這是一個主要的用例,那麼原文建議用 MySQL 來取代 PostgreSQL 就能夠說得通了。

關於 Select 的問題

還有一個關於他們用例的宣告引起了我的注意:原文解釋了 MySQL/InnoDB 使用了 clustered indexed ,並且承認了“這種設計意味著在做二次關鍵值查詢時和 Postgres 相比 InnoDB 的優勢並不明顯”。關於這個問題(the clustered index penalty),我之前寫過在 SQL Server 環境下的文章。

他們寫到 clustered index penalty 的優勢很小,這引發了我的興趣。在我看來,當你運行了很多使用二次索引的查詢時,這個優勢是很大的。如果對於他們來說這總種優勢很小,那這可能說明這些索引使用得很少。這將意味著他們在大多數情況下使用了 primary key 來進行查詢(那麼就不需要付出 clustered index penalty 了)。注意我寫的是“查詢”而不是“選擇”。這其中的原因在於 clustered index penalty 會影響具有 where 字句的宣告,而不僅僅是選擇。這也意味著高頻率的更新大多基於primary key。

最後,還有關於查詢的問題:他們沒有提到PostgreSQL在  index-only scans 方面的限制。尤其是在更新比較多的資料庫,安裝 PostgreSQ L的 index-only scans  沒有多大用處。我甚至說過這是唯一影響我大多數客戶端的問題。我在 2011 年已經在部落格上寫過這個問題。2012 年, PostgreSQL 9.2 在 index-only scans 獲得了一些有限的支援(這些工作大多服務於靜態資料)。2014 年我甚至在 PgCon 提出了我關注的一個方面。然而, Uber 並沒有抱怨這個問題。選擇速度對於他們而言並不是問題。我猜測查詢速度有執行在  replicas 上的選擇進行解決(如下所示),並且可能被大多數 primary key 所限制。

如今,他們的使用案例看起來似乎更加適合一個鍵/值儲存。並且 Innodb 是一個很堅實和流行的鍵/值儲存。這裡甚至有一些包將 InnoDB 和一些(非常有限的)的前端 SQL 繫結:我認為 MySQL 和 MariaDB 是非常流行的兩個。但是認真地說,如果你需要一個鍵/值儲存並且恰巧想執行一個簡單的 SQL 查詢, MySQL (或者 MariaDB )是合理的選擇。我猜測這至少比任何隨機的 NoSQL 鍵/值儲存要好,它們往往只提供一些更加有限的 SQL-ish 查詢語句作為開始。Uber ,從另一個方面來說僅僅在 InnoDB 和 MySQL 上層建立了他們自己的東西(“Schemaless”)。

關於索引再平衡(Rebalancing)

關於原文談索引,我最後再說一個方面:它在 B-tree 索引語句中使用了“再平衡”這個詞。它甚至連結到了維基百科上詞條 “Rebalancing after deletion”。不幸的是,維基百科詞條解釋並不普遍適用於資料庫索引,因為維基百科上所描述的演算法維持了每個節點至少是 half-full 的要求。為了提高併發性, PostgreSQL 使用了Lehman, Yao variation of B-trees,其升高了要求並且因此允許稀疏索引。作為一個旁節點,PostgreSQL 仍舊移除索引中的空白頁(參見“ Indexing Internals” 幻燈片第 15 頁)。然而,這僅僅只是一個次要問題。

真正使我擔心的是這句話:“B-tree 有核心方面,必須週期性再索引(rebalancing)…”,看到這裡,我想宣告這不是一個每天執行的週期性的過程。索引平衡是和每一次單獨的索引變化一同維護的(或許更嚴重,對嗎?)。但原文繼續寫到“…當子樹移動到新的 no-disk 位置時,這些 rabalancing 操作會徹底改變樹的結構”。如果你現在認為“rebalancing”包含了大量的資料移動,那你就誤解了它。

一個 B-tree 中的重要操作就是結點分裂。正如你所猜想的那樣,結點分裂發生在一個結點不能主持屬於這個結點的一個新條目。給你一個近似數,這可能在 100 次插入中發生一次。結點分裂分配新的結點,將一般的條目移動到新的結點並將新結點和它的前、後以及父結點連線起來。這就是 Lehman , Yao 節省了大量鎖定的地方。在某些情況下,新的結點不能直接加入到父結點中,因為父結點沒有足夠的空間來讓新結點加入。在這種情況下,父結點進行分裂並重復所有的過程。

在最糟糕的情況下,分裂泡沫會到達根結點,它也會進行分裂,並且會在其之上產生一個新的根結點。在這種情況下,一個 B-tree 將會增加深度。請注意,根結點的分裂有效轉變了整棵樹,並且因此保持了平衡。然而,這並不包括許大量資料的移動。在最差情況下,它可能會在每個曾經(level)上影響到三個節點和一個新的根結點。更確切地說:根結點分裂的最差情況,可能在一個十億的插入中發生大約 5 次。在另一種情況下,它不需要遍歷整棵樹。總的來說,索引的維護不是“週期性的”,甚至不會發生的很頻繁,並且永遠不會改變整棵樹的結構。至少在物理儲存單元不會這樣。

關於物理複製

我的下一個關注點是原文有關於 PostgreSQL 的物理複製(Physical Replication)。原文談到「索引再平衡」話題的原因是 Uber 曾經遇到了 PostgreSQL 的複製錯誤,這導致了下游伺服器的資料毀損。(這個錯誤值“影響了 Postgres  9.2 的一些特定版本並且已經修復了一段很長時間”)。

因為 PostgreSQL 9.2 只在 core 提供物理複製,複製錯誤“只造成了樹的很大一部分變得徹底無效。”具體解釋為:如果一個結點分裂進行錯誤的重複,那麼它就不能指向正確的子結點,這些子樹將會變得無效。這是絕對正確的,正如其他警言“如果存在一個 Bug,那麼不好的事情就會發生”。你不需要改變大量的資料,就可以打破樹的結構。一個單獨的壞指標就足夠了。

Uber 的文章提到了其他關於物理複製的情況:大量的複製流量—一部分由於更新帶來的編寫增幅—並且故障時間需要更新到新的 PostgreSQL 版本。如果第一個對於我而言是由意義的,那麼我是不能評論第二個的(但是這裡仍有 statements on the PostgreSQL-hackers mailing list )。

最後,原文也聲明瞭 Postgres 並沒有真正的 MVCC 支援副本。幸運的是,原文連結到了 PostgerSQL 的文件,裡面這個問題得到了解釋。這個問題基本上是主機並不需要知道副本做了什麼,並且因此會刪除資料,而這些資料仍然需要副本來完成查詢。

1.為可配置的超時延遲複製流的應用,以便讀取事務有機會完成。如果查詢沒有按時完成,殺死這個查詢,並且繼續應用這個複製流。

2.配置副本,把所有正在執行的查詢,反饋送回到主機(master),這樣一來主機並不需要清空行版本(row versions),恰好這些行版本在一些從屬(slave)中仍然需要。Uber 的文章對第一個版本制定了規則,但根本沒提第二個版本。相反,原文指責了 Uber 的開發人員。

關於開發者

以榮譽之名引用這段話:“舉例來說,開發者有一些資料需要將傳送給使用者。取決於程式碼是如何寫的,這些程式碼可能隱含有一個數據庫事務,同時執行不相關的 I/O 阻塞,事實上是許多工程師都不是資料庫方面的專家,因此不能總是理解這些問題,尤其是當使用 ORM 時,ORM 隱藏了許多底層細節,比如 open transactions 。”

不幸的是,我理解甚至同意這個評論。但是我不認同“許多工程師不是資料庫,是因為每一個開發人員接觸 SQL 是需要了解 transaction 的 ,不只資料庫專家。”

鑑於對開發人員進行 SQL 培訓是我的主要工作。我在各種規模的公司做過這件事。如果說我能確定一件事,那就是和資料庫相關的知識低的離譜。比如在剛剛提到的“open transaction”,我能確定的是,很少有開發人員知道只讀事務是真事。大多數的開發人員只知道 事務可以用於回寫。我遇到太多這種誤解了,所以我準備了幻燈片來解釋

關於成功

這是我想寫的最後一個問題。一個公司僱傭的員工越多,他們的資質就越趨於平均。誇張一點說,如果你僱傭了整個星球上的人員,你將會擁有絕對的平均水平。僱傭再多的人,只是增加了公司的規模。

這裡有兩種方法來擊敗這種比率:

1、只僱傭最好的員工。這個方法最難的部分就是,當沒有高於平均資質的候選者時,需要等待。

2、僱傭一般的員工,然後在工作上培訓。這對於新員工來說,需要一段很長的熱身時間,並且可以結合現有員工進行培訓。這兩個方法的共同問題就是需要時間。如果你沒有時間——因為你的公司在快速增長——你就不得不接受平庸,接受一些不是很瞭解資料庫的人( empirical data from 2014 )。換句話來說:對於一個快速增長的公司來說,技術比人更容易改變。

正如隨時間改變著的需求,成功的因素也會影響技術棧。在初期階段,創業公司需要創造性的技術,即可以立即獲得並且足夠靈活來用於業務中。 SQL 一個比較好的選擇,因為它確實靈活(你可以用任何方法來查詢你的資料)並且很容易找到理解 SQL 的人,哪怕只對 SQL 有一點點了解。好了,那麼就開始吧!然而對許多—或許大多數公司來說,故事就在這裡結束了。即使他們比較成功並且他們的公司成長了,他們也可能在 SQL  的侷限中永遠處於原地踏步。Uber 不這樣。

一些幸運的創業公司最終從 SQL 蛻變。隨著這種情況的發生,他們會有更多的機會訪問資源並且然後…一些奇妙的事情發生了:他們意識到,如果他們開發一個只為自己使用的專用系統來取代通用資料庫,他們就可以解決許多問題。這就是一個全新 NoSQL 資料庫誕生的時刻。在 Uber ,他們將之稱為 Schemaless 。

關於 Uber 的資料庫選擇

直到現在,我相信 Uber 沒有像他們的文章所建議的使用 MySQL 取代 PostgreSQL 。看來他們確實在一些特定的方法上替換了 PostgreSQL ,而這些方法恰好是 MySQL / InnoDB 所支援的(在當時)。看來原文只是解釋了為什麼 MySQL/InnoDB 比 PostgreSQL 更好地支援 Schemaless 。對於在用 Schemaless 的人來說,採納他們的建議!不幸的是,原文並沒有明確這一點,因為它並沒有提及,和 2013 年從 MySQL 遷移到 PostgreSQL 相比,他們的需求是如何隨著 Schemaless 的引進而改變的。

可悲的是,Uber 那文章留在讀者腦海中的唯一事情,就是 PostgreSQL 很差勁。