精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Uber為什么放棄Postgres選擇遷移到MySQL?

開發(fā) 前端 新聞
Uber 的早期架構(gòu)包含了一個用 Python 開發(fā)的單體后端應(yīng)用程序,這個應(yīng)用程序使用 Postgres 作為數(shù)據(jù)存儲。

 Uber 的早期架構(gòu)包含了一個用 Python 開發(fā)的單體后端應(yīng)用程序,這個應(yīng)用程序使用 Postgres 作為數(shù)據(jù)存儲。從那個時候開始,Uber 的架構(gòu)已經(jīng)發(fā)生了巨大變化,變成了微服務(wù),并采用新的數(shù)據(jù)平臺模型。具體地說,之前使用 Postgres 的地方,現(xiàn)在改用 Schemaless,一種構(gòu)建在 MySQL 之上的新型數(shù)據(jù)庫分片層。在本文中,我們將探討 Postgres 的一些缺點,并解釋為什么我們要在 MySQL 之上構(gòu)建 Schemaless 和其他后端服務(wù)。

1. Postgres 架構(gòu)

我們遭遇了 Postgres 的諸多限制:

  • 低效的寫入操作;
  • 低效的數(shù)據(jù)復(fù)制;
  • 數(shù)據(jù)損壞問題;
  • 糟糕的副本 MVCC 支持;
  • 難以升級到新版本。

我們將通過分析 Postgres 的表和索引在磁盤上的表示方式來探究以上這些限制,并將其與 MySQL 的 InnoDB 存儲引擎進(jìn)行比較。請注意,我們的分析主要是基于我們對較舊的 Postgres 9.2 版本系列的經(jīng)驗。據(jù)我們所知,在本文中討論的內(nèi)部架構(gòu)在較新的 Postgres 發(fā)行版中并未發(fā)生顯著變化,并且至少自 Postgres 8.3 發(fā)行版(現(xiàn)在已近 10 歲)以來,9.2 版本的基本設(shè)計都沒有發(fā)生顯著變化。

磁盤表示

一個關(guān)系型數(shù)據(jù)庫必須能夠執(zhí)行一些關(guān)鍵任務(wù):

  • 提供插入、更新和刪除能力;
  • 提供修改模式的能力;
  • 支持 MVCC,讓不同的數(shù)據(jù)庫連接具有各自的事務(wù)視圖。

這些功能如何協(xié)同工作是設(shè)計數(shù)據(jù)庫磁盤數(shù)據(jù)表示的重要部分。

Postgres 的一個核心設(shè)計是不可變數(shù)據(jù)行。這些不可變數(shù)據(jù)行在 Postgres 中被稱為“元組”。這些元組通過 ctid 來唯一標(biāo)識。從概念上看,ctid 表示元組在磁盤上的位置(即物理磁盤偏移)。可能會有多個 ctid 描述單個行(例如,為了支持 MVCC,可能存在一個數(shù)據(jù)行的多個版本,或者一個數(shù)據(jù)行的舊版本還沒有被 autovacuum 進(jìn)程回收掉)。元組集合構(gòu)成一張表。表本身是有索引的,這些索引被組織成某種數(shù)據(jù)結(jié)構(gòu)(通常是 B 樹),將索引字段映射到 ctid。

