早在 2019 年,老馮就在《》提到過 —— 不要在生產環境用容器運行 PostgreSQL 數據庫,因為你有極大概率會遇上一堆在物理機/虛擬機上根本不存在的麻煩與問題。
這不,最近用 Docker “官方” 的 Postgres 鏡像的用戶在升級的時候就踩雷了。 昨天 PostgreSQL 社區的老法師 Gwen Shapira 在 X 發了個帖子吐槽了這個事。
![]()
??重要提醒:不要在生產環境用 Docker 官方的 Postgres 鏡像。 如果非要用,請務必顯式指定 Debian 基礎鏡像版本。 PostgreSQL 的小版本升級(比如 17.6 → 17.7)通常是 安全、簡單、推薦 的,理論上 絕不會破壞任何東西。 但是 ,如果你使用的是 Docker 官方鏡像,并在最近(8 月以來)做過小版本升級,你可能見過這樣的警告:
“數據庫創建時使用的排序規則版本為 2.36,但當前操作系統提供的版本是 2.41。請重建所有使用默認排序規則的對象,并執行 ALTER DATABASE "mydb" REFRESH COLLATION VERSION,或使用正確版本的庫重新構建 PostgreSQL。” 為什么會這樣?原因其實很簡單、也很離譜: 1.Docker 官方 PG 鏡像只支持 兩個 Debian 版本2.當Debian發布新版本時,只要你沒明確指定debian版本標簽,它會 自動變成新的默認基礎鏡像3.新的 Debian 版本用了 新版本的 glibc4.glibc 更新后,locale(排序規則)文件發生變化
于是你現在的狀態變成: ?運行的 PostgreSQL 鏈接的是一套 locale 文件?而數據庫里的數據與索引 是基于另一套舊的 locale 文件生成的 PostgreSQL 很清楚這種混用會導致: ?查詢結果錯誤?排序錯誤?更嚴重時甚至會觸發 數據損壞 因此它才會要求你: ?重建所有受影響的對象?再執行 ALTER DATABASE ... REFRESH COLLATION VERSION
而這一套操作本來只有在 大版本升級 才需要做,誰都不會想到 一個小版本升級 居然要你重建整個數據庫。 結果是:Docker 官方鏡像強行把這東西甩到用戶臉上:小版本升級也可能觸發 glibc/locale 變化。 小心!官方鏡像并不意味著 “負責任的生產環境表現”
想象一下,你用著 Docker 提供的 “官方” postgres 鏡像,然后趕上這周的 PostgreSQL 最新小版本發布 —— 于是準備升級一個小版本。 PG 小版本升級難道不是很安全,很簡單的嗎?只要重新 pull 一下 latest 鏡像(我猜相當一部分人是這么干的), 另外一部分稍微講究一點的用戶大概會使用 (17.6 -> 17.7)這樣的方式來拉取最新鏡像。如果是這樣,那就完犢子了!
除非你使用的鏡像 Tag ,嚴格包含了 Debian 版本號,也就是 17.6-bookworm 這樣的版本號,否則在最近的小版本更新中實際 隱含著一次 Linux 操作系統大版本升級。 你以為自己是從 17.6 升級到 17.7 ,但實際上還一起把底下的操作系統從 Debian 12 升級到了 13!而這種計劃外的原地升級會導致你的數據庫索引原地報廢!(或者更多!)
到底是怎么回事
Docker 官方提供的 PostgreSQL 鏡像主要基于 Debian 系統鏡像(也提供 Alpine 版本,只不過基本都用 debian 的)。 維護者指出這些鏡像 同時只支持兩個 Debian 發行版,當新的 Debian 穩定版發布時,就會升級基礎鏡像到新版本并停止對最舊版本的支持。
最近不是 Debian 13 trixie 剛發布了嘛,于是 Docker 官方把 postgres 這個鏡像升級了一下,底層的 debian 系統鏡像從 12 bookworm 升級到了 13 trixie。 結果底層 C 函數庫 (glibc) 版本的出現躍遷 —— Debian 13的 glibc 版本從 12 的 2.36 升級到了 2.41,而在這兩個 glibc 版本中,排序規則發生了變化,這就壞事了。
![]()
因為數據庫索引的核心 —— 排序,是由排序規則定義的,而排序規則并非是一成不變的。 每當排序規則出現變化時,使用舊版本排序規則的數據庫集群就需要重建 —— 至少是重建索引,否則的話就有可能出現 數據損壞。 生產環境的嚴肅數據庫哪有不用索引的,結果就是至少在全庫重建索引之前 —— “原地索引報廢”,數據庫性能雪崩。 最壞的情況下,還可能影響數據庫約束,數據一致性,分區表的行為等等。
這個失誤的影響范圍會很大,在 DockerHub 上, postgres 鏡像是下載量最大的鏡像之一 —— 下載量已經超過十億次,最近一周 pull 大約一千七百萬次。 很多用戶都是 docker pull postgres 一把梭的,就算指定了 17.6 這樣的 PG 版本號, 只要沒指定 Debian 版本號,也照樣會翻車。
緊急應對措施
對于在生產環境中使用所謂 Docker 官方 "postgres" 容器的朋友,老馮的建議是,盡早把你的容器版本切換為鎖定 PG + Debian 版本號的鏡像(比如 17.6-bookworm) ,這件事至少要在下次小版本升級 / 或者是重新 Pull 之前完成。在進行升級的時候,也務必使用諸如 17.7-bookworm 這樣的版本號。
另外,也不要妄想原地從 17.7-bookworm 直接飛升到 17.7-trixie。 任何涉及到 Glibc (Linux 發行版大版本) 的變動,標準 SOP 都是要邏輯遷移的 —— 要么通過邏輯復制藍綠部署在線遷移,要么 pg_dump 邏輯轉儲。 除非你已經是聰明的 PG 老司機 —— 在初始化集群的時候就聰明的顯式指定并選擇了 PG built-in locale provider with C/C-UTF8[1]。
當然從長期來看,最好還是遷移到物理機/虛擬機上的數據庫部署方案更穩妥。 這一點老馮在《》以及 《》 就已經展開過了 —— 越復雜的架構雜耍,翻車的時候摔的就越痛!
如果你非要用容器不可的話,老馮的建議也是,找一個好點兒的三方 Docker Postgres 鏡像,也比 “官方” 的這個土鱉鏡像要好得多。
為什么排序規則很重要
那么,為什么會出現這個問題呢?老馮在《PG中的本地化排序規則[2]》就深入聊過這個問題。 簡單的結論就是你應該始終使用 C.UTF-8 作為全局排序規則,同時在 PostgreSQL 17 之后的版本則應該強制使用 PG 內置的 locale provider,而不是使用操作系統 glibc 的排序規則。 真的要用到特定 Locale 規則的時候(什么漢語拼音排序之類的),直接在 DDL / SQL 里面顯式聲明就可以,不影響使用的 —— 用 ICU 排序規則,不要使用操作系統的!
這里的原因是,(至少在 PG 17 之前)PostgreSQL 強依賴操作系統的本地化庫 來執行字符串比較排序, 這是 glibc 提供的一個核心功能,而 glibc 中排序規則是會變化的! 而 glibc 的版本都會在每次 Linux 發行版大版本升級的時候更新。 這就意味著對于生產環境來說,你通常不能把 A 系統上的 PG 物理文件直接拷貝到 B 系統上去運行 —— 除非你使用了 PG17 后的內置排序規則,而這并非默認設置。
在 initdb 的時候,使用 --locale-provider=builtin 以及 --builtin-local=C.UTF-8 這兩個參數
在 2024 年的 PGConf.Dev 上,Jeremy Schneider 的 Collations from A-Z[3] 主題演講就深入解釋過這個問題。 PostgreSQL 開發組也意識到這確實是一個問題,所以在去年 PG 17 發布的時候,引入了一個新的特性,內置排序規則。也就是不再用操作系統 glibc 提供的排序規則了,不過只支持 C 和 C.UTF-8 這兩種規則。 如果你想更深入的進一步了解這個主題,老馮非常建議你閱讀這份材料。或者收看 PGConf.Dev 2024 現場視頻[4]。
![]()
排序規則的23個常見誤區,以下全錯!
1.讓字符排好序是一件簡單的事情。2.人和電腦用的排序規則是不變的。3.改變排序規則是一件很罕見的事。4.改變排序規則總是有意進行的。5.排序規則只會搞爛索引6.搞爛的東西可以重建7.我的數據庫沒有用到奇怪語言中的字符,所以跟排序規則無關8.我的數據庫能理解所有放在里面的字符9.PG 的 “錯誤排序庫版本” 警告總是能被某人看到10.PG 總是能知道宿主系統使用的C標準庫版本11.你可以把老的 glibc 代碼里面的排序規則部分單拉出來,單獨構建然后裝到新系統上來解決問題12.ICU 可以解決一切排序規則問題!13.ICU 沒有 glibc 2.28 fiasco 那樣重大的排序規則變化14.假設 Devrim 和 Christoph 樂意替你構建老版本的 ICU15.glibc 小版本/補丁版本不會修改排序規則16.庫版本號不變,排序規則就不變17.PG 還沒有提供內置的Collation Provider,來解決上面所有的數據損壞危機18.PG 的 C 和 C.UTF-8 排序規則是一回事兒19.C.UTF-8 排序規則是不變的。20.Collation Provider 只解決排序規則的問題。21.C.UTF-8 里面的 CTYPE 是不變的22.用戶想要DB級別的語言排序23.PG不太可能有一個內置的排序規則來解決上面這些問題
令人欣慰的是,PostgreSQL 去年的 17 版本中引入了內置排序規則,解決了上面這些問題。 老馮的 PG 發行版 Pigsty 也相應地在 v3.4.0[5] 正式引入應用了這個特性。
—— 所有 PG 17 以上的集群都統一使用 built-in locale-provider,固定使用 C.UTF-8 排序規則。 對于 17 以下的版本,則使用操作系統的 C.UTF-8 排序規則,如果操作系統實在是搓到不支持 C.UTF-8 (真的有!),那就保底用 C 排序規則。
這樣做的好處是,只要用這個內置排序規則,操作系統再怎么瞎搞,也不影響 PostgreSQL 的排序了。你即使升級了底層操作系統,也不用折騰什么索引重建,擔心數據損壞了。
官方不等于“靠譜”
不過顯然對于 PostgreSQL 專家屬于 “常識性質的最佳實踐”,并不是那么普及。 至少在 Docker 的 “官方 postgres 鏡像” 上,就很缺少這些已知的 “最佳實踐”。正如 Gwen 所說:有個 “官方” 倆字,并不代表 “負責任的生產表現”。
![]()
DockerHub 上的 postgres 鏡像被廣泛使用(據說是下載量最多的鏡像),然而它的質量在 PostgreSQL 專家看來確實是相當令人堪憂的。 這個 “官方” 指的是 Docker 的 “官方”,而不是 PostgreSQL 社區。所以里面充斥的大量的反模式,使用起來非常難受。
說到底這個所謂官方鏡像就是一個極其簡陋的封裝:用 apt 給你從 PGDG APT 倉庫里安裝一下,然后跑一個土法 init 腳本。 這個鏡像,對于 POC,開發,測試,學習來說是基本夠用了,但離生產環境的距離,可謂差著十萬八千里。
生產數據庫不宜使用容器
如果你用的是 Docker Postgres 容器,即使沒有在這次的小版本升級上翻車,也有很大概率會在其他問題上翻車。 比如默認的 64 MB Shmem 共享內存段;直接寫 Overlay FS;安裝的擴展在從節點上消失;在一個卷上跑兩個PG實例把數據烤糊;奇葩的從庫搭建流程;
諸如此類在物理機/虛擬機上根本不存在的容器特有問題,老馮在《》討論過很多, 但顯然社區還會不斷出現新驚喜(嚇),容器上運行數據庫的狀態,仍然沒有達到裸 Linux 上運行的長期博弈均衡態。
像 Locale 配置這樣的工程細節有許許多多,絕對不是 docker pull 一個所謂 “官方鏡像” 能解決的。 老馮的 Pigsty 為了解決用好 PostgreSQL 的問題,光本身的純代碼就有近十萬行, 這也顯然不是 “官方鏡像” 一個幾百行 Shell/Dockerfile 腳本能 Cover 的問題。
實際上有一些第三方的 PostgreSQL over Kubernetes 供應商,他們提供的 PG 容器會比這個 “官方版” 要好得多。 但老實說,也依然會受到容器本身的掣肘 —— 一堆 K8S / Docker 大師吭哧吭哧優化半天,也很難趕上直接在 Linux 上裸奔的 PG。 對數據庫老司機來說,確實有一種隔靴搔癢的感覺。
Docker 確實很方便,老馮也拿他跑無狀態的服務,批量運行編譯任務,有時候當廉價虛擬機測試,或者是簡單測試一下數據庫功能。 但唯獨在生產環境使用的時候,老馮會對用容器跑數據庫堅定說 “不” (—— 唯一的例外可能是 Redis)。
應該如何安裝 PostgreSQL?
那么,如果不用容器,又應該如何安裝部署 PostgreSQL 呢?
PostgreSQL 這樣的數據庫是與操作系統緊密聯系的特殊軟件。 最好的狀態,就是直接不帶套運行在裸 Linux 上,簡單,直接,穩定,可靠,沒有額外的性能折損與管理負擔。
有很多人覺得這是一件很復雜的事情,好像又要折騰什么 YUM/APT 倉庫,官方鏡像太慢又要翻墻; 國內鏡像站也全面斷更《》,然后安裝好了之后怎么配置調參優化也一籌莫展。 實際上這都已經是老黃歷了。老馮的 開源 PG 發行版 Pigsty[6] 就是為了直接在 Linux 上運行企業級 PostgreSQL 服務而設計的。
![]()
目前,我在 Debian 12/13,Ubuntu 22/24,EL 8/9/10 ,ARM / x86 也就是 14 個主流 Linux 發行版上提供了原生的 PostgreSQL 內核(PG 13-18 六個大版本),8 款不同風味的 PG 內核分支,近百個生態工具與 430 個生態擴展。 并將其打造成一鍵部署安裝拉起,自帶監控高可用,PITR 的生產級方案。還提供了 PGDG 官方倉庫的中國鏡像,應該是目前國內唯一沒有和 PGDG 斷更的 PG 鏡像站 —— 《》
![]()
老實說,這可真是個辛苦的活兒。光打出來的 RPM/DEB 包就有大幾萬個。各種測試組合,上游變動,都需要照顧到。 老馮也想過 —— 做個 Docker 鏡像唄,偷懶又省事,丟給用戶,你愛在什么操作系統上跑就在什么系統上跑。 但作為一個也要給自己用的大規模生產方案,我還是決定要去做 “正確而艱難” 的事情—— 提供在 14 個主流 Linux 發行版上直接運行整個 PostgreSQL 生態的能力。
畢竟,“官方鏡像” 也是用 APT 從倉庫里安裝的…,總要有人做這個事
非常好的一點是,Pigsty 的擴展倉庫和鏡像倉庫是獨立的,如果你不喜歡用一個大而全的發行版, 你也可以直接使用免費的 APT/YUM 倉庫,安裝原生 PGDG 內核與上面所有這些工具與擴展。
當然, 廣告就到這里。這一篇老馮聊了為什么 不要在生產環境中用容器跑 PostgreSQL。 下一篇,老馮會詳細的介紹一下 PostgreSQL 安裝實操 —— 如果不用容器,我應該怎么裝 PG!
![]()
參考閱讀
特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.