本文是對兩大開源關係型資料庫MySQL、PostgreSQL做了詳細的對比,歡迎大家在評論區發表自己的見解。
在這些版本之前,人們普遍認為,Postgres 在功能集表現更出色,也因其“學院派”風格而備受稱贊,MySQL 則更善長大規模併發讀/寫。
但是隨著它們最新版本的釋出,兩者之間的差距明顯變小了。
特性比較
讓我們來看看我們都喜歡談論的“時髦”功能。
特性 | MySQL 8 | PostgreSQL 10 |
---|---|---|
查詢 & 分析 | ||
公用表運算式 (CTEs) | ✔ New | ✔ |
視窗函式 | ✔ New | ✔ |
資料型別 | ||
JSON 支援 | ✔ Improved | ✔ |
GIS / SRS | ✔ Improved | ✔ |
全文檢索 | ✔ | ✔ |
可擴充套件性 | ||
邏輯複製 | ✔ | ✔ New |
半同步複製 | ✔ | ✔ New |
宣告式分割槽 | ✔ | ✔ New |
過去經常會說 MySQL 最適合在線事務,PostgreSQL 最適合分析流程。但現在不是了。
公共表運算式(CTEs) 和視窗函式是選擇 PostgreSQL 的主要原因。但是現在,透過取用同一個表中的 boss_id 來遞迴地遍歷一張僱員表,或者在一個排序的結果中找到一個中值(或 50%),這在 MySQL 上不再是問題。
在 PostgreSQL 中進行複製缺乏配置靈活性,這就是 Uber 轉向 MySQL 的原因。但是現在,有了邏輯複製特性,就可以透過建立一個新版本的 Postgres 並切換到它來實現零停機升級。在一個巨大的時間序列事件表中截斷一個陳舊的分割槽也要容易得多。
就特性而言,這兩個資料庫現在都是一致的。
有哪些不同之處呢?
現在,我們只剩下一個問題 —— 那麼,選擇一個而不選另一個的原因是什麼呢?
生態系統是其中一個因素。MySQL 有一個充滿活力的生態系統,包括 MariaDB、Percona、Galera 等等,以及除 InnoDB 以外的儲存引擎,但這也可能是和令人困惑的。Postgres 的高階選擇有限,但隨著最新版本引入的新功能,這會有所改變。
治理是另一個因素。當 Oracle(或最初的 SUN)收購 MySQL時,每個人都擔心他們會毀掉這個產品,但在過去的十年裡,這並不是事實。事實上,在收購之後,發展反倒加速了。而 Postgres 在工作管理和協作社群方面有著豐富的經驗。
基礎架構不會經常改變,雖然近來沒有對這方面的詳細討論,這也是值得再次考慮的。
來複習下:
特性 | MySQL 8 | PostgreSQL 10 |
---|---|---|
架構 | 單行程 | 多行程 |
併發 | 多執行緒 | fork(2) |
表結構 | 聚簇索引 | 堆 |
頁壓縮 | Transparent | TOAST |
更新 | In-Place / Rollback Segments | Append Only / HOT |
垃圾回收 | 清除執行緒 | 自動清空行程 |
事務日誌 | REDO Log (WAL) | WAL |
複製日誌 | Separate (Binlog) | WAL |
行程vs執行緒
當 Postgres 派生出一個子行程來建立連線時,每個連線最多可以佔用 10MB。與 MySQL 的執行緒連線模型相比,它的記憶體壓力更大,在 64 位平臺上,執行緒的預設堆疊大小為 256KB。(當然,執行緒本地排序緩衝區等使這種開銷變得不那麼重要,即使在不可以忽略的情況下,仍然如此。)
儘管“寫時複製”儲存了一些與父行程共享的、不可變的記憶體狀態,但是當您有 1000 多個併發連線時,基於流程的架構的基本開銷是很繁重的,而且它可能是容量規劃的最重要的因素之一。
也就是說,如果你在 30 臺伺服器上執行一個 Rails 應用,每個伺服器都有 16 個 CPU 核心 32 執行緒,那麼你有 960 個連線。可能只有不到 0.1% 的應用會超出這個範圍,但這是需要記住的。
聚簇索引 vs 堆表
聚簇索引是一種表結構,其中的行直接嵌入其主鍵的 b 樹結構中。一個(非聚集)堆是一個常規的表結構,它與索引分別填充資料行。
有了聚簇索引,當您透過主鍵查詢記錄時,單次 I/O 就可以檢索到整行,而非叢集則總是需要查詢取用,至少需要兩次 I/O。由於外來鍵取用和 JOIN 將觸發主鍵查詢,所以影響可能非常大,這將導致大量查詢。
聚簇索引的一個理論上的缺點是,當您使用二級索引進行查詢時,它需要遍歷兩倍的樹節點,第一次掃描二級索引,然後遍歷聚集索引,這也是一棵樹。
但是,如果按照現代表設計的約定,將一個自動增量整數作為主鍵[1]——它被稱為代理鍵——那麼擁有一個 聚集索引幾乎總是可取的。更重要的是,如果您做了大量的 ORDER BY id 來檢索最近的(或最老的)N 個記錄的操作,我認為這是很適用的。
Postgres 不支援聚集索引,而 MySQL(InnoDB)不支援堆。但不管怎樣,如果你有大量的記憶體,差別應該是很小的。
頁結構和壓縮
Postgres 和 MySQL 都有基於頁面的物理儲存。(8KB vs 16KB)
PostgreSQL物理儲存的介紹
頁結構看起來就像右邊的圖。它包含一些我們不打算在這裡討論的條目,但是它們包含關於頁的元資料。條目後面的項是一個陣列識別符號,由指向元組或資料行的(偏移、長度)對組成。在 Postgres 中,相同記錄的多個版本可以以這種方式儲存在同一頁面中。
MySQL 的表空間結構與 Oracle 相似,它有多個層次,包括層、區段、頁面和行層。
此外,它還有一個用於撤銷的單獨段,稱為“回滾段”。與 Postgres 不同的是,MySQL 將在一個單獨的區域中儲存同一記錄的多個版本。
如果存在一行必須適合兩個資料庫的單個頁面,,這意味著一行必須小於 8KB。(至少有 2 行必須適合 MySQL 的頁面,恰巧是 16KB/2 = 8KB)
那麼當你在一個列中有一個大型 JSON 物件時會發生什麼呢?
Postgres 使用 TOAST,這是一個專用的影子表(shadow table)儲存。當行和列被選中時,大型物件就會被拉出。換句話說,大量的黑盒不會汙染你寶貴的快取。它還支援對 TOAST 物件的壓縮。
MySQL 有一個更複雜的特性,叫做透明頁壓縮,這要歸功於高階 SSD 儲存供應商 Fusio-io 的貢獻。它設計目的是為了更好地使用 SSD,在 SSD 中,寫入量與裝置的壽命直接相關。
對 MySQL 的壓縮不僅適用於頁面外的大型物件,而且適用於所有頁面。它透過在稀疏檔案中使用打孔來實現這一點,這是被 ext4 或 btrfs 等現代檔案系統支援的。
有關更多細節,請參見:在 FusionIO 上使用新 MariaDB 頁壓縮獲得顯著的效能提升。
更新的開銷
另一個經常被忽略的特性,但是對效能有很大的影響,並且可能是最具爭議的話題,是更新。
這也是Uber放棄Postgres的另一個原因,這激起了許多Postgres的支持者來反駁它。
-
MySQL 對Uber可能是合適的, 但是未必對你合適
-
一篇PostgreSQL對Uber的回應 (PDF)
兩者都是MVCC資料庫,它們可以隔離多個版本的資料。
為了做到這一點,Postgres將舊資料儲存在堆中,直到被清空,而MySQL將舊資料移動到一個名為回滾段的單獨區域。
在Postgres中,當您嘗試更新時,整個行必須被覆制,以及指向它的索引條目也被覆制。這在一定程度上是因為Postgres不支援聚集索引,所以從索引中取用的一行的物理位置不是由邏輯鍵抽象出來的。
為瞭解決這個問題,Postgres使用了堆上元組(HOT),在可能的情況下不更新索引。但是,如果更新足夠頻繁(或者如果一個元組比較大),元組的歷史可以很容易地超過8 KB的頁面大小,跨越多個頁面並限制該特性的有效性。修剪和/或碎片整理的時間取決於啟髮式解決方案。另外,設定不超過100的填充引數會降低空間效率——這是一種很難在建立表時考慮的折衷方案。
這種限制更深入; 因為索引元組沒有關於事務的任何資訊,所以直到9.2之前一直不能支援僅索引掃描。 它是所有主要資料庫(包括MySQL,Oracle,IBM DB2和Microsoft SQL Server)支援的最古老,最重要的最佳化方法之一。 但即使使用最新版本,當有許多UPDATE在可見性對映中設定臟位時,Postgres也不能完全支援僅索引掃描,並且在我們不需要時經常選擇Seq掃描。
在MySQL上,更新發生在原地,舊的行資料被封存在一個稱為回滾段的獨立區域中。 結果是你不需要VACUUM,並且提交非常快,而回滾相對較慢,這對於大多數用例來說是一個可取的折衷。
它也足夠聰明,儘快清除歷史。 如果事務的隔離級別設定為READ-COMMITTED或更低,則在陳述句完成時清除歷史記錄。
事務記錄的大小不會影響主頁面。 碎片化是一個偽命題。 因此,在MySQL上能更好,更可預測整體效能。
Garbage Collection 垃圾回收
在Postgres中VACUUM上開銷很高,因為它在主要工作在堆區,造成了直接的資源競爭。它感覺就像是程式語言中的垃圾回收 – 它會擋在路上,並隨時讓你停下來。
為具有數十億記錄的表配置autovacuum仍然是一項挑戰。
在MySQL上清除(Purge)也可能相當繁重,但由於它是在單獨的回滾段中使用專用執行緒執行的,因此它不會以任何方式影響讀取的併發性。即使使用 預設配置,變膨脹的回滾段使你執行速度減慢的可能性也是很低的。
擁有數十億記錄的繁忙表不會導致MySQL上的歷史資料膨脹,諸如儲存上的檔案大小和查詢效能等事情上幾乎是可以預測的並且很穩定。
日誌與副本
Postgres 擁有被稱作 預寫日誌 (WAL)的單信源事務歷史。它一直被用於副本,並且稱為邏輯複製的新功能可將二進位制內容快速解碼為更易消化的邏輯陳述句,從而可對資料進行細粒度控制。
MySQL維護兩個單獨的日誌:1.用於崩潰恢復的InnoDB特定的重做日誌,以及 2. 用於複製和增量備份的二進位制日誌。
InnoDB 上的重做日誌與 Oracle 一致,它是一個免維護的迴圈緩衝區,不會隨著時間的推移而增長,只在啟動時以固定大小建立。 這種設計保證在物理裝置上保留一個連續的連續區域,從而提高效能。 更大的重做日誌產生更高的效能,但要以崩潰恢復時間為代價。
隨著新的複製功能新增到Postgres,我覺得他們不分伯仲。
總結
令人驚訝的是,它證明瞭普遍的觀點依然存在;MySQL最適合在線交易,而PostgreSQL最適合僅用於append only樣式,像資料倉庫一樣分析過程。
正如我們在這篇文章中看到的,Postgres的絕大多數難題都來自於append only樣式,過於冗餘的堆結構。
Postgres的未來版本可能需要對其儲存引擎進行重大改進。您不必為接受我說的——實際上在官方wiki上已經有對它的討論,這表明現在是時候從InnoDB身上學回來一些好的想法了。
人們一次又一次的說MySQL正在追趕Postgres,但是這一次,潮流已經改變。
-
UUID作為主鍵是一個可怕的想法,順便說一句——密碼隨機性完全是為了殺死取用的區域性性而設計,因此效能會損失。↩︎
-
當我說Postgres特別適合分析時,我是認真的:萬一你不知道TimescaleDB,它是PostgreSQL上邊的一個封裝,允許你每秒插入100萬條資料,每臺伺服器又1000億行。多麼瘋狂的事情。難怪Amazon會選擇PostgreSQL作為Redshift的基礎。
朋友會在“發現-看一看”看到你“在看”的內容