通常,這些 ctid 對用戶是透明的,但了解它們的工作原理有助于了解 Postgres 表的磁盤結(jié)構(gòu)。要查看當(dāng)前行的 ctid,可以在語句中將“ctid”添加到列列表中:

  1. uber@[local] uber=> SELECT ctid, * FROM my_table LIMIT 1
  2.  
  3. -[ RECORD 1 ]--------+------------------------------ 
  4.  
  5. ctid | (0,1
  6.  
  7. ...其他字段... 

我們通過一個簡單的用戶表來解釋這個。對于每個用戶,我們都有一個自動遞增的用戶 ID 主鍵、用戶的名字和姓氏以及用戶的出生年份。我們還針對用戶全名(名字和姓氏)定義了復(fù)合二級索引,并針對用戶的出生年份定義了另一個二級索引。創(chuàng)建表的 DDL 可能是這樣的:

  1. CREATE TABLE users ( 
  2.  
  3. id SERIAL, 
  4.  
  5. first TEXT, 
  6.  
  7. last TEXT, 
  8.  
  9. birth_year INTEGER, 
  10.  
  11. PRIMARY KEY (id) 
  12.  
  13. ); 
  14.  
  15. CREATE INDEX ix_users_first_last ON users (first, last); 
  16.  
  17. CREATE INDEX ix_users_birth_year ON users (birth_year); 

這里定義了三個索引:一個主鍵索引和兩個二級索引。

我們往表中插入以下這些數(shù)據(jù),包括一些有影響力的歷史數(shù)學(xué)家:

如前所述,這里的每一行都有一個隱式、唯一的 ctid。因此,我們可以這樣考慮表的內(nèi)部表示形式:

主鍵索引(將 id 映射到 ctid)的定義如下:

B 樹索引是在 id 字段上定義的,并且 B 樹中的每個節(jié)點都存有 ctid 的值。請注意,在這種情況下,由于使用了自動遞增的 ID,B 樹中字段的順序恰好與表中的順序相同,但并不是一直都這樣。

二級索引看起來差不多,主要區(qū)別在于字段的存儲順序不同,因為 B 樹必須按字典順序來組織。(first,last) 索引從名字的字母表順序開始:

類似的,birth_year 索引按照升序排列,如下所示:

對于后兩種情況,二級索引中的 ctid 字段不是按照字典順序遞增的,這與自動遞增主鍵的情況不同。

假設(shè)我們需要更新該表中的一條記錄,比如我們要更新 al-Khwārizmī的出生年份。如前所述,行的元組是不可變的。因此,為了更新記錄,我們向表中添加了一個新的元組。這個新的元組有一個新的 ctid,我們將其稱為 I。Postgres 需要區(qū)分新元組 I 與舊元組 D。在內(nèi)部,Postgres 在每個元組中保存了一個版本字段和一個指向先前元組的指針(如果有的話)。因此,表的最新結(jié)構(gòu)如下所示:

只要存在 al-Khwārizmī行的兩個版本,索引中就必須同時包含兩個行的條目。為簡便起見,我們省略了主鍵索引,只顯示了二級索引,如下所示:

我們用紅色表示舊數(shù)據(jù)行,用綠色表示新數(shù)據(jù)行。Postgres 使用另一個版本字段來確定哪個元組是最新的。數(shù)據(jù)庫根據(jù)這個字段確定哪個元組對不允許查看新版本數(shù)據(jù)的事務(wù)可見。

在 Postgres 中,主索引和二級索引都直接指向磁盤上的元組偏移量。當(dāng)元組位置發(fā)生變化時,必須更新所有索引。

復(fù)制

當(dāng)我們在表中插入新行時,如果啟用了流式復(fù)制,Postgres 需要對其進(jìn)行復(fù)制。為了能夠在發(fā)生崩潰后恢復(fù),數(shù)據(jù)庫維護了預(yù)寫日志(WAL),并用它來實現(xiàn)兩階段提交。即使未啟用流式復(fù)制,數(shù)據(jù)庫也必須維護 WAL,因為 WAL 可以保證 ACID 中的原子性和持久性。

為了更好地理解 WAL,我們可以想象一下如果數(shù)據(jù)庫意外發(fā)生崩潰(例如突然斷電)會發(fā)生什么。WAL 代表了一系列數(shù)據(jù)庫計劃對表和索引在磁盤上內(nèi)容做出的更改。Postgres 守護進(jìn)程在啟動時會將 WAL 的數(shù)據(jù)與磁盤上的實際數(shù)據(jù)進(jìn)行對比。如果 WAL 中包含未反映到磁盤上的數(shù)據(jù),數(shù)據(jù)庫就會更正元組或索引數(shù)據(jù),并回滾出現(xiàn)在 WAL 中但在事務(wù)中沒有被提交的數(shù)據(jù)。

Postgres 通過將主數(shù)據(jù)庫上的 WAL 發(fā)送給副本來實現(xiàn)流式復(fù)制。每個副本數(shù)據(jù)庫就像是在進(jìn)行崩潰恢復(fù),不斷地應(yīng)用 WAL 更新。流式復(fù)制和實際發(fā)生崩潰恢復(fù)之間的唯一區(qū)別是,處于“熱備用”模式的副本在應(yīng)用 WAL 時可以提供查詢服務(wù),但真正處于崩潰恢復(fù)模式的 Postgres 數(shù)據(jù)庫通常會拒絕提供查詢服務(wù),直到數(shù)據(jù)庫實例完成崩潰恢復(fù)過程。

因為 WAL 實際上是為實現(xiàn)崩潰恢復(fù)而設(shè)計的,所以它包含了底層的磁盤更新信息。WAL 包含了元組及其磁盤偏移量(即行 ctid)在磁盤上的表示。如果副本完全與主數(shù)據(jù)庫同步,此時暫停 Postgres 的主數(shù)據(jù)庫和副本,那么副本的磁盤內(nèi)容與主數(shù)據(jù)庫的磁盤內(nèi)容將完全一致。因此,如果副本與主數(shù)據(jù)庫不同步,可以用 rsync 之類的工具來修復(fù)。

2. Postgres 的設(shè)計所帶來的后果

Postgres 的設(shè)計導(dǎo)致 Uber 的數(shù)據(jù)效率低下,還讓我們遇到了很多麻煩。

寫入放大

Postgres 的第一個問題是寫入放大。通常,寫入放大是指將數(shù)據(jù)寫入 SSD 磁盤時遇到的問題:小的邏輯更新(例如,寫入幾個字節(jié))在轉(zhuǎn)換到物理層時會放大,成本會變高。在之前的示例中,如果我們對 al-Khwārizmī的出生年份進(jìn)行小的邏輯更新,必須進(jìn)行至少四個物理更新:

  1. 將新的行元組寫入表空間;
  2. 更新主鍵索引;
  3. 更新 (first,last) 索引;
  4. 更新 birth_year 索引。

實際上,這四個更新也只反映了對主表空間的寫操作。除此之外,這些寫操作也需要反映在 WAL 中,因此磁盤上的寫操作總數(shù)會變得更多。

這里值得注意的是更新 2 和更新 3。在更新 al-Khwārizmī的出生年份時,實際上并沒有修改它的主鍵,也沒有修改名字和姓氏。但盡管如此,仍然必須在數(shù)據(jù)庫中創(chuàng)建新的行元組,以便更新這些索引。對于具有大量二級索引的表,這些多余的步驟可能會導(dǎo)致效率低下。例如,如果我們在一張表中定義了十二個索引,即使只更新了單個索引對應(yīng)的字段,也必須將該更新傳播給所有 12 個索引,以便反映新行的 ctid。

復(fù)制

這個寫入放大問題自然也轉(zhuǎn)化到了復(fù)制層,因為復(fù)制發(fā)生在磁盤級別。數(shù)據(jù)庫并不會復(fù)制小的邏輯記錄,例如“將 ctid D 的出生年份更改為 770”,而是將之前的 4 個 WAL 條目傳播到網(wǎng)絡(luò)上。因此,寫入放大問題也轉(zhuǎn)化為復(fù)制放大問題,Postgres 復(fù)制數(shù)據(jù)流很快變得非常冗長,可能占用大量帶寬。

如果 Postgres 復(fù)制僅發(fā)生在單個數(shù)據(jù)中心內(nèi),那么復(fù)制帶寬可能就不是問題。現(xiàn)代網(wǎng)絡(luò)設(shè)備和交換機可以處理大量帶寬,很多托管服務(wù)提供商還提供了免費或便宜的數(shù)據(jù)中心內(nèi)部帶寬。但是,如果要在數(shù)據(jù)中心之間進(jìn)行復(fù)制,問題就會迅速升級。例如,Uber 最初使用了西海岸托管中心里的物理服務(wù)器。為了進(jìn)行災(zāi)備,我們在東海岸托管中心添加了服務(wù)器。于是,我們在西部數(shù)據(jù)中心里有一個主 Postgres 實例(加上副本),在東部也有一個副本集。

級聯(lián)復(fù)制將數(shù)據(jù)中心間的帶寬限制為只能滿足主數(shù)據(jù)庫和單個副本之間的帶寬需求,雖然第二個數(shù)據(jù)中心里還有很多副本。因為 Postgres 復(fù)制協(xié)議的冗繁,使用了大量索引的數(shù)據(jù)庫會有很大的數(shù)據(jù)量。購買跨地域大帶寬成本非常高昂,即使錢不成問題,也不可能獲得與本地帶寬類似的效果。這個帶寬問題也給 WAL 歸檔帶來了麻煩。除了將所有 WAL 更新從西海岸發(fā)送到東海岸之外,我們還要將所有 WAL 都存檔到文件存儲服務(wù)中,這是為了確保在發(fā)生災(zāi)難時我們可以還原數(shù)據(jù)。在早期的流量高峰期間,我們寫入存儲服務(wù)的帶寬不夠快,無法跟上 WAL 的寫入速度。

數(shù)據(jù)損壞

在例行升級主數(shù)據(jù)庫以便增加數(shù)據(jù)庫容量的過程中,我們遭遇了 Postgres 9.2 個一個 bug。因為副本在切換時間方面出現(xiàn)了錯誤,導(dǎo)致其中一些副本錯誤地應(yīng)用了一小部分 WAL 記錄。由于這個問題,一些本應(yīng)由版本控制機制標(biāo)記為無效的記錄實際上并未被標(biāo)記為無效。

下面的查詢說明了這個錯誤將如何影響我們的用戶表:

  1. SELECT * FROM users WHERE id = 4

這個查詢將返回兩條記錄:初始的 al-Khwārizmī行(出生年份為 780 CE)和新的 al-Khwārizmī行(出生年份為 770 CE)。如果將 ctid 添加到 WHERE 中,對于這兩條返回的記錄,我們將看到不同的 ctid 值。

這個問題非常煩人。首先,我們無法得知這個問題究竟影響了多少行數(shù)據(jù)。數(shù)據(jù)庫返回的重復(fù)結(jié)果在很多情況下會導(dǎo)致應(yīng)用程序邏輯故障。我們最終添加了防御性編程語句,用來檢測會出現(xiàn)這個問題的表。這個錯誤影響到了所有服務(wù)器,而在不同的副本實例上損壞的數(shù)據(jù)行是不一樣的。也就是說,在其中一個副本實例上,行 X 可能是壞的,行 Y 是好的,但是在另一副本實例上,行 X 可能是好,行 Y 可能是壞的。我們無法確定數(shù)據(jù)損壞的副本數(shù)量以及問題是否影響了主數(shù)據(jù)庫。

據(jù)我們所知,每個數(shù)據(jù)庫只有幾行數(shù)據(jù)會出現(xiàn)這個問題,但我們擔(dān)心的是,由于復(fù)制發(fā)生在物理級別,最后可能會完全破壞數(shù)據(jù)庫索引。B 樹索引很重要的一點是必須定期進(jìn)行重新平衡(rebalance),并且當(dāng)子樹移動到新的磁盤位置時,這些重新平衡操作可能會完全改變樹的結(jié)構(gòu)。如果移動了錯誤的數(shù)據(jù),則可能導(dǎo)致樹的大部分完全無效。

最后,我們找到了問題所在,并確定新的主數(shù)據(jù)庫沒有損壞的數(shù)據(jù)行。我們通過從主數(shù)據(jù)庫的最新快照重新同步所有副本(這是一個費力的過程)來修復(fù)副本的數(shù)據(jù)損壞問題。

我們遇到的錯誤只出現(xiàn)在 Postgres 9.2 的某些版本中,并且已經(jīng)修復(fù)了很長時間了。但是,我們?nèi)匀粨?dān)心此類錯誤會再次發(fā)生。新版本的 Postgres 可能還會出現(xiàn)此類錯誤,并且由于數(shù)據(jù)復(fù)制的方式,這類問題有可能被傳播到所有的數(shù)據(jù)庫中。

