精品欧美一区二区三区在线观看 _久久久久国色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ù)棧公眾號

视频一区二区欧美| 黄色成人免费网| 国产一区999| 欧美国产亚洲视频| 粉嫩av懂色av蜜臀av分享| 色在线中文字幕| 国产精品视频你懂的| caoporen国产精品| 麻豆精品久久久久久久99蜜桃| 欧美一区二区三| 日韩欧美一二区| 日韩人妻精品无码一区二区三区| 午夜在线观看视频| 懂色av中文一区二区三区| 日韩免费在线观看视频| 草视频在线观看| 丝袜连裤袜欧美激情日韩| 欧美另类变人与禽xxxxx| 国产日韩欧美精品在线观看| 在线国产情侣| 久久综合色鬼综合色| 亚洲一区久久久| 一级一级黄色片| 亚洲青色在线| 久久视频精品在线| 性欧美精品中出| 麻豆精品av| 日韩欧美国产一区在线观看| 欧美成人黄色网址| 自拍偷拍亚洲视频| 亚洲影视资源网| 一级全黄肉体裸体全过程| 国产亚洲依依| 91麻豆swag| 国产精品视频入口| 成人h动漫精品一区二区无码| 看电视剧不卡顿的网站| 国产成人中文字幕| youjizz在线视频| 亚洲区欧美区| 欧美激情第三页| 青青草原在线免费观看视频| 爽成人777777婷婷| 在线播放精品一区二区三区 | 成人免费视频视频| 91色琪琪电影亚洲精品久久| 中国女人真人一级毛片| 亚洲欧美成人综合| 97香蕉久久超级碰碰高清版| 久久久久亚洲av无码专区| 亚洲老妇激情| 久久成年人视频| 中文乱码字幕高清一区二区| 久久美女视频| yw.139尤物在线精品视频| 少妇高潮一区二区三区喷水| 91综合视频| 精品国产一区久久久| 色哟哟一一国产精品| 91精品福利| 欧美xxxx综合视频| 久久久久人妻一区精品色欧美| 欧美一区二区三区另类| 九九久久精品一区| 精品少妇爆乳无码av无码专区| 国内久久视频| 4444欧美成人kkkk| 在线永久看片免费的视频| 日韩成人一区二区| 成人h视频在线观看播放| 99久久国产热无码精品免费| 国产成人一区在线| 精品国产乱码久久久久软件| 免费av在线电影| 中文字幕欧美国产| 99久久久无码国产精品性色戒| 97影院秋霞午夜在线观看| 亚洲愉拍自拍另类高清精品| 阿v天堂2017| 高清电影一区| 欧美日韩一区二区电影| 4438x全国最大成人| 欧美变态挠脚心| 有码中文亚洲精品| 欧美卡一卡二卡三| 亚洲伊人网站| 国产精品自拍偷拍视频| 国产夫绿帽单男3p精品视频| www.亚洲色图| 亚洲欧洲精品一区二区三区波多野1战4| 超碰人人在线| 午夜精品久久久久久久久| 天天操天天摸天天爽| 五月天色综合| 日韩精品在线免费| 91精品国自产在线| 亚洲精品人人| 国产乱肥老妇国产一区二| 亚洲精品福利网站| 国产女人18毛片水真多成人如厕| 国产 国语对白 露脸| 国产日韩另类视频一区| 欧美一二三在线| 亚洲v国产v欧美v久久久久久| 久久久久久久久久久妇女| 97国产精品视频| 一区二区三区午夜| www国产亚洲精品久久麻豆| 四虎免费在线观看视频| 性色av浪潮av| 亚洲男同gay网站| 色综合天天性综合| 亚洲av无码久久精品色欲| 欧美欧美黄在线二区| 欧美高清videos高潮hd| 中文字幕在线观看国产| 91色婷婷久久久久合中文| 成人在线观看www| 91精品国产66| 亚洲男女性事视频| 国产极品美女高潮无套嗷嗷叫酒店| 毛片av一区二区| 欧美激情导航| 91在线三级| 欧美成人video| 色哟哟一一国产精品| 日韩av一区二区三区| 久久久久久九九九九| 欧美6一10sex性hd| 宅男噜噜噜66一区二区66| 国产一区二区三区四区五区六区 | 日韩在线观看免费av| 国产成人无码一区二区在线播放| 成人免费三级在线| 久久亚洲a v| 欧洲一区在线| 久久人人爽亚洲精品天堂| 亚洲影视一区二区| 中文一区二区完整视频在线观看| 爱福利视频一区二区| 日韩mv欧美mv国产网站| 午夜精品久久久久久久久久久久| 蜜臀av免费在线观看| 亚洲黄网站在线观看| 网站在线你懂的| 亚洲成人一区| 亚洲一区二区免费| 青春草视频在线| 精品91自产拍在线观看一区| 国产亚洲第一页| 日本乱码一区二区三区不卡| 樱桃国产成人精品视频| 一区二区三区人妻| 欧美日韩国产一区精品一区| 亚洲一区二区三| 亚洲欧美成人影院| 精品国产伦一区二区三区观看方式| 青青草原免费观看| k8久久久一区二区三区| 免费一级特黄毛片| 亚洲第一福利专区| 国产99视频精品免视看7| 久久久久久久久亚洲精品| 欧美性生交大片免费| 亚洲AV无码成人精品区明星换面| 视频在线在亚洲| 亚洲国产精品www| 亚洲男人在线| 欧美激情免费视频| 天堂资源最新在线| 欧美在线综合视频| 久久免费看少妇高潮v片特黄 | 色综合久久88色综合天天免费| www.久久av| 蜜桃精品视频在线| 天堂av在线中文| 国产精品久久久久av蜜臀| 日韩av电影院| h片在线免费| 欧美精品一区二区三区很污很色的| 精品成人av一区二区在线播放| 国产欧美日韩一区二区三区在线观看| www.亚洲自拍| 免费欧美日韩| 美国av在线播放| 久久电影在线| 国产视频999| 国产不卡人人| 最近2019免费中文字幕视频三| 91网站免费观看| 黄色av免费观看| 色噜噜久久综合| 国产盗摄一区二区三区在线| 99国产精品久久久| xxww在线观看| 亚洲毛片播放| 在线播放 亚洲| 欧美午夜18电影| 成人激情在线观看| 小早川怜子影音先锋在线观看| 色阁综合伊人av| 日韩一区av| 欧美大片免费久久精品三p| 亚洲 欧美 中文字幕| 一区二区三区中文字幕精品精品| 中文字幕第4页| 成人激情视频网站| 怡红院亚洲色图| 香蕉精品999视频一区二区 | 久cao在线| 亚洲欧美另类自拍| 国模私拍视频在线| 91精品国产综合久久小美女| 四虎影院在线免费播放| 亚洲成av人影院在线观看网| 午夜三级在线观看| 国产拍欧美日韩视频二区| 精品久久久久一区二区| 国产综合色视频| 激情 小说 亚洲 图片: 伦| 国产日韩高清一区二区三区在线| 99久热在线精品视频| 日韩在线观看电影完整版高清免费悬疑悬疑| 精品国产一区二区三区四区vr| 精品一区二区三区免费看| 国产美女高潮久久白浆| 亚洲欧美在线成人| 日本亚洲欧洲色α| 手机在线观看av网站| 欧美精品videos| 少妇视频在线| 欧美超级乱淫片喷水| 黄色成人在线观看| 日韩在线观看你懂的| yw在线观看| 国产香蕉一区二区三区在线视频| 免费资源在线观看| 亚洲精品一区中文字幕乱码| 头脑特工队2在线播放| 日韩大陆毛片av| 五月婷婷伊人网| 亚洲国产精彩中文乱码av在线播放| 国产高清视频免费| 日韩视频免费观看高清完整版在线观看 | 亚洲一区二区三区香蕉 | 7777精品伊人久久久大香线蕉的| 中文字幕网址在线| 欧美色视频一区| 91亚洲国产成人精品一区| 欧美美女一区二区在线观看| 亚洲一区中文字幕永久在线| 欧美日韩免费高清一区色橹橹 | 男人操女人的视频网站| 亚洲免费观看高清| 麻豆亚洲av成人无码久久精品| 亚洲愉拍自拍另类高清精品| 日本三级午夜理伦三级三| 午夜精品123| 亚洲国产av一区二区三区| 欧美四级电影网| 91国内精品久久久| 欧美成人精品二区三区99精品| 狠狠综合久久av一区二区| 日韩av资源在线播放| 国产51人人成人人人人爽色哟哟| 这里只有精品丝袜| 国产在线激情| 欧美放荡办公室videos4k| 大桥未久在线视频| 国产精品999| 2020国产精品小视频| 97视频资源在线观看| 丝袜连裤袜欧美激情日韩| 亚洲国产一区二区三区在线| 亚洲午夜精品一区二区国产| 日本久久久网站| 老鸭窝毛片一区二区三区| 一区二区三区免费播放| 国产精品一区二区免费不卡| 国产精品无码专区| 国产精品网站一区| 国产在线拍揄自揄拍| 91精品办公室少妇高潮对白| 国产露脸国语对白在线| 精品少妇一区二区三区| 青青青手机在线视频观看| 久久精品国产一区| 免费看男女www网站入口在线| 国产精品99久久久久久www| 国产精品视频一区二区三区综合| 国产精品污www一区二区三区| 国内精品久久久久久99蜜桃| 日本中文字幕一级片| 久久久久久久高潮| 一级黄色大片免费看| 国产亚洲精品精华液| 久久亚洲av午夜福利精品一区| 欧美午夜精品久久久久久人妖| 国产又黄又大又粗的视频| 日韩精品视频在线观看网址| 麻豆网站在线| 97香蕉久久夜色精品国产| 国产精品久久久久久av公交车| 久久综合精品一区| 午夜精品网站| 污视频免费在线观看网站| 成人精品在线视频观看| 情侣偷拍对白清晰饥渴难耐| 欧美性猛交xxxx偷拍洗澡| 亚洲精品国偷拍自产在线观看蜜桃| 一区二区三区精品99久久 | 国产精品久久久久久一区二区| 懂色av一区二区| 四虎精品欧美一区二区免费| 人禽交欧美网站| 欧美图片一区二区| 亚洲韩国精品一区| 99精品免费观看| 色偷偷88888欧美精品久久久| 欧美aa视频| 国内成+人亚洲| 亚洲天堂偷拍| 能看毛片的网站| 国产精品国产馆在线真实露脸| 天天干天天色综合| 日韩毛片中文字幕| 草草在线视频| 国产视频99| 亚洲大胆在线| 无码国产69精品久久久久网站| 亚洲黄一区二区三区| 国产黄色av网站| 久久国产精品亚洲| 国产激情精品一区二区三区| 亚洲成人自拍| 免费av成人在线| 成人欧美一区二区三区黑人一| 欧美在线视频不卡| 国产永久免费高清在线观看视频| 日本欧美在线视频| 国产99亚洲| 超碰av在线免费观看| 中文字幕国产一区| 在线观看视频中文字幕| 在线观看视频99| 青草综合视频| 天堂av免费看| 国产成人av资源| 国产一级一级片| 亚洲国产欧美一区二区三区同亚洲| hd国产人妖ts另类视频| 狠狠色综合一区二区| 国产精品资源| 国产又黄又粗视频| 欧美午夜免费电影| 麻豆网站在线观看| 福利视频久久| 国产一区91| 老熟妇一区二区| 91精品免费观看| 9lporm自拍视频区在线| 美女三级99| 蜜臀av一区二区在线观看| av激情在线观看| 亚洲第一天堂无码专区| 亚洲一二三四| 亚洲免费精品视频| 国产成人综合在线| 日韩免费视频网站| 亚洲日本中文字幕| www.久久久.com| 妞干网在线观看视频| 国产无一区二区| 国产精品天天操| 69**夜色精品国产69乱| 成人在线电影在线观看视频| 波多野结衣免费观看| 午夜精品影院在线观看| 国产日本在线| 97超碰在线播放| 午夜宅男久久久| 久久免费看少妇高潮v片特黄| 日韩av在线网页| 91精品一久久香蕉国产线看观看| 国产高清www| 中文字幕+乱码+中文字幕一区| av综合在线观看| 日本免费在线精品| 欧美日韩hd| 久久美女免费视频| 日韩精品一区二| 日本欧美韩国| 国产一区二区四区| 中文字幕乱码日本亚洲一区二区 | 99久久精品国产精品久久| 成人黄色免费网| 97视频com| 五月激情久久久| 三级网站在线免费观看| 日韩精品中文字幕一区二区三区 |