PostgreSQL一線生產經驗分享:我討厭PostgreSQL的10件事 | Rick Branson

banq發表於2020-04-26

在過去的幾年中,軟體開發社群對流行的開源關聯式資料庫的熱愛已經達到了一個高潮。Hacker News主題有一個標題為“ PostgreSQL是世界上最好的資料庫”的文章,它的字裡行間充斥著討人喜歡的正能量,並無條件地讚美它。

雖然這種稱讚很多是當之無愧的,但缺乏有意義的異議卻讓我有些煩惱。沒有軟體是完美的,那麼PostgreSQL的缺陷到底是什麼?

自2003年以來,我就一直在生產中使用PostgreSQL,其部署範圍從小(千兆位元組)到中等(到PB級)。多年來,我經歷了一些痛苦的生產問題獲得了有關PostgreSQL特殊特性的第一手經驗。

#1:災難性的XID解決方案

這裡可以閱讀更多詳細情況。這裡我只想說,這一個可以啃硬骨頭經歷。有許多關於此問題導致多天停機的故事。可以用Google搜尋它,您會發現許多可憐的人敘述他們踏到這枚地雷的時間經歷。

#2:故障轉移可能會丟失資料

如果活躍的主伺服器突然出現故障,那麼執行中的流複製設定幾乎肯定會丟失已提交的資料。有人可能會說:“ 非同步複製的代價就是這樣。”但不一定非要這樣。PostgreSQL支援具有法定仲裁提交的同步複製,以實現容錯的永續性,但是它具有[url=https://www.postgresql.org/docs/current/warm-standby.html#SYNCHRONOUS-REPLICATION-PERFORMANCE]嚴格的效能範圍[/url],使應用程式複雜化。

等待不佔用系統資源,但是事務鎖將繼續保留,直到確認傳輸為止。結果,由於響應時間增加和爭用增加,謹慎使用同步複製將降低資料庫應用程式的效能。

這種固定的仲裁複製在某些情況下很有用,但我不建議在通用用例中推薦它。它類似於Kafka的ISR複製,具有acks = all和一個最小仲裁數問題,但是可執行任意查詢的事務性關聯式資料庫中差別細微。我目前尚不知道可以成功應用仲裁提交來應對非平常的規模實現高可用性,高耐用性(高可靠性)的複製。如果有,請聯絡!

就關聯式資料庫而言,Galera Cluster的組複製也不完美,但更接近理想狀態。他們甚至鼓勵按地理分佈的複製,這對於使用仲裁提交的PostgreSQL複製設定很可能是災難性的。

#3:低效率的複製會傳播中斷失敗

到目前為止,流複製是生產部署中最常用的複製機制。它是物理複製的一種形式,這意味著它可以複製磁碟二進位制資料本身中的更改。

每次需要通過寫操作修改磁碟上的資料庫頁面(8KB)時,即使只是一個位元組,也需要因為更改請求編輯的整個頁面的副本寫入預寫日誌(WAL)。物理流複製利用此現有的WAL基礎結構作為流到副本的更改日誌。

更新:有些人指出PostgreSQL僅需要在每個WAL檢查點執行一次全頁寫操作。的確如此,但是在大多數實際系統中,大多數寫入將遵循冪律分佈,最終出現在檢查點之間的唯一頁面上。但是,更重要的是:在預測系統行為時,正確的方法是假設情況更為昂貴,尤其是如果它取決於應用程式的難以預測和高度動態的行為時。

例如,使用物理複製,大型索引構建會建立大量WAL條目,從而很容易成為複製流的瓶頸。在頁面粒度的讀-修改-複製過程中,主伺服器可能會將硬體引起資料損壞更容易傳播到副本複製伺服器,我已經在生產中親眼目睹了好幾次。

這與邏輯複製相反,後者僅複製邏輯資料的更改。至少從理論上講,大型索引構建只會導致在網路上覆制單個命令。儘管PostgreSQL支援邏輯複製已有相當長的一段時間了,但是大多數部署都使用物理流複製,因為它更健壯,支援範圍更廣並且更易於使用。

#4:MVCC垃圾頻發

與大多數主流資料庫一樣,PostgreSQL使用多版本併發控制(MVCC)來實現併發事務。但是,它的特定實現常常會給垃圾行的版本化及其清理(VACUUM)帶來操作上的麻煩。一般而言,UPDATE操作會建立任何已修改行的新副本(或“行版本”),並將舊版本保留在磁碟上,直到可以清除它們為止。

多年來,這種情況一直在穩步改善,但它是一個複雜的系統,對於任何初次接觸該問題的人來說都是一個黑匣子。例如,瞭解純堆元組(HOT)及其何時啟動對於繁重的就地更新工作負載(如連續保持一致的計數器列)來說可能事關成敗的。預設的autovacuum設定在大多數情況下都起作用,但是如果不起作用,則good lord(燒香拜佛吧)。

相反,MySQL和Oracle使用重做和撤消日誌。他們不需要類似的後臺垃圾收集過程。他們做出的權衡主要是事務提交和回滾操作的額外延遲。

將來的拯救者解決方案:zheap saves us all.

#5:每次連線處理=在大規模上很痛苦

PostgreSQL為每個連線派生一個程式,因為大多數其他資料庫都使用更有效的連線併發模型。由於存在一個相對較低的閾值,在該閾值上新增更多的連線會降低效能(約2個核心),最終會導致效能下降,而這又是一個較高的閾值(難以估計,高度依賴工作負載),這將導致調優困難。

使用連線池的標準方法當然可以解決問題,但是會帶來額外的架構複雜性。在一個特別大的部署中,我最終不得不在第二個 pgbouncer層中分層。一層在應用程式伺服器上執行,另一層在資料庫伺服器上執行。它總共聚合了大約一百萬個客戶端程式的連線。調過程有40%屬於藝術性,40%用蠻力和10%的純正運氣。

程式可伸縮性在每個主要版本中都在逐步提高,但與MySQL中使用的“每連線執行緒數”相比,最終該體系結構的效能受到了一定的限制。

有關更多技術深度,請參見https://brandur.org/postgres-connections

#6:主鍵索引是“Space hog”(貪婪耗費空間)

PostgreSQL中的表有一個主鍵索引和稱為堆的獨立行儲存。其他資料庫將它們整合在一起或支援“索引組織表”。在這種安排下,主鍵查詢過程直接導致行資料,而無需輔助獲取完整行以及必需的額外CPU和I / O利用率。

PostgreSQL中的CLUSTER命令根據索引重新組織表以提高效能,但實際上不適用於大多數實際OLTP情況。它以互斥鎖重寫整個表,從而阻止任何讀取或寫入。PostgreSQL不維護新資料的叢集佈局,因此該操作必須定期執行。因此,僅當您可以使資料庫長期長時間離線時,它才真正 有用。

但是更關鍵的是,索引組織的表可以節省空間,因為索引不需要單獨的行資料副本。對於具有主要由主鍵覆蓋的小行的表(例如聯接表),這可以輕鬆地將表的儲存空間減少一半。

考慮下表,該表儲存任意物件的社交“贊”:

CREATE TABLE likes (
 object_type INTEGER NOT NULL,
 object_id BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY,
 user_id BIGINT NOT NULL,
 created_at TIMESTAMP WITH TIME ZONE NOT NULL,
 PRIMARY KEY(object_type, object_id, user_id)
);

PostgreSQL將維護與基表儲存區分開的主鍵索引。該指數將包含的完整副本object_type,object_id以及user_id列的每一行。每行28個位元組中的20個(〜70%)將被複制。如果PostgreSQL支援索引組織的表,它不會消耗所有這些額外的空間。

#7:主要版本升級可能需要停機

一些主要版本升級需要數小時的停機時間才能轉換大型資料庫的資料。使用典型的流複製機制,無法通過升級副本並執行故障轉移來優雅地做到這一點。磁碟二進位制格式在主要版本之間不相容,因此,主副本之間的有線協議實際上也是不相容的。

希望邏輯複製最終將完全取代流複製,這將啟用線上滾動升級策略。當我進行大規模水平部署時,我們在自定義基礎架構上進行了重大工程投資,以使用額外的基於觸發器的複製系統(也用於分片遷移)在不停機的情況下進行這些升級。

#8:有點繁瑣的複製設定

公平地說,MySQL的即用型複製要麻煩得多,但與某些NoSQL儲存(如MongoDB和Redis)或某些面向叢集的複製系統(如MySQL Group ReplicationGalera Cluster)相比,存在易用性和sharp-edge-avoidance等問題,PostgreSQL中設定複製留下了很多不理想的後果。雖然邏輯複製從理論上為第三方解決方案提供了更大的靈活性,以彌補這些空白,但到目前為止,使用它代替流複製存在一些很多的警告

#9:沒有計劃提示是荒謬的教條

計劃者提示使查詢能夠指示查詢計劃者使用原本不會使用的策略。PostgreSQL開發團隊多年來一直拒絕支援查詢計劃程式提示,這似乎是一種足夠聰明的編譯器引數的形式。

我確實瞭解他們的理由,這主要是關於防止使用者使用應通過編寫適當查詢而解決的查詢提示來攻擊問題。但是,當您看到生產資料庫在突然而意外的查詢計劃變更下激增至完全崩潰時,這種哲學似乎是殘酷的家長式作風。

在許多情況下,給計劃者的提示可以在幾分鐘內緩解問題,併為工程團隊提供他們為查詢進行適當修復所需的時間或幾天。雖然是涉及禁用某些查詢規劃戰略,一些間接的解決方法,他們是有風險的,絕對不應該任何時間壓力下使用。

#10:無塊壓縮

InnoDB在MySQL中的頁面壓縮通常可以將儲存空間減少一半,並且從效能角度來看幾乎是“免費的”。PostgreSQL將自動壓縮較大的值,但這對於將資料儲存在關聯式資料庫中的最常用的方式沒有用。對於大多數RDBMS用例,一行通常為幾百個位元組或更少,這意味著壓縮僅在跨多行或成塊應用時才真正有效。

對於PostgreSQL核心的資料結構來說,塊壓縮確實很難實現,但是儘管有一些缺點 MySQL InnoDB儲存引擎採用的“打孔”策略在實踐中似乎效果很好。

2020年4月7日更新:“ MySQL在Facebook上”一舉成名的Mark Callaghan在此質疑我的說法,即打孔壓縮hole-punching在實踐中“相當不錯”。事實證明,正如我之前認為的那樣,世界上最大的MySQL安裝從未使用過打孔壓縮。他們確實成功地使用了較舊版本的InnoDB壓縮的成功,但是在幾年前遷移到MyRocks之前。

雖然打孔壓縮對某些人似乎確實有效,但是有些注意事項。如果您正在執行Percona的MySQL版本,那麼MyRocks是更好的選擇。如果不是這樣,對於快閃記憶體中非常繁重的讀取工作負載,經典的InnoDB表壓縮似乎是一個更安全的選擇。馬克沒有指出主要生產問題的任何特定情況,但指出他“懷疑檔案系統是為每頁打孔而設計的,我會擔心隱晦的故障。”

在PostgreSQL世界中廣泛使用的通用塊壓縮也用在leverages ZFS中,它似乎對人們來說確實很好用。ZFS如今已成為Linux上的生產級現實,但無疑帶來了一些管理上的開銷,而這些開銷在XFS或ext4等更“現成的開箱即用”檔案系卻不存在。

總結...

您可能應該仍然使用PostgreSQL,通常,我建議從PostgreSQL開始,然後嘗試弄清楚為什麼它不適用於您的用例。

PostgreSQL非常成熟,設計精良,功能豐富,通常沒有鋒利的邊緣,並且在絕大多數用例中都表現出色。它也不受主要公司贊助商的約束,包括出色的文件資料,並擁有一個專業的,包容的社群。

好訊息是,可以通過使用託管資料庫服務(例如Heroku PostgreSQLCompose PostgreSQL用於PostgreSQL的Amazon RDS用於PostgreSQL的[url=https://cloud.google.com/sql]Google Cloud SQL)[/url]來減輕或消除由本文中提到的許多問題引起的痛苦。如果您可以使用其中一項服務,為了愛所有神聖的東西,請這樣做!

我很自豪地說,我已經在PostgreSQL的基礎上構建了將近20年的軟體,儘管存在缺陷,但我仍然是堅定的擁護者。鑑於我多年來由其令人難以置信的開發團隊所見證的進步,我可以說,大多數(如果不是全部)這些問題將在適當的時候得到解決。

 

相關文章