副本 MVCC

Postgres 沒有提供真正的副本 MVCC 支持。副本只應(yīng)用 WAL 更新,導(dǎo)致它們在任何時候都具有與主數(shù)據(jù)庫相同的磁盤數(shù)據(jù)副本。這種設(shè)計給 Uber 帶來了麻煩。

Postgres 需要為 MVCC 維護舊數(shù)據(jù)的一個副本。如果流式復(fù)制遇到一個正在執(zhí)行的事務(wù),而數(shù)據(jù)庫更新影響到了事務(wù)范圍內(nèi)的行,那么更新操作就會被阻塞。在這種情況下,Postgres 會暫停 WAL 線程,直到事務(wù)結(jié)束。如果事務(wù)處理要花費很長時間,這就會是個問題,因為副本可能嚴(yán)重滯后于主數(shù)據(jù)庫。因此,Postgres 在這種情況下應(yīng)用超時策略:如果一個事務(wù)導(dǎo)致 WAL 發(fā)生阻塞一定的時間,Postgres 將會終止這個事務(wù)。

這種設(shè)計意味著副本通常會比主數(shù)據(jù)庫落后幾秒鐘,很容易出現(xiàn)事務(wù)被終止的情況。例如,假設(shè)開發(fā)人員寫了一些代碼,需要通過電子郵件將收據(jù)發(fā)送給用戶。根據(jù)編寫方式的不同,代碼可能會隱式地讓數(shù)據(jù)庫事務(wù)處于打開狀態(tài),直到電子郵件完成發(fā)送為止。盡管在執(zhí)行不相關(guān)的阻塞 IO 時一直打開數(shù)據(jù)庫事務(wù)是很糟糕的做法,但大多數(shù)工程師并不是數(shù)據(jù)庫專家,他們可能也不知道有這個問題,特別是在使用隱藏了底層細(xì)節(jié)的 ORM 框架時。

