More Related Content Similar to 淺入淺出 MySQL & PostgreSQL (20) More from Yi-Feng Tzeng (15) 淺入淺出 MySQL & PostgreSQL2. 2015
2/147
請先閱讀 Triton Ho 的教材
♪ Triton Ho( 以下稱作者 ) 的教材。
https://drive.google.com/file/d/0Bw4cH_iKZJzKOE5YRWtZS3lyTkk/view
https://drive.google.com/file/d/0Bw4cH_iKZJzKLXFIWVFfMk13Vm8/view
https://drive.google.com/file/d/0Bw4cH_iKZJzKd25qdV9HTEp4VVk/view
♪ 作者簡報中有值得參考的內容。
♪ 目的不是為了批評,而是討論。
♪ 本討論將探討其中幾個論點。
8. 2015
8/147
前言
♪ 我不是 MySQL / PostgreSQL 推廣者,只看重誰能解決問題。
♪ 非專業 DBA ,只是個架構實習生。
♪ 無漫罵,不認為 MSSQL 、 MySQL 或 PostgreSQL 是垃圾。
♪ 每個軟體都有優缺點,依實際場景選擇不同的需求。
12. 12/147
2015
淺談 SSD
Random IO ? Sequential IO ?
但我想談的是背後更深的原因。
『為什麼 MySQL 及 MSSQL 盡力追求 Sequential IO ?』
『 Sequential IO 在 SSD 的世界中真的無所謂?』
13. 13/147
2015
淺談 SSD
很多人都會說 SSD 的 Sequential IO 不重要,但真的不重要?
雖然 MySQL 相較 PostgreSQL 實際只用到不算明顯的優勢。
本篇的內容側重在 Ordered INSERT 生成的 Reorder 及 Merge
,這是後續會提到的。
28. 28/147
2015
OLTP: Fragmentation
《我的解釋》
因為 INSERT 循序 ( 規律 ) ,所以 Hotspot 會發生在 last node
( 頻繁地在 last node 寫入 ) ;但若不循序,則容易造成 Storage /
Memory Fragmentation ,也就是若資料增刪改是不循序的,
則空間配置也會是不循序的。
Ref: https://www.facebook.com/yftzeng.tw/posts/10202227746654516
37. 37/147
2015
OLTP: Fragmentation
《我的解釋》
INSERT 循序時,會頻繁在同一 page 操作 ( 維持 Sequential ,
但有 Hotspot 問題 ) 。 MySQL 預設會延遲寫入 Storage ,並將
連續異動的 pages 一次寫入,甚至將請求合併,從而有效降低
Random write 的次數以及合併部分 Random write 變成
Sequential write 。
但若使用 Random UUID 時,寫的不一定是同一 page ,所以即
使延遲寫入 Storage ,也會產生很多次 Random write 。
Ref: http://www.tocker.ca/2013/05/06/when-does-mysql-perform-io.html
40. 40/147
2015
OLTP: Fragmentation
《我的解釋》
SELECT 時,若在 Memory 中, MySQL 會保持 ( 邏輯 )Ordered 。
但 PostgreSQL 不是,而且再加上它的 MVCC 設計, UPDATE/
DELETE 遺留下的 dead rows 會使得 Fragmentation 更嚴重。
若 SELECT 是從 Storage 取出時,因 MySQL 連續異動的 pages
一次寫入,甚至將請求合併,所以相近的資料放在同一 block 的
機率較高。且將 Storage 讀出放入 Memory 時, MySQL 仍會保
持 ( 邏輯 )Ordered 。
45. 45/147
2015
OLTP: Fragmentation
我同意「 OLTP 在現實是中是自然存在的」,其實不管是 OLTP /
OLAP 都一樣。我從來沒有否定這點。
我指的也不是
『難道 triton.ho@gmail.com 登入系統了,然後便會是
triton.hp@gmail.com(p 比 o 後)會登入系統嗎?』
不過,即使真的 .ho 登入後是 .hp 登入也一定是 Random IO 。
因為這是兩個不同的 SELECT Query 。同一個 Query 下才有
可能是 Sequential IO 。
Sequential IO 需例如一次寫入一批資料,而這些資料寫入的
block 是順序的。 Random IO 則是分別寫入不同的 block 。
52. 52/147
2015
Hotspot
這部分我的結論是,反正 Disk IOPS 會愈來愈快 ( 作者也同意 ) ,
Hotspot 的影響程度也會愈來愈低。雖然 Fragmentation 的問題也
隨 IOPS 增加而減輕,但是只要 SSD 的「非循序讀寫」與「循序
讀寫」差距仍然高達至少四分之一時,通常應該傾向選擇 Hotspot
會比較好。 ( 因為我知道緩解甚至最終解方案 )
另外, Hotspot 會造成 write 效能降低,而 Fragmentation 會降低
read 效能,但一般資料庫的操作通常是 read 比 write 次數高很多,
所以選擇 Hotspot 也 ( 可能 ) 是正確的。
最後, PostgreSQL 天生無法避免 Fragmentation 問題,而且修復
Fragmentation 的解決方法就是執行 VACUUM FULL ( 標準的
VACUUM 沒用 ) ,可惜這會造成 table locked ,更慘。
Ref: https://www.facebook.com/yftzeng.tw/posts/10202227746654516
60. 60/147
2015
Auto / Manual Clean Dead Node
Ref: https://www.facebook.com/yftzeng.tw/posts/10202227746654516
《我的解釋》
MySQL 會在有限時間與空間內保持數據循序性,並依 background
job 清理 dead tuples 。這在簡報第 17 頁有提及。雖然會影響
concurrent write 的效能,但卻保持 concurrent read 的效率。
但 PostgreSQL 使用不一樣的思路,它把清理 dead tuples 的功能
交由 VACUUM 程序處理。不過自從 PostgreSQL 8.1 版新增
AUTOVACUUM 功能後,也等同於開始允許程序自動清理舊數據
而非單純只能人工; 9.0 版更是預設啟動 AUTOVACCUM 功能。
61. 61/147
2015
Auto / Manual Clean Dead Node
Ref: https://www.facebook.com/yftzeng.tw/posts/10202227746654516
《我的解釋》
這意謂 PostgreSQL 轉向 MySQL 的自動 ( 而非人工 ) 數據清理
方式。所以也就沒有作者所說的,「讓你自行決定什麼時候執行」
的問題,除非我們主動關閉 AUTOVACUUM 改手動。
不過,清理數據的時機很敏感 ( 否則會影響正常運行的效能 ) ,
交由官方演算法自行決定通常會比較安穩,除非你自己很明白你
在做什麼。況且, VACUUM 也不是沒有副作用。
(http://rhaas.blogspot.tw/2011/03/troubleshooting-stuck-vacuums.html)
62. 62/147
2015
Auto / Manual Clean Dead Node
Ref: https://www.facebook.com/groups/199493136812961/permalink/729886527106950/
63. 63/147
2015
Auto / Manual Clean Dead Node
Ref: https://www.facebook.com/groups/199493136812961/permalink/729886527106950/?comment_id=731212386974364
《我的解釋》
VACUUM 或 AUTOVACUUM ,我都讓使用者自行選擇,我
只是說 AUTOVACUUM 自有一套演算法自己知道何時會最好,
如果專家自認「手工」的方式比官方自帶的好,自然改「手工」
,我對此從來沒有意見。
64. 64/147
2015
Auto / Manual Clean Dead Node
Ref: https://devcenter.heroku.com/articles/postgresql-concurrency
Heroku 對 auto_vacuum 的引入非常樂見
65. 65/147
2015
Auto / Manual Clean Dead Node
Ref: http://www.postgresql.org/docs/9.4/static/routine-vacuuming.html
PostgreSQL 官方文件:
有些管理員會習慣在承載很低的時候,自動執行 VACUUM 。但
固定在某個時間執行會有一定的風險,例如 ( 不一定這期間 ) 突
然有非預期大量的 UPDATE 活動時,那麼就會發生 "bloat" ,反
而事後需要使用 VACUUM FULL 。
使用 autovacuum 可以緩解這個問題,因為 autovacuum 會動態
視 UPDATE 活動做調整。所以關閉 autovacuum 並不明智,除
非你能夠預期極端的工作量發生。
66. 66/147
2015
Auto / Manual Clean Dead Node
Ref: https://www.facebook.com/groups/199493136812961/permalink/731201573642112/
67. 67/147
2015
Auto / Manual Clean Dead Node
Ref: https://www.facebook.com/groups/199493136812961/permalink/729886527106950/
作者一直強調:
1. DBA 一定要知道什麼時候系統最清閒。
2. 手動決定而不要自動。
68. 68/147
2015
Auto / Manual Clean Dead Node
《我的解釋》
很多營運不是政府機關或金融單位。夜間也不一定可以緩機
休息。因此,面對全球化營運思維時,系統隨時都有高承載
的可能。這意謂著系統即使相對上有「比較空閒」的時間,
但此時線上人數都還可能超過數萬,甚至數百萬人使用。
這時候的任何回收行為都會影響正常的系統營運。
還是回到初衷,選擇最適你的業務需求。
69. 69/147
2015
Auto / Manual Clean Dead Node
《 Sentry 的災難與慘痛經驗 》
The internet is full of awful advice of users suggesting you
should turn off autovacuum, or run it manually at low traffic
times, or simply adjust its schedule to run less often. To know
why that’s ill-advised, you first need to understand the
consequences of autovacuum not running.
網路上很多人建議關閉 AUTOVACUUM ,改以排程或手動
選擇負載低的期間執行 VACUUM 。但這是不明智的,你只
要知道不運行 AUTOVACUUM 會帶來的各種嚴重後果就會
明白了。
Ref: http://blog.getsentry.com/2015/07/23/transaction-id-wraparound-in-postgres.html
73. 73/147
2015
大型系統不能用 auto-inc 一定要 UUID
Ref: http://en.wikipedia.org/wiki/Universally_unique_identifier
UUID 有很多種,用錯結果會不一樣。
常見的有 UUIDv1(Ordered UUID) 及 UUIDv4 (Random UUID) 。
作者在名詞上混淆了兩者。
UUIDv1 是跳號循序,同機器上後一個會比前一個大,所以總是
INSERT 在 last node ,仍有 Hotspot 問題。
顯然作者這裡用字若再精準些,應該要用 Random UUID ,
而不應該用較廣泛定義的 UUID 。
( 所以作者建議的是 Random UUID ,如 UUIDv4)
82. 82/147
2015
MySQL index->lock contention
Ref: https://www.facebook.com/groups/199493136812961/permalink/731201573642112/?comment_id=731385080290428
作者簡報上說:
MySQL 的 B+ tree 在 page splitting / merging 時
, 整棵 B+ tree 都會加上 WRITE_LOCK
但引用 Oracle 文章時又改說:
Oracle 官方人員說得很明白: leaf-node split / merge
( 他口中的 tree modification change) 會引起 index
X lock 的。
”前者意謂: always” 會發生。
”後者意謂: 只有發生在 leaf-node ”才會 。
我不是故意要找碴,但定義不清楚,很難討論。
83. 83/147
2015
MySQL index->lock contention
Ref: https://www.facebook.com/groups/199493136812961/permalink/731201573642112/?comment_id=731385080290428
不過我的論點也不是指「 page splitting / merging
不會造成整個 B+ tree 都會加上 WRITE_LOCK 」
,而是在某些特定情況下才會,所以不認為是您簡
報上說的 "always" 。
"latch" 中文是 " 鎖 " 沒錯,但他的 locked 是針對
"one page" ,不等同於 "full index" ,這是我的重點。
但會不會造成 "full index" ,不好意思,我承認沒有
追完所有原始碼。
85. 85/147
2015
MySQL index->lock contention
《整理》
作者的論點 ( 前後論點兩者彼此衝突 )
1. MySQL 的 B+ tree 在 page splitting / merging 時 ,(always) 整棵
B+ tree 都會加上 WRITE_LOCK 。
2. 只有 leaf-node split / merge 會引起 index X lock 。
我的論點 ( 兩個是獨立事件 )
1. “latch” “與 lock” 的定義在 MySQL 中不一樣。
2. ”latch” “只是 one page” ,不等同於 "full index" 。
( ”但若 latch” ”不只是 one page” 則可能,原始碼還沒追完 )
87. 87/147
2015
MySQL index->lock contention
《整理》
作者的論點 ( 雖然兩者衝突 )
1. MySQL 的 B+ tree 在 page splitting / merging 時 ,(always) 整棵
B+ tree 都會加上 WRITE_LOCK 。
2. 只有 leaf-node split / merge 會引起 index X lock 。
我的論點 ( 兩個是獨立事件 )
1. “latch” “與 lock” 的定義在 MySQL 中不一樣。
2. ”latch” “只是 one page” ,不等同於 "full index" 。
( ”但若 latch” ”不只是 one page” 則可能,原始碼還沒追完 )
2. “latch” ”可以對 node” ”,也可以對 full index” 。
89. 89/147
2015
MySQL index->lock contention
《整理》
作者的論點 ( 雖然兩者衝突 )
1. MySQL 的 B+ tree 在 page splitting / merging 時 ,(always) 整棵
B+ tree 都會加上 WRITE_LOCK 。
2. 只有 leaf-node split / merge 會引起 index X lock 。
我的論點 ( 兩個是獨立事件 )
1. “latch” “與 lock” 的定義在 MySQL 中不一樣。
2. ”latch” “只是 one page” ,不等同於 "full index" 。
( ”但若 latch” ”不只是 one page” 則可能 )
2. “latch” ”可以對 node” ”,也可以對 full index” 。
90. 90/147
2015
MySQL index->lock contention
Ref: http://mysqlserverteam.com/mysql-5-7-improves-dml-oriented-workloads/
Before 5.7, every modifications to non-leaf pages (every modifications for the
tree structure) required to exclude the other threads’ access to the whole index
by X-lock, and every concurrent accessing the index tree were blocked.
91. 91/147
2015
MySQL index->lock contention
Ref#1: http://mysqlserverteam.com/mysql-5-7-improves-dml-oriented-workloads/
Ref#2: http://www.percona.com/blog/author/yasufumi/
Ref#3: http://dev.mysql.com/worklog/task/?id=6326
Ref#4: https://github.com/mysql/mysql-server/commit/070115a3d9548f790039c39a48b19d759ab2407c
Before 5.7, every modifications to non-leaf pages (every modifications for the
tree structure) required to exclude the other threads’ access to the whole index
by X-lock, and every concurrent accessing the index tree were blocked.
MySQL 開發者講了兩件事:
1. page split / merge “時,只有發生在 non-leaf pages” 時才會 whole index locked 。
2. page split / merge “時,只有在 non-leaf pages” ”才稱 tree structure modification” 。
補充:這位 MySQL 開發者是誰?
1. 他是 Yasufumi Kinoshita 。
2. Percona 官方認證數一數二 InnoDB 專家,從事 InnoDB 內核改進多年。
( 底下附參考連結 #2)
3. 本次的改良幾乎由他主導,是實際改程式碼的人。 ( 詳見下方參考連結 #3 及 #4)
4. 如果不相信他,我也不知道該相信誰。
92. 92/147
2015
MySQL index->lock contention
《整理》
作者的論點 ( 雖然兩者衝突 )
1. MySQL 的 B+ tree 在 page splitting / merging 時 ,(always) 整棵
B+ tree 都會加上 WRITE_LOCK 。
2. 只有 leaf-node split / merge 會引起 index X lock 。
我的論點 ( 兩個是獨立事件 )
1. “latch” “與 lock” 的定義在 MySQL 中不一樣。
2. ”latch” “只是 one page” ,不等同於 "full index" 。
( ”但若 latch” ”不只是 one page” 則可能 )
93. 93/147
2015
MySQL index->lock contention
Ref: https://www.facebook.com/groups/199493136812961/permalink/731201573642112/?comment_id=731280403634229
1. 作者引用官方文件卻誤解真實現象。
(full index lock 會發生在 non-leaf node 而非 leaf node)
2. “如同我所述,確實 latch” ≠ “lock” 。
3. “我沒有說過 tree modification ≠ page split / merge” 。
102. 102/147
2015
無論如何絕對別升級到 MySQL 5.7 ?
Ref: https://www.facebook.com/groups/199493136812961/permalink/731201573642112/?comment_id=731236786971924
可是後來作者又承認 MySQL 5.7 解決了他心中最大的問題之一
104. 104/147
2015
Replication crash safe
PostgreSQL 9.0 引進了 Stream replication 技術,能夠極快地
處理 WAL (Write-Ahead Logging) 日誌。
但 9.0 的 Stream replication 是 Async replication ;當 Master
當機時,會有資料丟失的風險。
直到 PostgreSQL 9.1 ( 2011-09-11 ) 引進 Sync replication
技術後才解決。
而 MySQL 必須等到 5.7 才有完整的解決方案。比 PostgreSQL
晚了約 4 年,但也說明 MySQL 5.7 有很多大改進。
( 還是不建議升級為 MySQL 5.7 嗎? )
106. 106/147
2015
Query Optimizer
–《数据库查询优化器的艺术 原理解析与 SQL 性能》
PostgreSQL 9.2.3 vs. MySQL 5.6.10
17.3 本章小結 (p486)
對於子查詢的優化, PostgreSQL 和 MySQL 各有所長;對於等價謂詞
重寫,條件的處理, MySQL 略勝 PostgreSQL 一籌,在各種連接消除方面
,二者基本相當,都支持外連接消除和嵌套連接消除。在索引和約束的利用
方面,尤其是語義優化和非 SPJ 的優化, MySQL 顯得技高一籌;對於索引
和約束以及條件化簡的充分利用,使得 MySQL 能及早把計算和推理的工作
在查詢計劃生成的過程中完成,從而生成更為高效的查詢執行計劃。
整體上, MySQL 查詢優化器支持的邏輯優化點比 PostgreSQL 多,
MySQL 查詢優化器邏輯查詢優化部分靈光閃爍,讓讀之者愛不德手,但這
不代表查詢優化器的效率高於 PostgreSQL 。查詢優化器的效率高低需要
在現實中根據實際場景通過測試來評估。
108. 108/147
2015
High Concurrent Write?(Fragmentation)
《 MySQL 》
INSERT 循序時,會頻繁在同一 page 操作 ( 維持 Sequential ,
但有 Hotspot 問題 ) 。 MySQL 預設會延遲寫入 Storage ,並將
連續異動的 pages 一次寫入,甚至將請求合併,從而有效降低
Random write 的次數以及合併部分 Random write 變成
Sequential write 。
但若使用 Random UUID 時,寫的不一定是同一 page ,所以即
使延遲寫入 Storage ,也會產生很多 Random write 。
Ref: http://www.tocker.ca/2013/05/06/when-does-mysql-perform-io.html
109. 109/147
2015
High Concurrent Write?(Fragmentation)
《 MySQL 》
SELECT 時,若在 Memory 中, MySQL 會保持 ( 邏輯 )Ordered 。
但 PostgreSQL 不是,而且再加上它的 MVCC 設計, UPDATE/
DELETE 遺留下的 dead rows 會使得 Fragmentation 更嚴重。
若 SELECT 是從 Storage 取出時,因 MySQL 連續異動的 pages
一次寫入,甚至將請求合併,所以相近的資料放在同一 block 的
機率較高。且將 Storage 讀出放入 Memory 時, MySQL 仍會保
持 ( 邏輯 )Ordered 。
117. 117/147
2015
High Concurrent Write?(Hotspot:UPDATE)
Ref: http://zh.wikipedia.org/zh-tw/%E5%86%99%E5%85%A5%E6%94%BE%E5%A4%A7
SSD :寫入放大效應 + 垃圾回收
SSD 在寫入資料時,一定要抹除該區塊
的資料後才能寫入。而抹除的最小單位
是 512KB 。
即使這 512KB 中只有 1KB 的資料需要
更改,也要將整個區塊中的資料複製到
緩衝區,然後將資料抹除後寫回。
舉某些廠牌的測試數據,寫入資料的延
遲約為 0.2ms ,但抹除需要 2ms 。
125. 125/147
2015
High Concurrent Write?( 實驗數據 )
有沒有把 PostgreSQL VACUUM / Fragmentation 因子考慮進去?
有些從開始到結束,可能連一次 VACUUM 都沒執行過。
PostgreSQL 8.1 新增 autovacuum 功能。
PostgreSQL 9.0 預設啟動 autovacuum 。
MySQL 預設則是會在背景啟動 Purge 。
MySQL 預設使用 REPEATABLE-READ ;
PostgreSQL 預設使用 READ-COMMITTED 。
127. 127/147
2015
High Concurrent Write?(PURGE/VACUUM)
InnoDB 只有最新的資料會留在 table 中,舊資料會移到 rollback
segment 。意謂舊資料會移出空間並標示為未來可清除。
於是, Purge 得以擺脫 table 中的任何 deleted rows ,並專心從
rollback segment 中清理舊資料。
PostgreSQL 沒有類似 rollback segment 的設計,導致最終的清
理工作較昂貴。由於少了中心化的清理資訊, VACUUM 必須掃
描全表,以找出需要清理的舊資料。
Ref: http://rhaas.blogspot.tw/2011/02/mysql-vs-postgresql-part-2-vacuum-vs.html
132. 132/147
2015
High Concurrent Write?( 抖動 )
對於一個上線服務而言,穩定性遠大於平均效能。
意即效能防抖動,好預估,降低重要時刻發生在低點的機率。
PostgreSQL 的理念是把問題往後丟,不管是 Fragmentation 或
GC ,而這些多少也可能造成 SSD 的效能抖動。且 VACUUM
的性能會因表愈大愈慢 ( 執行期間也會影響其它工作 ) 。
MySQL 的理念則是提早解決問題,不管是 Ordered Index 、
Sequential 或 GC 。所以 MySQL 平均效能抖動有機會平緩,
但相對地,最佳高峰性能表現可能不及 PostgreSQL 。
不過這問題太複雜,需視參數及硬體的實際特性而論,最終
還是需以業務長期測試數據為準。不同業務有不同結果。
133. 133/147
2015
High Concurrent Write?(XID)
PostgreSQL 對每個事務都有分配一個 XID 。這些事務不是只指
BEGIN/COMMIT ,還包括 INSERT / UPDATE / DELETE 。每次
使用都會遞增。
但這個 XID 是 32bits ,最大支持 40 億個事務。當 XID 達到最大
值時,會從零再度開始。
突然間,所有事務變成未來所產生的,新事務都沒有辦法訪問這
些舊紀錄了。
Ref: https://devcenter.heroku.com/articles/postgresql-concurrency
134. 134/147
2015
High Concurrent Write?(XID)
《 Sentry 的災難與慘痛經驗 》
Sentry 遇到非常嚴重的災難,這一切都與 XID 的設計有關。
如果你的系統負載很高,而你又關閉 AUTOVACUUM 時。最終
你會因為 XID 達到上限值而造成 MVCC 不再正常運作,造成資
料遺失等各種麻煩問題。
PostgreSQL 官方網站聲明, XID 是 32 bits ,理論上限為四百
萬筆交易。為了避免這個問題,最好每二百萬筆交易時就要執
行 VACUUM 。
Ref: http://blog.getsentry.com/2015/07/23/transaction-id-wraparound-in-postgres.html
Ref: http://www.postgresql.org/docs/9.5/static/routine-vacuuming.html#VACUUM-FOR-WRAPAROUND