來源:www.infoq.cn/article/underlying -storage-of-uber-change-from-mysql -to-postgres
背景
早期的 Uber 后臺軟件由 Python 寫成,數(shù)據(jù)存儲使用 Postgres。后期隨著業(yè)務(wù)的飛速發(fā)展后臺架構(gòu)也變化巨大,演進(jìn)成了微服務(wù)加數(shù)據(jù)平臺。數(shù)據(jù)存儲也由 Postgres 變成了 Schemalesshttps://eng.uber.com/schemaless-part-one/——Uber 自主研發(fā)的以 MySQL 做為底層的高可用數(shù)據(jù)庫。Uber 的數(shù)據(jù)庫主要存儲的是 Trip 數(shù)據(jù),就是一個叫車訂單從下單起,到上車、下車、付費等的全過程跟蹤及處理。從 2014 年初起,由于業(yè)務(wù)增長迅猛,Uber 的原有基礎(chǔ)架構(gòu)已經(jīng)無法繼續(xù)支撐業(yè)務(wù)。改進(jìn)的項目花了將近一年時間。
對于新的數(shù)據(jù)庫存儲系統(tǒng),Uber 的主要關(guān)鍵需求是:
要有能力通過增加服務(wù)器而線性地增加容量。增加服務(wù)器不但要增加可用的硬盤容量,還要減少系統(tǒng)的響應(yīng)時間。
需要有寫緩沖能力,萬一持久化到數(shù)據(jù)庫失敗時,仍可以稍后重試。
需要通知下游依賴關(guān)系的方式,數(shù)據(jù)變更要能無損的通知出去。
需要二級索引。
系統(tǒng)要足夠健壯,可以支持 7*24 服務(wù)。
在調(diào)查對比了 Cassandra、Riak 和 MongoDB 等等之后,Uber 技術(shù)團(tuán)隊沒有發(fā)現(xiàn)能完全滿足需求的現(xiàn)成解決方案。而再考慮到數(shù)據(jù)可靠性、對技術(shù)的把握能力等因素,他們決定自己開發(fā)一套數(shù)據(jù)庫管理系統(tǒng)——Schemaless,一個鍵值型存儲庫,可以存放 JSON 數(shù)據(jù)而無需嚴(yán)格的模式驗證,是完全的無模式風(fēng)格。用 MySQL 作底層存儲,其中只有順序?qū)懭?,?MySQL 主庫故障時支持寫入緩沖。并有一個數(shù)據(jù)變更通知的發(fā)布 - 訂閱功能(命名為 trigger),支持?jǐn)?shù)據(jù)的全局索引。
Schemaless 的強大與簡單更多是因為我們在存儲節(jié)點中使用了 MySQL。Schemaless 本身是在 MySQL 之上相對較薄的一層,負(fù)責(zé)將路由請求發(fā)送給正確的數(shù)據(jù)庫。借助于 MySQL 第二索引及 InnoDB 的 BufferPool,Schemaless 的查詢性能很高。
寫入效率不高
數(shù)據(jù)主從復(fù)制效率不高
表損壞問題
難于升級到新版本
在 Postgres 的底層設(shè)計中,它的行數(shù)據(jù)是不可修改的,每個不可修改的行都叫做“元組”,每個唯一的元組都由一個唯一的 標(biāo)志,ctid 也就實際指出了這個元組在磁盤上的物理偏移量。這樣對于一行修改過的數(shù)據(jù)來說,就會對應(yīng)著在物理上有多個元組。表是有索引的,主鍵索引和第二索引都以 B 樹組織,都直接指向 ctid。
除了 ctid 之外還有一個關(guān)鍵字段 prev,它的默認(rèn)值為 null,但對于有數(shù)據(jù)修改的記錄,新的元組里面的 prev 字段里存儲的就是舊元組的 ctid 值。
與 Postgres 相對應(yīng)的是,MySQL 的 InnoDB 引擎主鍵索引和第二也都以 B 樹組織,但是索引指向的是主鍵,而主鍵才真正指向數(shù)據(jù)記錄。而且,InnoDB 的數(shù)據(jù)是可以修改的。兩者實現(xiàn) MVCC 的機制不同,MySQL 依靠 UNDO 空間中的回滾段,而不是象Postgres 依靠在數(shù)據(jù)表空間對同一條數(shù)據(jù)保持多份。
Postgres 和 InnoDB 都通過 WAL來保證數(shù)據(jù)可以在數(shù)據(jù)庫上安全寫入,但對于主從庫的數(shù)據(jù)復(fù)制實現(xiàn)原理并不同。Postgres 會直接把 WAL 發(fā)送到從庫上,讓從庫也執(zhí)行 WAL 來復(fù)制數(shù)據(jù)。而 MySQL 則是發(fā)送 Binlog,在從庫上應(yīng)用 Binlog。
由此,再來看看 Uber 對于 Postgres 有哪些不滿意:
寫放大
一般來說大家介意寫放大https://en.wikipedia.org/wiki/Write_amplification的問題是由于對SSD 磁盤的使用。SSD 磁盤是有壽命的,它的寫入次數(shù)是有限的(雖然數(shù)字很大)。這樣如果應(yīng)用層只是想寫入少量數(shù)據(jù)而已,但數(shù)據(jù)落入磁盤時卻變大了許多倍,那大家就會比較介意了。比如你只是想寫入1K 的數(shù)據(jù),可是最終卻有10K 數(shù)據(jù)落盤。
Postgres 的寫放大問題主要表現(xiàn)在對有索引的表進(jìn)行數(shù)據(jù)更新上。因為 Postgres 的索引都是指向元組的 ctid,而元組又是不可更新的,所以當(dāng)你更新一條記錄時,它會創(chuàng)建一個新的元組存入磁盤,并且要針對所有的索引,為每個索引都創(chuàng)建一條新記錄來指向新的元組,不管你更改的字段和這個索引有沒有關(guān)系。這樣對于 WAL 來說,Postgres 更改一條記錄操作會寫入新的完整記錄,再加上多條索引記錄。
「作者注」 :不過 MySQL 的 InnoDB 其實也是有寫放大問題的。InnoDB 是以數(shù)據(jù)頁的形式組織數(shù)據(jù)的,Linux 上默認(rèn)數(shù)據(jù)頁的大小是 16K。這樣當(dāng)你更改了一條記錄時,最終會把這條記錄所在的數(shù)據(jù)頁整頁刷回磁盤,設(shè)想一下你可能只是改了一個小字段,也許只有 4 個字節(jié),可是最終卻會導(dǎo)致 16K 字節(jié)的寫入。
另外,Postgres 的這個設(shè)計也是有其好處的,它的第二索引直接指向元組的 ctid,這樣在讀取數(shù)據(jù)時效率就非常高。相對應(yīng)地,通過 MySQL 的第二索引去讀數(shù)據(jù)會經(jīng)歷“第二索引——主鍵——數(shù)據(jù)”的過程,MySQL 的讀效率不如 Postgres。這是一個經(jīng)典的讀寫性能權(quán)衡問題,在此 Evan 沒有給出具體的數(shù)字讓我們體會他們的業(yè)務(wù)特征。
主從復(fù)制
Postgres 的寫放大問題最終也反應(yīng)在了主從復(fù)制的日志傳輸上,變成了流量放大問題。Postgres 的主從復(fù)制傳輸?shù)氖?WAL 日志,所以對于一條數(shù)據(jù)更新來說,它要傳輸新的數(shù)據(jù),還要傳輸這張表上每一條索引修改的日志。這樣的流量放大在同一機房內(nèi)還稍可接受,但對于跨機房的情況,傳輸速度和價格等問題讓 Uber 產(chǎn)生了顧慮。Uber 是有跨機房從庫的,一方面是容災(zāi),另一方面是 WAL 的備份,以備有時需要靠它來搭建新的從庫。
MySQL 的確沒有引起流量放大。MySQL 的主從復(fù)制依靠的是 Binlog,它只是記錄這條數(shù)據(jù)的修改,而不在乎這張表上到底有多少索引,所以可以認(rèn)為與 Postgres 相比,它的 Binlog 是一種對數(shù)據(jù)修改的“邏輯”描述。MySQL 從庫上應(yīng)用 Binlog 日志時,如果有第二索引涉及了改動的字段,那就更新第二索引,否則第二索引壓根不需要修改。而且,MySQL 有三種不同的 Binlog 格式,包含了不同數(shù)量的信息來供使用者選擇:
Statement:只傳輸 DML 的 SQL 語句,如:UPDATE users SET birth_year=770 WHERE id = 4。這種模式日志量最小,但在某些場景下和對某些字段來說容易出錯。
Row:對于更改了的數(shù)據(jù),會把修改前和修改后的所有字段值都打印在 Binlog 中。這種模式日志量最大,但也最嚴(yán)謹(jǐn),越來越多的公司在轉(zhuǎn)向這種日志格式。很多日志解析工具更是只工作在這種模式下。
Mixed:上面兩種的結(jié)合體,MySQL 會根據(jù)不同的語句來自行判斷。這種模式日志量居中。
數(shù)據(jù)損壞
Uber 使用 Postgres 9.2 時曾經(jīng)因為一個 BUG 導(dǎo)致了很大的故障。當(dāng)時由于硬件升級的原因他們做了主從切換,結(jié)果就引發(fā)了這個 BUG 導(dǎo)致各個從庫的數(shù)據(jù)全都亂掉了,而且還沒有辦法判斷哪個從庫的哪些數(shù)據(jù)是正確的或者亂的。最終他們確認(rèn)了新的主庫上的數(shù)據(jù)全部正確后,用新主庫的數(shù)據(jù)把所有從庫數(shù)據(jù)全覆蓋了一遍,才算過了這一關(guān)。可是一朝被蛇咬十年怕井繩,他們最后用的版本仍是 Postgres 9.2,原因之一是不想再去踩別的版本的坑了。
「作者注」 :以這個作為拋棄 Postgres 的理由就太容易引起爭議、令人質(zhì)疑 Uber 技術(shù)團(tuán)隊的技術(shù)水平了。在社區(qū)的口碑中,Postgres 的穩(wěn)定性恰恰是高于 MySQL 的,如果因為害怕碰上 Postgres 的 BUG 而轉(zhuǎn)用 MySQL,那……我們只好祝福 Uber 了。
從庫上的 MVCC 支持不好
Postgres 的從庫上并沒有真正的 MVCC,它的數(shù)據(jù)表空間、表空間文件內(nèi)容和主庫是完全一樣的,在從庫上就是依次應(yīng)用 WAL??扇绻麖膸焐嫌幸粋€正在進(jìn)行中的事務(wù)的話,它就會擋住 WAL 的應(yīng)用,從而導(dǎo)致看起來主從同步延遲很大。Postgres 實現(xiàn)了一個機制,如果某個業(yè)務(wù)程序的事務(wù)擋住同步線程太久的話,就直接將那個事務(wù)殺掉。所以如果在從庫上有一些比較大的事務(wù)在運行的話,你可能就會經(jīng)??匆娔涿畹闹鲝耐骄脱舆t了,也會看見自己的操作運行了一段時間就不知被誰殺掉了。并不是每個程序員都很熟悉數(shù)據(jù)庫的底層工作機制,所以這些現(xiàn)象會讓大家覺得很詭異。
「作者注」 :這一點的確是的。相比來說對于這個 Postgres 的復(fù)制過程,MySQL 的主從復(fù)制并不會殺死從庫上的事務(wù)。
Postgres 數(shù)據(jù)庫的升級
Postgres 的數(shù)據(jù)復(fù)制是物理級的,主從數(shù)據(jù)文件完全一致,所以不能支持不同版本之間的主從復(fù)制,比如主庫使用 9.2 從庫使用 9.3,或者相反,等等。Uber 最初使用的是 Postgres 9.1,他們成功的升級成了 9.2,但升級耗費了相當(dāng)長的時間,再加上后來業(yè)務(wù)爆發(fā)式增長,讓他們再也沒能安排下一次升級。而且 Postgres 直到 9.4 之后才有了工具 pglogical來幫助減少升級耗時,可是 pglogical 又不在 Postgres 主分支里,讓使用舊版本的人無所適從。
「作者注」 :有消息 Postgres 的 WAL 日志也將變成邏輯型了,在這樣的功能推出之后,就可以支持不同版本間的數(shù)據(jù)復(fù)制了。
MySQL 的其他優(yōu)點
除了上文所述的幾點,MySQL 還有幾個其他 Postgres 不具備的優(yōu)點:
BufferPoolhttps://dev.mysql.com/doc/refman/5.7/en/innodb-buffer-pool.html:雖然 Postgres 在內(nèi)部有比較小的緩存,但和現(xiàn)在動輒幾百 G 的服務(wù)器內(nèi)存比起來,它的緩存還是太小,對硬件利用率太低了。InnoDB 則有 BufferPool,可以同時用于寫緩沖和讀緩存,用 LRU 管理,大小可配,這樣就把硬件資源充分合理的利用起來了。
連接管理:MySQL 的連接管理是每個連接一個線程,每個線程消耗的資源都很有限,所以 MySQL 可以輕松支持 10000 個以上的連接。可是 Postgres 是每個連接一個進(jìn)程的,進(jìn)程之間通信和共享資源復(fù)雜,消耗資源嚴(yán)重,而且對多連接支持不好。Uber 的業(yè)務(wù)已經(jīng)需要極大的增加數(shù)據(jù)庫連接數(shù),Postgres 已經(jīng)無法滿足需要。
Evan Klitzke 總結(jié)說:
在初期 Postgres 還是工作得很好的,但業(yè)務(wù)擴展時我們就碰上了非常嚴(yán)重的問題。現(xiàn)在我們還是在用著一些 Postgres 數(shù)據(jù)庫,但是主要的數(shù)據(jù)已經(jīng)挪到了 Schemaless 上,有些特別的業(yè)務(wù)也用了 Cassandra 等 NoSQL 數(shù)據(jù)庫。我們現(xiàn)在用 MySQL 用得很好,我們也會寫更多的博客來分享更多關(guān)于 MySQL 在 Uber 的使用內(nèi)容。
審核編輯:劉清
-
存儲器
+關(guān)注
關(guān)注
38文章
7522瀏覽量
164130 -
緩沖器
+關(guān)注
關(guān)注
6文章
1924瀏覽量
45575 -
python
+關(guān)注
關(guān)注
56文章
4804瀏覽量
84915 -
MYSQL數(shù)據(jù)庫
+關(guān)注
關(guān)注
0文章
96瀏覽量
9416 -
MVCC
+關(guān)注
關(guān)注
0文章
13瀏覽量
1481
原文標(biāo)題:為什么Uber的底層存儲從Postgres換成MySQL了?
文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論