升級 Postgres

由于復(fù)制發(fā)生在物理層面,所以我們無法在 Postgres 的不同版本之間復(fù)制數(shù)據(jù)。Postgres 9.3 的主數(shù)據(jù)庫不能被復(fù)制到 Postgres 9.2 的副本,而 Postgres 9.2 的主數(shù)據(jù)庫也不能被復(fù)制到 Postgres 9.3 的副本。

我們按照以下這些步驟從一個 Postgres GA 版本升級到另一個版本:

  • 關(guān)閉主數(shù)據(jù)庫。
  • 在主數(shù)據(jù)庫上運行 pg_upgrade 命令,這個命令會就地更新主數(shù)據(jù)庫數(shù)據(jù)。對于大型數(shù)據(jù)庫,通常需要花費數(shù)小時,并且在這個過程過程中無法從主數(shù)據(jù)庫讀取數(shù)據(jù)。
  • 再次啟動主數(shù)據(jù)庫。
  • 創(chuàng)建主數(shù)據(jù)庫的最新快照。這一步驟完全復(fù)制了主數(shù)據(jù)庫的所有數(shù)據(jù),因此大型數(shù)據(jù)庫也需要花費數(shù)小時。
  • 擦除所有副本,并將最新的快照從主數(shù)據(jù)庫還原到副本上。
  • 將副本帶回到復(fù)制層次結(jié)構(gòu)中。等待副本完全跟上主數(shù)據(jù)庫的所有更新。

我們從 Postgres 9.1 開始,并成功完成了升級過程,遷移到了 Postgres 9.2。但是,這個過程花費了數(shù)小時,我們無力承擔(dān)再次執(zhí)行這種升級過程的費用。到 Postgres 9.3 發(fā)布時,Uber 的規(guī)模增長極大增加了我們的數(shù)據(jù)集,因此升級時間就變得更長了。因此,即使 Postgres 9.5 已經(jīng)發(fā)布了,我們的 Postgres 實例仍然是 9.2 版本。

如果你的 Postgres 是 9.4 或更高版本,可以使用 pgologic 之類的東西,它為 Postgres 實現(xiàn)了一個邏輯復(fù)制層。你可以用它在不同的 Postgres 版本之間復(fù)制數(shù)據(jù),這意味著可以從 9.4 升級到 9.5,而不會造成大面積停機。不過,這個功能仍然是有問題的,因為它尚未被集成到 Postgres 主線中。而對于那些使用較舊版本的 Postgres 的人來說,pgologic 并不適用。

3. MySQL 架構(gòu)

上文解釋了 Postgres 的一些局限性,接下來,我們將解釋為什么 MySQL 會成為 Uber 工程團隊存儲項目(例如 Schemaless)的新工具。在很多情況下,我們發(fā)現(xiàn) MySQL 更適合我們的使用場景。為了理解這些差異,我們研究了 MySQL 的架構(gòu),并將其與 Postgres 進(jìn)行了對比。我們專門分析了 MySQL 的 InnoDB 存儲引擎。

InnoDB 的磁盤表示

與 Postgres 一樣,InnoDB 支持 MVCC 和可變數(shù)據(jù)等高級功能。關(guān)于 InnoDB 磁盤表示的詳盡細(xì)節(jié)不在本文的討論范圍之內(nèi),我們將把重點放在它與 Postgres 的主要區(qū)別上。

最主要的架構(gòu)差異是:Postgres 直接將索引記錄映射到磁盤上的位置,而 InnoDB 使用了二級結(jié)構(gòu)。InnoDB 的二級索引有一個指向主鍵值的指針,而不是指向磁盤位置的指針(如 Postgres 中的 ctid)。因此,MySQL 會將二級索引將索引鍵與主鍵相關(guān)聯(lián):

要基于 (first, last) 索引 執(zhí)行查詢,需要進(jìn)行兩次查找。第一次先搜索表,找到記錄的主鍵。在找到主鍵之后,搜索主鍵索引,找到數(shù)據(jù)行對應(yīng)的磁盤位置。

所以,在執(zhí)行二級查找時,InnoDB 相比 Postgres 略有不利,因為 InnoDB 必須搜索兩個索引,而 Postgres 只需要搜索一個。但是,由于數(shù)據(jù)已經(jīng)規(guī)范化,在更新行數(shù)據(jù)時只需要更新實際發(fā)生變化的索引記錄。此外,InnoDB 通常會在原地進(jìn)行行數(shù)據(jù)更新。為了支持 MVCC,如果舊事務(wù)需要引用一行數(shù)據(jù),MySQL 會將舊行復(fù)制到一個叫作回滾段的特殊區(qū)域中。

