《為PostgreSQL討說法:淺析Uber切換MySQL》要點:
本文介紹了為PostgreSQL討說法:淺析Uber切換MySQL,希望對您有用。如果有疑問,可以聯系我們。
原題目為:為PostgreSQL討說法 - 淺析《UBER ENGINEERING SWITCHED FROM POSTGRES TO MYSQL》
配景
最近有一篇文檔,在國外鬧得沸沸揚揚,是關于UBER使用mysql替換postgres原因的文章.
https://eng.uber.com/mysql-migration/
文章涉及到PG數據庫的部分,論點過度的浮于外面,沒有深入的理解和分析.
很容易導致用戶對PostgreSQL產物的誤解.
另外還有一篇翻譯的文檔,則看起來已經完全變味.
http://www.jdon.com/48216
隱約感覺到一股黑PG的勢力襲來.
為了讓用戶更清楚的認識涉及技術的本色,我打算寫一篇淺析的文章,深入淺出的講講個中道理.
uber在文章論述的遇到的PG問題
We encountered many Postgres limitations:
Inefficient architecture for writes
Inefficient data replication
Issues with table corruption
Poor replica MVCC support
Difficulty upgrading to newer releases
我接下來會依依介紹其原理,以及文章內容存在的問題.
1. Inefficient architecture for writes
uber文章的觀點
PG的MVCC機制,更新數據為新增版本,會帶來兩個問題
SSD的寫放大
索引的寫放大
本文觀點
事實并不是MVCC的問題,所有的數據庫只要支持并發讀寫,就必要多版本,只是版本管理的手段可能不一樣.
有通過回滾段管理的,也有通過多版本(MVCC)進行管理的.
原理剖析
基于回滾段管理的數據庫
當更新一條記錄時,有些數據庫必要將整個數據塊拷貝到回滾段區域(有些是基于邏輯行的拷貝,則拷貝到回滾段的是記錄).
注意寫回滾段也是會發生REDO寫操作的.
更新可能在當前的row進行.
這種情況,只要索引字段不變化,索引就不必要變.
如果索引字段值產生變化,索引也要變化.
如果更新后的記錄超過本來行的長度,可能在本頁找一塊空閑區域(如果能裝下),也可能要到其他頁找一塊區域進行更新,有擦除舊記錄,寫入新紀錄的寫操作.
不管怎樣,索引都要變化.
基于回滾段的數據庫,如果要回滾事務,開銷會很大(特別是當事務修改的數據量很大時),因為要從回滾段將整個塊拷貝到數據文件(基于邏輯行拷貝的回滾則是類似重新來一遍UNDO事務的SQL操作,同時還必要擦除之前更改的行).
代價非常高 .
基于MVCC的數據庫
當更新一條記錄時,發生一個新的版本.
PostgreSQL 會優先使用在當前頁更新(HOT),即在當前頁進行更新,不管行長度是否產生變化.
這種情況,只要索引字段不變化,索引就不必要變.
如果索引字段值產生變化,索引也要變化.
(hot時,索引不變,通過HEAP頁內舊item指向新item來做到定位到新的記錄)
如果未在當前頁更新,則索引才必要變化
(通過配置表的fillfactor,可以大大減少這種情況的發送,盡量走HOT)
如果讀者還是擔心這個問題,我們可以做一個壓測試驗,看看到底會不會更新索引,會不會對更新造成性能影響如何?
1000萬數據,9個字段,8個索引,更新此中的mod_time字段.
postgres=# create table tbl(id int, mod_time timestamp(0), c1 int, c2 int, c3 int, c4 int, c5 int, c6 int, c7 int) with (fillfactor=80);CREATE TABLETime: 1.906 mspostgres=# insert into tbl select i,clock_timestamp(),i+1,i+2,i+3,i+4,i+5,i+6,i+6 from generate_series(1,10000000) t(i);INSERT 0 10000000Time: 14522.098 ms
postgres=# create index idx1 on tbl(c1) with (fillfactor=80);CREATE INDEXTime: 3005.753 ms
postgres=# create index idx2 on tbl(c2) with (fillfactor=80);CREATE INDEXTime: 2793.361 ms
postgres=# create index idx3 on tbl(c3) with (fillfactor=80);CREATE INDEXTime: 2804.031 ms
postgres=# create index idx4 on tbl(c4) with (fillfactor=80);CREATE INDEXTime: 2856.954 ms
postgres=# create index idx5 on tbl(c5) with (fillfactor=80);CREATE INDEXTime: 2895.643 ms
postgres=# create index idx6 on tbl(c6) with (fillfactor=80);CREATE INDEXTime: 2932.394 ms
postgres=# create index idx7 on tbl(c7) with (fillfactor=80);CREATE INDEXTime: 2939.927 ms
postgres=# alter table tbl add constraint pk_tbl primary key(id) with (fillfactor=80);ALTER TABLETime: 3292.544 ms
記錄下當前表的大小和8個索引的大小
postgres=# \dt+ tblList of relations
Schema | Name | Type | Owner | Size | Description
--------+------+-------+----------+--------+-------------
public | tbl | table | postgres | 919 MB | (1 row)
postgres=# \di+
List of relations
Schema | Name | Type | Owner | Table | Size | Description
--------+-----------------------+-------+----------+------------------+--------+-------------
public | idx1 | index | postgres | tbl | 241 MB | public | idx2 | index | postgres | tbl | 241 MB | public | idx3 | index | postgres | tbl | 241 MB | public | idx4 | index | postgres | tbl | 241 MB | public | idx5 | index | postgres | tbl | 241 MB | public | idx6 | index | postgres | tbl | 241 MB | public | idx7 | index | postgres | tbl | 241 MB | public | pk_tbl | index | postgres | tbl | 241 MB |
全力壓測30分鐘,更新mod_time字段
$ vi test.sql\setrandom id 1 10000000update tbl set mod_time=now() where id=:id;
壓測開始
pgbench -M prepared -n -r -P 5 -f ./test.sql -c 48 -j 48 -T 1800
壓測成果,更新速度持續在 13萬/s 以上. 這個壓力應該可以覆蓋很多的用戶吧.
progress: 5.0 s, 133373.6 tps, lat 0.357 ms stddev 0.269progress: 10.0 s, 133148.2 tps, lat 0.359 ms stddev 0.310progress: 15.0 s, 134249.0 tps, lat 0.356 ms stddev 0.299progress: 20.0 s, 131037.9 tps, lat 0.364 ms stddev 0.341progress: 25.0 s, 135326.3 tps, lat 0.353 ms stddev 0.292progress: 30.0 s, 135023.9 tps, lat 0.354 ms stddev 0.289......progress: 1385.0 s, 135997.9 tps, lat 0.351 ms stddev 0.261progress: 1390.0 s, 133152.5 tps, lat 0.359 ms stddev 0.302progress: 1395.0 s, 133540.7 tps, lat 0.357 ms stddev 0.287progress: 1400.0 s, 132034.8 tps, lat 0.362 ms stddev 0.314progress: 1405.0 s, 135366.6 tps, lat 0.353 ms stddev 0.266progress: 1410.0 s, 134606.6 tps, lat 0.355 ms stddev 0.280.....
progress: 1855.0 s, 134013.7 tps, lat 0.356 ms stddev 0.298progress: 1860.0 s, 132374.8 tps, lat 0.361 ms stddev 0.306progress: 1865.0 s, 133868.3 tps, lat 0.357 ms stddev 0.282progress: 1870.0 s, 133457.1 tps, lat 0.358 ms stddev 0.303progress: 1875.0 s, 133598.3 tps, lat 0.357 ms stddev 0.297progress: 1880.0 s, 133234.5 tps, lat 0.358 ms stddev 0.297progress: 1885.0 s, 131778.9 tps, lat 0.362 ms stddev 0.319progress: 1890.0 s, 134932.2 tps, lat 0.354 ms stddev 0.274......
progress: 2235.0 s, 135724.6 tps, lat 0.352 ms stddev 0.284progress: 2240.0 s, 136845.0 tps, lat 0.349 ms stddev 0.256progress: 2245.0 s, 136240.6 tps, lat 0.350 ms stddev 0.264progress: 2250.0 s, 136983.2 tps, lat 0.348 ms stddev 0.248progress: 2255.0 s, 137494.5 tps, lat 0.347 ms stddev 0.251......
壓測結束后,查看表和索引的大小,如果按UBER文中指出的,會更新索引,但實際上,成果說話,表和索引根本沒有膨脹.
UBER 文章對用戶的誤導不攻自破.
表的大小未變化postgres=# \dt+
List of relations
Schema | Name | Type | Owner | Size | Description
--------+------------------+-------+----------+---------+-------------
public | tbl | table | postgres | 919 MB | 索引的大小也未變化
postgres=# \di+
List of relations
Schema | Name | Type | Owner | Table | Size | Description
--------+-----------------------+-------+----------+------------------+--------+-------------
public | idx1 | index | postgres | tbl | 241 MB | public | idx2 | index | postgres | tbl | 241 MB | public | idx3 | index | postgres | tbl | 241 MB | public | idx4 | index | postgres | tbl | 241 MB | public | idx5 | index | postgres | tbl | 241 MB | public | idx6 | index | postgres | tbl | 241 MB | public | idx7 | index | postgres | tbl | 241 MB | public | pk_tbl | index | postgres | tbl | 241 MB |
小結
基于回滾段的數據庫,在更新數據時SSD寫放大 > 100%(因為回滾段是必定要寫的,并行寫回滾段的操作也需要寫REDO);而基于MVCC的數據庫,SSD寫放大的概率低于100%(因為可能發生HOT,發生在當前頁),而且舊記錄只改行的xmax標記,產生的REDO極少.
基于回滾段的數據庫,在刪除數據時SSD寫放大是100%(因為回滾段是必定要寫的,并行寫回滾段的操作也需要寫REDO);而基于MVCC的數據庫,SSD寫放大的概率為0 (因為只需要改一下行頭部的xmax的標記).
基于回滾段或MVCC的數據庫,索引的寫放大,都與是否產生行遷移有關,概率差不多.
基于回滾段的數據庫,如果要回滾事務,開銷會很大(特別是當事務修改的數據量很大時),因為要從回滾段將整個塊拷貝到數據文件(基于邏輯行拷貝的回滾則是類似重新來一遍UNDO事務的SQL操作,同時還必要擦除之前更改的行).
基于MVCC的數據庫,事務回滾非常快,因為不必要拷貝行或者數據塊,也不必要修改已更新的記錄,只是記錄clog時將當前事務標記為ABORT即可,也就是說只必要改2個比特位.
PG的HOT技術完美的辦理了索引更新的問題,根本不存在UPDATE就一定需要更新索引的問題.
彩蛋
PostgreSQL TOAST機制
PostgreSQL的TOAST機制,可以將變長類型的值,自動壓縮存儲到另一片區域,通過內部的POINT指向,而不影響行的其他值. 例如存儲文檔,或者圖片的表,如果這個表上有一些字段要更新,有一些字段不要更新,那么在更新時,PostgreSQL數據庫會有非常大的優勢,因為行很小.
基于回滾段的數據庫,必要拷貝舊的記錄或數據塊到回滾段,記錄或塊越大,這個開銷越大.
存儲文檔、圖像、非布局化數據,使用PostgreSQL很有優勢.
MySQL innodb是基于B+樹的存儲,當PK數據隨機數據寫入時存在巨大寫放大,因為常常要分裂,不僅影響插入速度和查詢速度,同時數據存放也會變得非常無序.
即使按PK順序掃描時,也可能呈現大量的離散IO.
基于B+樹布局的存儲,為了提高插入速度,如果使用index cache的話,則影響并發的查詢,因為查詢時要先合并索引.
另一方面,B+樹的存儲,必需要求表需要一個PK(即使表沒有PK的需求,也要硬塞一個PK列進來),secondary index則指向這個PK.
如果PK發生更新,則所有的secondary index都要更新,也便是說,為了保證secondary不更新,務必確保PK不更新.
如果要對secondary index進行范圍掃描,其實物理的掃描上是離散的.
所以uber本文提出的,secondary index 不需要變更的好處,其實背后是有以上代價存在的(例如必定要加PK,插入速度更慢,插入時PK不能隨機否則分裂帶來的IO巨大,使用secondary index范圍掃描時會造成離散的IO等弊端),把原理,代價都交代清楚,才能看得更清楚.
PostgreSQL 有幾種辦法來消除這種離散IO.
1. bitmap scan,獲取heap tuple前,先根據ctid的blockid排序然后再從heap獲取記錄,以獲得物理上次序的掃描.
2. cluster by index,將表的物理存儲順序依照索引的順序來存放,從而使用該索引掃描時,則是順序的掃描.
PostgreSQL的表是基于HEAP存儲的,不存在以上B+樹存儲的問題,隨便怎么插入,速度都很快.
SSD的原子寫,通常SSD寫入時是以最小單位為4K的寫入,即使修改很小的數據.
那么以directio或buffer io為主的數據庫,哪個對SSD的傷害更大呢?
對于directio的數據庫,因為每次都是真實的傷害,而buffer io的數據庫,OS層還會合并IO,可以大幅降低SSD的真實寫(os 層調整vm.dirty_background_ratio可以調整寫頻率,從而影響合并粒度).
PostgreSQL的shared buffer管理是基于buffer io的管理,對SSD來說是一種很好的掩護,有興趣的童鞋可以測試驗證一下.
2. Inefficient data replication
uber文章的觀點
PG的復制低效,有寫放大.
本文觀點
PostgreSQL的流復制非常高效,延遲幾乎為0,同時還支持流的壓縮和加密傳輸,很多企業用流復制來實現異地容災,HA,讀寫分離的應用場景.
同時PostgreSQL也支持邏輯復制(>=9.4支持流式邏輯復制, <9.4的版本則支持基于觸發器或者基于異步消息的邏輯復制).
原理剖析
問題反駁 1 (復制低效)
我第一次聽說PG的復制低效的,要知道PG的復制是業界有名的高效,延遲極低(關鍵是復制延遲與事務大小無關),網絡好的話,幾乎是接近0的延遲.
PostgreSQL流復制原理
即時喚醒,流式復制,所以延遲極低.
問題反駁 2 (REDO寫放大)
基于回滾段的數據庫,在更新時,拷貝到回滾段的舊版本,是要寫REDO的.
而基于MVCC的數據庫,舊版本僅僅需要寫修改行頭bit位的REDO,所以基于MVCC的數據庫,更新時寫入的REDO應該是基于回滾段的數據庫的一半甚至更少(好比基于物理的回滾段要拷貝整個塊,產生的REDO也很大).
同時,由于基于回滾段的數據庫回滾時,要將回滾段的數據拷貝回數據文件,是會發生REDO的,這一點,基于MVCC的數據庫不存在這種寫放大的問題.
問題反駁 3(復制流量放大)
基于REDO的物理復制,意思便是要把REDO復制一份到備庫.
所以REDO寫了多少,就要復制多少到備庫,網絡的流量也是這樣的.
另一種是基于REDO的邏輯復制,需要復制的數據不僅僅包含新的數據,還要包含舊的版本數據(PK或者full row).
可能一條記錄更新前和更新后的數據都要復制.
對更新操作來說,物理復制,不必要復制舊的記錄(因為產生REDO的僅僅是XMAX的變化)過去,而邏輯復制則必要復制舊的記錄過去.
另外需要注意的是,目前PG的垃圾回收也是以物理恢復的形式復制的,在實現上還有改進空間,好比通過邏輯的方式復制垃圾回收(只復制block id),可以大大減少網絡傳輸的流量.
而 uber 文章并沒有指出,事實上 MySQL 目前只支持邏輯復制,而且如果要開啟邏輯復制,不僅僅要寫redo,同時還要寫 binlog,等于寫了雙份日志,這個寫放大也是很大的.
MySQL redo 用于恢復數據庫,binlog用于復制.
自PostgreSQL 9.4開始,PG內核層就同時支持物理復制和邏輯復制,并且僅僅寫一份日志就能同時支持物理以及邏輯復制.
在9.4版本之前,則可以通過其他軟件進行邏輯復制(例如Londiste3, slone-I)
邏輯復制有一個弊端,被復制的表必定要有PK. 物理復制不存在這個問題 .
邏輯復制另一個弊端,大事務導致主備的延遲非常大,因為備庫必定要等主庫事務結束,備庫才能開始回放該事務.物理復制不存在這個問題 .
小結
PG的復制是業界有名的高效,延遲極低(關鍵是復制延遲與事務大小無關),網絡好的話,幾乎是接近0的延遲.
基于MVCC的數據庫,就版本僅僅需要寫修改行頭bit位的REDO,所以基于MVCC的數據庫,更新時寫入的REDO應該是基于回滾段的數據庫的一半甚至更少(好比物理回滾段要拷貝整個塊,產生的REDO也很大).
對更新操作來說,基于REDO的物理復制,不必要復制舊的記錄過去,而邏輯復制則必要復制舊的記錄過去,物理復制產生的網絡流量更小.
邏輯復制有一個弊端,必定要PK. 物理復制不存在這個問題 .
邏輯復制另一個弊端,大事務導致主備的延遲非常大,因為備庫必定要等主庫事務結束,備庫才能開始回放該事務. 物理復制不存在這個問題 .
彩蛋
PostgreSQL可以開啟協議層壓縮,同時可以選擇是否加密傳輸,壓縮傳輸REDO.更高效,更平安.
PG的用戶如果有主備環境,可以關閉FULL_PAGE_WRITE,產生的REDO更少(第一次更新的PAGE不必要寫FULL PAGE).
但是必要注意,如果關閉了FPW并且主庫因主機問題或在OS問題掛了,必要從備份環境恢復.
PG用戶,可以將checkpoint拉長,減少FULL PAGE的發生,從而減少REDO的發生.
PG的用戶,如果必要從PG或者MYSQL復制到阿里云的rds PG,可以使用阿里dbsync插件,目前支持全量復制,增量的邏輯復制正在開發中.
3. Issues with table corruption
uber文章的觀點
用戶在使用PG 9.2 時,因為主備切換,導致了一些數據問題.
本文觀點
UBER在文中并沒有描述清楚這個問題的始末,如何復現,同時也沒有聽說過PG的其他用戶遇到這樣的問題.
并且我在測試環境模擬TPC-B,同時不停的進行主備切換,也沒有遇到類似問題.
PG的物理復制是可以保證主備完全一致的.
uber給出的觀點心理暗示比擬可怕.
PG一直以來便是一個以穩定性和功能強大著稱的數據庫,在企業市場有非常好的口碑.
國內的銀行,運營商,保險,互聯網公司都有在核心環境使用 ;
安全科技、阿里巴巴、高德、去哪兒、騰訊、用友、陽光、中移動、探探、智聯、典典、華為、斯凱、通策醫療、同花順、核電、國家電網、郵儲銀行、友盟、蓮子......
海外的汽車生產巨頭,政府部分,醫療,物流等各個行業也都有非常多的用戶 .
生物制藥 {Affymetrix(基因芯片), 美國化學協會, gene(布局生物學應用案例), …}
電子商務 { CD BABY, etsy(與淘寶類似), whitepages, flightstats, Endpoint Corporation …}
學校 {加州大學伯克利分校, 哈佛大學互聯網與社會中心, .LRN, 莫斯科國立大學, 悉尼大學, …}
金融 {Journyx, LLC, trusecommerce(類似支付寶), 日本證券交易交所, 郵儲銀行, 同花順…}
游戲 {MobyGames, …}
政府 {美國國家氣象局, 印度國家物理實驗室, 聯合國兒童基金, 美國疾病控制和預防中心, 美國國務院, 俄羅斯杜馬…}
醫療 {calorieking, 開源電子病歷項目, shannon醫學中心, …}
制造業 {Exoteric Networks, 豐田, 捷豹路虎}
媒體 {IMDB.com, 美國華盛頓郵報國會投票數據庫, MacWorld, 綠色和平組織, …}
零售 {ADP, CTC, Safeway, Tsutaya, Rockport, …}
科技 {Sony, MySpace, Yahoo, Afilias, APPLE, 富士通, Omniti, Red Hat, Sirius IT, SUN, 國際空間站, Instagram, Disqus, …}
通信 {Cisco, Juniper, NTT(日本電信), 德國電信, Optus, Skype, Tlestra(澳洲電訊), 中國移動…}
物流 {SF}
小結
僅憑一個沒有始末的結論,似乎很難闡明什么.
基于邏輯復制的數據庫,主庫壓力大時通常會遇到備庫追不上.
又或者因為某些原因導致主備紛歧致,即使發現了,可能并沒有很好的修復手段,因為你不知道該以哪個數據為準.
邏輯復制導致主備紛歧致的原因較多,例如 主庫執行失敗,備庫執行成功,或者備庫執行成功,主庫執行失敗.
又或者 主庫和備庫的環境紛歧致,例如字符集,或者其他的,都非常容易導致主和備的紛歧致.
對于要求主備嚴格一致的場景,強烈建議使用物理復制.
4. Poor replica MVCC support
uber文章的觀點
PG備庫的MVCC支持較差,查詢會與恢復堵塞
本文觀點
首先,PG的備庫分兩種,一種是物理備庫,一種是邏輯備庫.
對于邏輯備庫來說,與MYSQL的恢復機制是一樣的,既然是一樣的就不必要討論了.
UBER文章說的 查詢會與恢復堵塞,說的是物理備庫,但必需糾正一個觀點,查詢是否堵塞恢復,是要論看場景的,況且堵塞的情況極為少見,還有一點要注意,邏輯復制也會有堵塞.
原理剖析
物理復制,什么情況下查詢會堵塞、或與恢復沖突?
當以下操作產生的REDO被復制到備庫,而且備庫準備拿這些REDO來恢復時.
Access Exclusive locks taken on the primary server, including both explicit LOCK commands and various DDL actions, conflict with table accesses in standby queries.
主庫的拜訪排它鎖,與備庫對應的鎖產生沖突.
例如主庫truncate a表, 備庫查詢a表.
這種情況的沖突面很窄.
Dropping a tablespace on the primary conflicts with standby queries using that tablespace for temporary work files.
主庫刪除表空間,備庫使用這個表空間產生臨時文件. 例如主庫刪除TBS,備庫的一個大的查詢必要寫臨時文件,并且這個臨時文件是寫到這個表空間的.
這種情況非常少見,也很容易規避,新建一個臨時表空間不要刪除即可.
Dropping a database on the primary conflicts with sessions connected to that database on the standby.
主庫刪除數據庫,備庫剛好連在這個數據庫上.
這種情況也非常的少見.
Application of a vacuum cleanup record from WAL conflicts with standby transactions whose snapshots can still "see" any of the rows to be removed.
主庫回收dead tuple的REDO,同事備庫當前的query snapshot必要看到這些記錄.
這種情況可以通過參數控制,恢復優先,或查詢優先. 可以配置時間窗口.
并且這種沖突出現的概率也非常的小,除非用戶在備庫使用repeatable read,同時是非常大的事務.
而通常用戶用的都是read committed.
Application of a vacuum cleanup record from WAL conflicts with queries accessing the target page on the standby, whether or not the data to be removed is visible.
同上,但是當query拜訪的頁就是要清理垃圾的頁時,也是有沖突的.
這是物理復制與邏輯復制唯一有差其余地方,但是對現實場景來說,這種情況出現的概率也不大.
對于PG來說,主備沖突導致的備庫延遲,絕對沒有MySQL邏輯復制在碰到大事務時那么可怕,邏輯復制遇到大事務,導致的延遲是很嚴重.
在現實應用場景中,很少有用戶擔心PG的備庫延遲,即使有短暫的沖突,因為是基于塊的恢復,恢復速度是很快的,馬上就能追平(只要備庫的IO才能夠好,通常追平是瞬間完成的).
難道邏輯復制就不會呈現查詢與恢復的堵塞、沖突嗎?
當然也會有沖突
邏輯復制,什么情況下查詢會堵塞、與恢復沖突?
備庫發起一個repeatable read的事務,由于備庫賡續的恢復,備庫的該查詢事務有可能因為snapshot too old失敗.
主庫發起的DDL語句,回放時會與備庫的查詢沖突,DDL的回放會被完全堵塞.
主庫刪除一個數據庫,回放時如果備庫正好連在這個數據庫上,發生沖突.
小結
基于物理復制或邏輯復制,只要備庫拿來使用,都有可能呈現查詢與恢復沖突的情況.
PG對于沖突的處置非常的人性化,你可以選擇恢復優先 or 查詢優先,設置時間窗口即可.
同時PG還支持備庫的QUERY反饋機制,主庫可以根據備庫的QUERY,控制垃圾回收的延遲窗口,避免QUERY和垃圾回收的沖突.
5. Difficulty upgrading to newer releases
uber文章的觀點
PG的跨版本升級較難,跨版本不支持復制
本文觀點
看起來文章的作者或者UBER里可能沒有熟悉PG的人,PG的大版本升級的途徑很多,也很便利.
我這里給出兩個辦法
1. 辦法1, 通過遷移元數據的方式升級,這種升級方式,取決于元數據的大小(即數據結構,函數,視圖等元信息)所以不管數據庫多大,都能很快的完成升級.
例如以10萬張表,1萬個函數,1000個視圖為例,這樣的元數據大小可能在幾十MB的水平. 自動化水平高的話,導出再導入應該可以控制在分鐘級別完成.
關鍵是它能支持原地升級,也就是說,你不必要再準備一套環境,特別是數據庫非常龐大的情況下,再準備一套環境是很恐怖的開銷.
當然,如果企業有環境的話,為了保險,通常的做法是,復制一個備庫出來,在備庫實現原地升級,然后激活備庫轉換為主庫的角色.
備庫升級結束后,再升級老的主庫,由于只動到元數據,所以主備的差異很小,rsync一小部門數據給老的主庫,就能讓老的主庫實現升級,同時將老的主庫切換成備庫即可.
簡單的幾步就完成了主備的大版本升級.
基于pg_upgrade的大版本升級可以參考我以前寫的文章
http://blog.163.com/digoal@126/blog/static/1638770402014111991023862/
http://blog.163.com/digoal@126/blog/static/163877040201341981648918/
2. 辦法2, 通過邏輯復制增量平滑升級,與MySQL的升級辦法一樣,也很便利,但是要求一定要準備一個備庫環境,如果數據庫已經很龐大的話,總的升級時間會比較漫長.
對于 >= 9.4的版本可以使用PG內置的邏輯復制.
小于9.4的版本則可以使用londiste3或者slony-I.
PG跨版本支持復制,并且支持的很好.
對于>=9.4的版本,可以用基于流的邏輯復制.
對于<9.4的版本,可以使用londiste3, slony-I.
小結
每種數據庫都要去深入了解,才能去辦理業務上面對的問題.
每種數據庫存在即有存在的理由,有它適合的場景,MySQL和PostgreSQL發展這么多年,都有各自的用戶群體,相互都有學習和借鑒的地方, 作為數據庫內核工作者,要多學習,把數據庫做好,把最終用戶服務好才是王道 , 也許下一代的數據庫引擎便是PostgreSQL和MySQL雜交的,看看阿里云ApsaraDB接下來會放什么招吧 .
沒有深入探討問題的根源就拋觀點,是紕謬的,容易被拿去當槍使,對整個開源行業沒什么好處 .
UBER頒發的該文章對PG的論點過于表面和片面,讀者要多思考,別被拿去當槍使了還不知道 .
基于線程和進程的討論太多,PG基于進程,優勢是非常強壯,所以PG的擴展能力極強,看看PG那無數的插件就知道了,是一個貼近用戶的,高度可定制化的數據庫.本文末尾的推薦閱讀也包括了大量通過插件方式擴展PG功能的文章.
本文僅對uber發文的PG部門作出解釋,網友可以多多交流.
更多深度技術內容,請存眷云棲社區微信公眾號:yunqiinsight.
《為PostgreSQL討說法:淺析Uber切換MySQL》是否對您有啟發,歡迎查看更多與《為PostgreSQL討說法:淺析Uber切換MySQL》相關教程,學精學透。維易PHP學院為您提供精彩教程。