我們來看看更新 al-Khwārizmī的出生年份會發(fā)生什么。如果空間足夠,id 為 4 的那一行數(shù)據(jù)中的出生年份字段會進(jìn)行原地更新(實際上,這個更新總是發(fā)生在原地,因為出生年份是一個占用固定空間量的整數(shù))。出生年份索引也進(jìn)行原地更新。舊數(shù)據(jù)行將被復(fù)制到回滾段。主鍵索引不需要更新,(first, last) 索引也不需要更新。即使這張表有大量索引,也只需要更新包含 birth_year 字段的索引。假設(shè)我們基于 signup_date、last_login_time 等字段建立了索引,我們不需要更新這些索引,但在 Postgres 中需要更新。

這種設(shè)計還讓數(shù)據(jù)清理和壓縮變得更加高效。回滾段中的數(shù)據(jù)可以直接清除,相比之下,Postgres 的 autovacuum 進(jìn)程必須進(jìn)行全表掃描來識別哪些行可以清除。

MySQL 使用了額外的中間層:二級索引記錄指向主索引記錄,主索引保存了數(shù)據(jù)行在磁盤上的位置。如果數(shù)據(jù)行偏移量發(fā)生變化,只需要更新主索引。

復(fù)制

MySQL 支持多種不同的復(fù)制模式:

  • 基于語句的復(fù)制將會復(fù)制邏輯 SQL 語句(它將按字面意義復(fù)制 SQL 語句,例如:UPDATE users SET birth_year = 770 WHERE id = 4);
  • 基于行的復(fù)制將會復(fù)制發(fā)生變化的行記錄;
  • 混合復(fù)制將這兩種模式混合在一起。

這幾種模式各有優(yōu)缺點。基于語句的復(fù)制通常是最緊湊的,但可能需要副本應(yīng)用大量語句來更新少量數(shù)據(jù)。另一方面,基于行的復(fù)制(與 Postgres WAL 復(fù)制類似)雖然更為冗繁,但更具可預(yù)測性和在副本上的更新效率。

在 MySQL 中,只有主索引有指向行的磁盤偏移量的指針。在進(jìn)行復(fù)制時,這具有重要的意義。MySQL 復(fù)制流只需要包含有關(guān)行的邏輯更新信息。對于類似“將行 X 的時間戳從 T_1 更改為 T_2”這樣的更新,副本會自動推斷需要修改哪些索引。

相比之下,Postgres 復(fù)制流包含了物理變更,例如“在磁盤偏移量 8,382,491 處寫入字節(jié) XYZ”。在使用 Postgres 時,對磁盤進(jìn)行的每一個物理變更都需要包含在 WAL 流中。較小的邏輯修改(例如更新時間戳)也需要執(zhí)行很多磁盤變更:Postgres 必須插入新的元組,并更新所有索引,讓它們指向這個元組,所以會有很多變更被放入 WAL 流中。這種設(shè)計差異意味著 MySQL 復(fù)制二進(jìn)制日志比 PostgreSQL WAL 流更緊湊。

復(fù)制方式也對副本的 MVCC 產(chǎn)生重要影響。由于 MySQL 復(fù)制流具有邏輯更新,副本可以具有真正的 MVCC 語義,所以對副本的讀取查詢不會阻塞復(fù)制流。相比之下,Postgres WAL 流包含了磁盤上的物理更改,Postgres 副本無法應(yīng)用與讀取查詢相沖突的復(fù)制更新,因此無法實現(xiàn) MVCC。

MySQL 的復(fù)制架構(gòu)意味著即使有 bug 導(dǎo)致表損壞,也不太可能會發(fā)生災(zāi)難性故障。因為復(fù)制發(fā)生在邏輯層,所以像重新平衡 B 樹之類的操作永遠(yuǎn)不會導(dǎo)致索引損壞。一個典型的 MySQL 復(fù)制問題是語句被跳過(或者被應(yīng)用兩次),這可能導(dǎo)致數(shù)據(jù)丟失或無效,但不會導(dǎo)致數(shù)據(jù)庫中斷。

最后,MySQL 的復(fù)制架構(gòu)可以很容易在不同的 MySQL 版本之間進(jìn)行復(fù)制。MySQL 的邏輯復(fù)制格式還意味著存儲引擎層中的磁盤變更不會影響復(fù)制格式。在進(jìn)行 MySQL 升級時,典型的做法是一次將更新應(yīng)用于一個副本,在更新完所有副本后,將其中一個提升為新的主副本。這幾乎可以實現(xiàn)零停機升級,很容易就可以讓 MySQL 保持最新狀態(tài)。

4. MySQL 的其他優(yōu)勢

到目前為止,我們介紹了 Postgres 和 MySQL 的磁盤架構(gòu)。MySQL 還有其他一些重要方面也讓它的性能明顯優(yōu)于 Postgres。

緩沖池

首先,兩個數(shù)據(jù)庫的緩存方式不同。Postgres 為內(nèi)部緩存分配了一些內(nèi)存,但是與計算機上的內(nèi)存總量相比,這些緩存通常很小。為了提高性能,Postgres 允許內(nèi)核通過頁面緩存自動緩存最近訪問的磁盤數(shù)據(jù)。例如,我們最大的 Postgres 副本有 768 GB 的可用內(nèi)存,但實際上只有 25 GB 被用作 Postgres 的進(jìn)程 RSS 內(nèi)存,這樣就為 Linux 頁面緩存留出了 700 GB 以上的可用內(nèi)存。

這種設(shè)計的問題在于,與訪問 RSS 內(nèi)存相比,通過頁面緩存訪問數(shù)據(jù)實際上開銷更大。為了從磁盤上查找數(shù)據(jù),Postgres 進(jìn)程發(fā)出 lseek 和 read 系統(tǒng)調(diào)用來定位數(shù)據(jù)。這些系統(tǒng)調(diào)用中的每一個都會引起上下文切換,這比從主存儲器訪問數(shù)據(jù)的開銷更大。實際上,Postgres 在這方面甚至還沒有完全進(jìn)行優(yōu)化:Postgres 并未利用 pread 系統(tǒng)調(diào)用,這個系統(tǒng)調(diào)用會將 seek 和 read 操作合并為一個系統(tǒng)調(diào)用。

相比之下,InnoDB 存儲引擎通過緩沖池實現(xiàn)了自己的 LRU。從邏輯上講,這與 Linux 頁面緩存相似,但它是在用戶空間中實現(xiàn)的。盡管 InnoDB 緩沖池的設(shè)計比 Postgres 的設(shè)計要復(fù)雜得多,但它具備一些優(yōu)勢:

  1. 可以實現(xiàn)自定義 LRU。例如,可以檢測出可能會破壞 LRU 的訪問模式,并防止其造成更大問題。
  2. 較少的上下文切換。通過 InnoDB 緩沖池訪問的數(shù)據(jù)不需要進(jìn)行用戶 / 內(nèi)核上下文切換。最壞的情況是發(fā)生 TLB 未命中,這些開銷相對較小,可以通過使用大頁面來緩解。

連接處理

MySQL 通過一個連接一個線程的方式來實現(xiàn)并發(fā)連接。這種開銷相對較低,每個線程都有自己的棧內(nèi)存和分配給特定連接的緩沖堆內(nèi)存。在 MySQL 中使用 10000 個左右的并發(fā)連接,這種情況并不少見,實際上,在我們現(xiàn)有的某些 MySQL 實例上,連接數(shù)已經(jīng)接近這個數(shù)字。

但是,Postgres 采用的是一個連接一個進(jìn)程的設(shè)計,這比一個連接一個線程的設(shè)計要昂貴得多。派生新進(jìn)程比生成新線程占用更多的內(nèi)存。此外,進(jìn)程之間的 IPC 比線程之間的 IPC 也昂貴得多。Postgres 9.2 通過 System V IPC 原語實現(xiàn) IPC,而不是使用輕量級的 futex。futex 比 System V IPC 更快,因為通常情況下,futex 不存在竟態(tài)條件,因此無需進(jìn)行上下文切換。

除了內(nèi)存和 IPC 開銷,Postgres 似乎也無法很好地支持大量連接,即使有足夠的可用內(nèi)存。我們在 Postgres 中使用數(shù)百個活動連接時遇到了大問題。Postgres 文檔建議采用進(jìn)程外連接池機制來處理大量連接,但沒有詳細(xì)說明是為什么。因此,我們使用 pgbouncer 來處理 Postgres 的連接池。但是,我們的后端服務(wù)偶爾會出現(xiàn) bug,導(dǎo)致它們打開的活動連接過多,從而延長了宕機時間。

5. 結(jié)論

在 Uber 早期,Postgres 為我們提供了很好的服務(wù),但是隨著公司規(guī)模的增長,我們遇到了伸縮性問題。現(xiàn)在,我們?nèi)匀槐A袅艘恍┡f的 Postgres 實例,但大部分?jǐn)?shù)據(jù)庫都建立在 MySQL 之上(通常使用 Schemaless 層),或者在某些特殊情況下會使用像 Cassandra 這樣的 NoSQL 數(shù)據(jù)庫。

 

責(zé)任編輯:張燕妮 來源: 架構(gòu)頭條
相關(guān)推薦

2020-01-18 09:35:03

微服務(wù)團隊架構(gòu)

2020-03-12 08:00:34

MySQL遷移TiDB

2018-12-21 11:26:49

MySQLMongoDB數(shù)據(jù)庫

2021-07-07 10:48:00

DigGoWire

2021-01-25 07:40:37

Druid數(shù)據(jù)eBay

2020-04-20 08:08:23

MongoDBElasticsear數(shù)據(jù)庫

2021-02-01 07:20:51

KafkaPulsar搜索

2020-09-09 09:38:47

GoLangNodeJS編程語言

2020-10-13 09:25:27

ESClickHouse搜索引擎

2018-09-28 10:06:21

移動開發(fā)App

2020-02-24 09:38:05

PythonGo語言Linux

2023-09-14 23:08:54

PostgreSQLMySQL

2023-09-22 10:48:42

MySQLPostgreSQL

2020-08-14 10:40:35

RestTemplatRetrofitJava

2017-08-31 17:43:06

云端遷移云計算

2021-04-22 15:55:56

UCaaS統(tǒng)一通信企業(yè)通信

2013-06-21 13:49:08

MariaDB

2011-06-08 10:30:08

MongoDB

2019-08-20 09:24:54

Python編程語言Java

2024-06-24 07:58:00

點贊
收藏

51CTO技術(shù)棧公眾號

美女av一区| 九色视频在线观看免费播放 | 精品人伦一区二区三区 | 欧美日韩国产免费一区二区| 免费国产成人看片在线| 农村少妇久久久久久久| 欧美96一区二区免费视频| 欧美成人h版在线观看| 中文字幕在线永久| 久久人体av| 午夜视频一区二区三区| 亚洲欧美国产精品桃花| 亚洲国产精品久久久久久久| 亚洲少妇在线| 欧美成年人视频| 欧美 日韩 国产 成人 在线观看 | 8v天堂国产在线一区二区| 青草视频在线观看视频| av在线播放网| 99这里都是精品| 欧美视频国产精品| 欧美一区二区视频在线观看2022 | 国产深夜精品| 欧美另类在线播放| 91狠狠综合久久久久久| 精品视频在线你懂得| 在线不卡中文字幕播放| 国产精品少妇在线视频| 国产精品探花在线| 最新国产精品久久精品| 五月天国产一区| 三级黄视频在线观看| 丰满放荡岳乱妇91ww| 成人黄色免费在线观看| 中文字幕久久熟女蜜桃| 老司机一区二区三区| 欧美高清在线播放| 国产精品丝袜一区二区| 日韩成人三级| 中文字幕日本欧美| 小早川怜子久久精品中文字幕| 国产乱论精品| 亚洲福利视频网站| 亚洲精品一二三四| 国产精品白丝久久av网站| 欧美日韩中字一区| 中文久久久久久| 免费观看成人性生生活片 | 国产理论片在线观看| 免费亚洲电影在线| 国产精品欧美激情| 丰满熟女人妻一区二区三| 手机精品视频在线观看| 清纯唯美日韩制服另类| 四虎成人永久免费视频| 亚洲综合日本| 国产成人精品在线视频| 日韩乱码一区二区三区| 蜜桃视频在线观看一区| 成人欧美一区二区三区在线 | 国产一区视频在线看| 成人美女av在线直播| 国产又粗又猛又爽又黄91| 激情综合色综合久久综合| 成人免费视频在线观看超级碰| 国产三级视频在线播放| 国产精品系列在线播放| 国产伦理一区二区三区| 午夜性色福利影院| 久久久电影一区二区三区| 日韩偷拍一区二区| 日韩理伦片在线| 一区二区三区四区乱视频| 国产女主播自拍| 91精品产国品一二三产区| 一本色道久久加勒比精品| 成年人视频在线免费| 国产精品麻豆成人av电影艾秋| 欧美日韩一区高清| 国产又粗又猛大又黄又爽| 风间由美中文字幕在线看视频国产欧美| 精品国产百合女同互慰| 午夜理伦三级做爰电影| 色综合天天综合网中文字幕| 久久99久久亚洲国产| 日韩少妇裸体做爰视频| 日本怡春院一区二区| 91网在线免费观看| 天天射天天色天天干| 国产女同互慰高潮91漫画| 一区二区不卡在线观看| 蜜臀av在线| 91久久精品午夜一区二区| www.午夜av| 亚洲老女人视频免费| 久久精品视频一| 久久国产精品系列| 久久99国产精品久久| 久久99精品久久久久久秒播放器| 成年人视频在线看| 亚洲午夜久久久久| 杨幂毛片午夜性生毛片| 中文字幕区一区二区三| 在线观看久久av| 五月天综合在线| 日本视频一区二区三区| 国产欧美一区二区三区不卡高清| 一广人看www在线观看免费视频| 亚洲国产精品麻豆| 国内外成人免费在线视频| 女仆av观看一区| 久久夜精品香蕉| 伊人久久国产精品| 97精品超碰一区二区三区| 樱空桃在线播放| 国产精品久久亚洲不卡| 亚洲精品美女免费| 一级黄色录像视频| 美女网站视频久久| 免费在线观看一区二区| 黄色在线观看视频网站| 91精品国产色综合久久不卡电影| 国产手机在线观看| 亚洲高清资源| 91香蕉嫩草影院入口| а天堂8中文最新版在线官网| 亚洲成av人影院| 69久久精品无码一区二区| 成人中文视频| 人人爽久久涩噜噜噜网站| 欧美一区二区三区黄片 | 国产无遮挡aaa片爽爽| 国内精品在线播放| 亚洲欧洲日韩精品| 亚洲不卡系列| 一区二区三区美女xx视频| 在线精品免费视| 91在线精品一区二区| 免费看黄在线看| 中文字幕亚洲在线观看| 欧美日本高清一区| www.久久成人| 亚洲一区二区三区四区五区中文 | 亚洲人成网亚洲欧洲无码| 国产69精品久久久久9999| 丰满少妇被猛烈进入| 一区二区三区精品在线观看| 亚洲国产综合av| 欧美精品午夜| 懂色av一区二区三区在线播放| 在线观看男女av免费网址| 91精品国产入口| 激情小说中文字幕| 成人免费视频国产在线观看| 成人黄色大片网站| 噜噜噜天天躁狠狠躁夜夜精品| 97人人做人人爱| 水莓100在线视频| 色琪琪一区二区三区亚洲区| 在线免费观看视频| 激情五月婷婷综合网| 男女h黄动漫啪啪无遮挡软件| 久久伊人影院| 国模私拍视频一区| 日本不卡视频一区二区| 欧美亚洲国产一区二区三区| 国产真实乱在线更新| 国产成人午夜电影网| 成熟丰满熟妇高潮xxxxx视频| 亚洲资源网你懂的| 国产精品入口夜色视频大尺度 | 99精品视频国产| 欧美精品三区| 欧美日韩免费观看一区| 成人亚洲网站| 色综合老司机第九色激情| 婷婷色在线观看| 在线观看亚洲成人| 成人观看免费视频| 久久久精品免费免费| 精品亚洲一区二区三区四区| 国产精品mm| 欧美日韩国产三区| 久久国产精品美女| 欧美性受xxx| 国产黄大片在线观看画质优化| 亚洲成人精品久久久| 国产精品久久久久久久久夜色| 日韩美女啊v在线免费观看| 中文字幕在线观看91| 久久精品一区| 超碰人人爱人人| 国产一卡不卡| 国产精品久久久久久久久婷婷| 桃花岛成人影院| 久久99国产精品自在自在app| 毛片在线播放网站| 日韩欧美一区在线| 国产一级一级国产| 亚洲尤物在线视频观看| 成人无码av片在线观看| 懂色av一区二区夜夜嗨| 亚洲国产精品三区| 亚洲美女网站| 最新av在线免费观看| 美女久久久久| 国产精品毛片va一区二区三区| 精品福利在线| 日韩av电影国产| 91超碰在线播放| 久久中文字幕国产| 91社区在线| 亚洲区在线播放| 国模私拍视频在线| 欧美久久久久久久久久| 黄色片中文字幕| 亚洲成a人v欧美综合天堂下载| 中文字幕电影av| 国产精品三级电影| 波多野结衣福利| www.99精品| 97中文字幕在线观看| 激情综合五月婷婷| 在线观看免费av网址| 日韩精品免费专区| 国产精品后入内射日本在线观看| 欧美88av| 黄色片免费在线观看视频| 天天久久综合| 五月天久久狠狠| 不卡日本视频| 午夜老司机精品| 精品少妇av| 日韩欧美在线电影| 精品国产a一区二区三区v免费| 欧美激情论坛| 午夜a一级毛片亚洲欧洲| 国内精品久久久久久久果冻传媒| 日韩三级av高清片| 97久久人人超碰caoprom欧美| 日本国产一区| 成人亚洲激情网| 亚洲伊人精品酒店| 91沈先生作品| 一区二区三区在线资源| 国产精品久久久久久久久久久久午夜片| 久久99成人| 丁香五月网久久综合| 国产成人澳门| 精品在线视频一区二区| 亚洲第一论坛sis| 日本一区二区三区视频在线观看| 久久99国内| 日韩欧美一区二区视频在线播放| 日韩欧美伦理| 亚洲小说欧美另类激情| 欧美精品国产一区| 免费观看美女裸体网站| 国产农村妇女精品一区二区| 老熟妇仑乱视频一区二区| 天堂一区二区在线| 一女二男3p波多野结衣| 国产真实乱对白精彩久久| 精品国产一二区| 99久久精品情趣| 久久久久久久久久久久| 国产精品色呦呦| 九九热这里有精品视频| 无码av中文一区二区三区桃花岛| 日韩在线 中文字幕| 欧美日韩国产电影| 亚洲精品久久久蜜桃动漫| 亚洲精品黄网在线观看| 国产黄在线观看| 久久成人精品一区二区三区| av资源新版天堂在线| 国产91在线播放| 91嫩草国产线观看亚洲一区二区| www.久久草| 国产欧美日韩精品一区二区三区| 中文字幕免费在线不卡| 亚洲激情欧美| 超碰在线97免费| 粉嫩一区二区三区在线看| 一区二区不卡免费视频| 亚洲欧美综合色| 日本在线观看中文字幕| 欧美综合欧美视频| 成人av手机在线| 亚洲午夜精品视频| 18视频在线观看网站| 国产成人精品免费视频| 一区二区在线免费播放| 日韩欧美三级一区二区| 狠狠色综合网| 成年人三级黄色片| 91香蕉视频黄| 黄色一级视频在线观看| 欧美影院午夜播放| 日韩中文字幕免费观看| 日韩一区二区三区国产| 亚洲国产成人二区| 91久久极品少妇xxxxⅹ软件| 要久久电视剧全集免费| 大荫蒂性生交片| 美女性感视频久久| 亚洲天堂视频一区| 亚洲国产一区视频| 国产一区二区三区黄片| 亚洲欧美一区二区三区情侣bbw| 影音先锋在线播放| 国产免费一区二区三区在线能观看| 欧美人与动xxxxz0oz| 久久久国内精品| 国产一区免费电影| 国产又粗又硬视频| 色综合久久久久综合体桃花网| 蜜桃久久一区二区三区| 久久精品视频在线播放| 成人国产激情在线| 欧美日韩一区二区三区在线视频| 伊人久久大香线蕉av超碰演员| 日日夜夜精品视频免费观看| 中文字幕中文在线不卡住| 亚洲高清视频免费观看| 亚洲乱码国产乱码精品精| av免费不卡国产观看| 国产精品日韩一区二区三区| 在线精品国产| 欧美精品 - 色网| 国产精品美女久久久久av爽李琼 | 日韩一级在线观看| 日本天堂在线观看| 国产精品稀缺呦系列在线| 亚洲人成网站77777在线观看| 国产美女主播在线播放| www.欧美日韩国产在线| 日韩av在线播| 亚洲第一页中文字幕| 不卡av免费观看| 不卡视频一区| 亚洲国产高清一区二区三区| 中文字幕第3页| 精品国产31久久久久久| 污污视频在线免费看| 8050国产精品久久久久久| 日韩a级大片| 久久久久狠狠高潮亚洲精品| 2021久久国产精品不只是精品| 国产精品白浆一区二小说| 精品av综合导航| 成年女人在线看片| 国精产品99永久一区一区| 美女黄色成人网| 国产综合精品在线| 欧美久久久久中文字幕| av在线影院| 国产视频不卡| 久久国产精品毛片| av免费播放网站| 777久久久精品| 免费污视频在线| 欧美大香线蕉线伊人久久| 老司机精品久久| 国产免费久久久久| 精品国产电影一区二区| 91精品论坛| 亚洲成年人专区| 成人综合激情网| 久久久精品视频网站| 精品国产欧美成人夜夜嗨| 99精品中文字幕在线不卡| 91精品91久久久中77777老牛| 国产欧美日韩不卡| 国产特黄一级片| 国产91精品久久久久| 日韩三级在线| 精品无码人妻少妇久久久久久| 色一情一乱一乱一91av| 老司机精品影院| 精品国产一区二区三区麻豆免费观看完整版 | 在线不卡a资源高清| 97在线超碰| 亚洲欧美久久久久一区二区三区| 国产乱码精品1区2区3区| 91浏览器在线观看| 久久伊人免费视频| 亚洲区小说区图片区qvod按摩| 亚洲三级在线观看视频| 欧美色欧美亚洲高清在线视频| 日本视频在线| 久久久福利视频| 精品一区二区三区在线观看| 亚州国产精品视频| 波霸ol色综合久久| 亚洲欧美成人vr| 欧美丰满熟妇bbb久久久| 欧美在线看片a免费观看| 国内高清免费在线视频| 亚洲欧美国产一区二区|