作者注:不過(guò)MySQL的InnoDB其實(shí)也是有寫(xiě)放大問(wèn)題的。InnoDB是以數(shù)據(jù)頁(yè)的形式組織數(shù)據(jù)的,Linux上默認(rèn)數(shù)據(jù)頁(yè)的大小是16K。這樣當(dāng)你更改了一條記錄時(shí),最終會(huì)把這條記錄所在的數(shù)據(jù)頁(yè)整頁(yè)刷回磁盤(pán),設(shè)想一下你可能只是改了一個(gè)小字段,也許只有4個(gè)字節(jié),可是最終卻會(huì)導(dǎo)致16K字節(jié)的寫(xiě)入。
另外,Postgres的這個(gè)設(shè)計(jì)也是有其好處的,它的第二索引直接指向元組的ctid,這樣在讀取數(shù)據(jù)時(shí)效率就非常高。相對(duì)應(yīng)地,通過(guò)MySQL的第二索引去讀數(shù)據(jù)會(huì)經(jīng)歷“第二索引——主鍵——數(shù)據(jù)”的過(guò)程,MySQL的讀效率不如Postgres。這是一個(gè)經(jīng)典的讀寫(xiě)性能權(quán)衡問(wèn)題,在此Evan沒(méi)有給出具體的數(shù)字讓我們體會(huì)他們的業(yè)務(wù)特征。
主從復(fù)制
Postgres的寫(xiě)放大問(wèn)題最終也反應(yīng)在了主從復(fù)制的日志傳輸上,變成了流量放大問(wèn)題。Postgres的主從復(fù)制傳輸?shù)氖荳AL日志,所以對(duì)于一條數(shù)據(jù)更新來(lái)說(shuō),它要傳輸新的數(shù)據(jù),還要傳輸這張表上每一條索引修改的日志。這樣的流量放大在同一機(jī)房?jī)?nèi)還稍可接受,但對(duì)于跨機(jī)房的情況,傳輸速度和價(jià)格等問(wèn)題讓Uber產(chǎn)生了顧慮。Uber是有跨機(jī)房從庫(kù)的,一方面是容災(zāi),另一方面是WAL的備份,以備有時(shí)需要靠它來(lái)搭建新的從庫(kù)。
MySQL的確沒(méi)有引起流量放大。MySQL的主從復(fù)制依靠的是Binlog,它只是記錄這條數(shù)據(jù)的修改,而不在乎這張表上到底有多少索引,所以可以認(rèn)為與Postgres相比,它的Binlog是一種對(duì)數(shù)據(jù)修改的“邏輯”描述。MySQL從庫(kù)上應(yīng)用Binlog日志時(shí),如果有第二索引涉及了改動(dòng)的字段,那就更新第二索引,否則第二索引壓根不需要修改。而且,MySQL有三種不同的 Binlog格式 ,包含了不同數(shù)量的信息來(lái)供使用者選擇:
Statement:只傳輸DML的SQL語(yǔ)句,如:UPDATE users SET birth_year=770 WHERE id = 4。這種模式日志量最小,但在某些場(chǎng)景下和對(duì)某些字段來(lái)說(shuō)容易出錯(cuò)。
Row:對(duì)于更改了的數(shù)據(jù),會(huì)把修改前和修改后的所有字段值都打印在Binlog中。這種模式日志量最大,但也最嚴(yán)謹(jǐn),越來(lái)越多的公司在轉(zhuǎn)向這種日志格式。很多日志解析工具更是只工作在這種模式下。
Mixed:上面兩種的結(jié)合體,MySQL會(huì)根據(jù)不同的語(yǔ)句來(lái)自行判斷。這種模式日志量居中。
數(shù)據(jù)損壞
Uber使用Postgres 9.2時(shí)曾經(jīng)因?yàn)橐粋€(gè)BUG導(dǎo)致了很大的故障。當(dāng)時(shí)由于硬件升級(jí)的原因他們做了主從切換,結(jié)果就引發(fā)了這個(gè)BUG導(dǎo)致各個(gè)從庫(kù)的數(shù)據(jù)全都亂掉了,而且還沒(méi)有辦法判斷哪個(gè)從庫(kù)的哪些數(shù)據(jù)是正確的或者亂的。最終他們確認(rèn)了新的主庫(kù)上的數(shù)據(jù)全部正確后,用新主庫(kù)的數(shù)據(jù)把所有從庫(kù)數(shù)據(jù)全覆蓋了一遍,才算過(guò)了這一關(guān)??墒且怀簧咭昱戮K,他們最后用的版本仍是Postgres 9.2,原因之一是不想再去踩別的版本的坑了。
作者注:以這個(gè)作為拋棄Postgres的理由就太容易引起爭(zhēng)議、令人質(zhì)疑Uber技術(shù)團(tuán)隊(duì)的技術(shù)水平了。在社區(qū)的口碑中,Postgres的穩(wěn)定性恰恰是高于MySQL的,如果因?yàn)楹ε屡錾螾ostgres的BUG而轉(zhuǎn)用MySQL,那……我們只好祝福Uber了。
從庫(kù)上的MVCC支持不好
Postgres的從庫(kù)上并沒(méi)有真正的MVCC,它的數(shù)據(jù)表空間、表空間文件內(nèi)容和主庫(kù)是完全一樣的,在從庫(kù)上就是依次應(yīng)用WAL??扇绻麖膸?kù)上有一個(gè)正在進(jìn)行中的事務(wù)的話(huà),它就會(huì)擋住WAL的應(yīng)用,從而導(dǎo)致看起來(lái)主從同步延遲很大。Postgres實(shí)現(xiàn)了一個(gè)機(jī)制,如果某個(gè)業(yè)務(wù)程序的事務(wù)擋住同步線(xiàn)程太久的話(huà),就直接將那個(gè)事務(wù)殺掉。所以如果在從庫(kù)上有一些比較大的事務(wù)在運(yùn)行的話(huà),你可能就會(huì)經(jīng)常看見(jiàn)莫名其妙的主從同步就延遲了,也會(huì)看見(jiàn)自己的操作運(yùn)行了一段時(shí)間就不知被誰(shuí)殺掉了。并不是每個(gè)程序員都很熟悉數(shù)據(jù)庫(kù)的底層工作機(jī)制,所以這些現(xiàn)象會(huì)讓大家覺(jué)得很詭異。
作者注:這一點(diǎn)的確是的。相比來(lái)說(shuō)對(duì)于這個(gè)Postgres的復(fù)制過(guò)程,MySQL的主從復(fù)制并不會(huì)殺死從庫(kù)上的事務(wù)。
Postgres數(shù)據(jù)庫(kù)的升級(jí)
Postgres的數(shù)據(jù)復(fù)制是物理級(jí)的,主從數(shù)據(jù)文件完全一致,所以不能支持不同版本之間的主從復(fù)制,比如主庫(kù)使用9.2從庫(kù)使用9.3,或者相反,等等。Uber最初使用的是Postgres 9.1,他們成功的升級(jí)成了9.2,但升級(jí)耗費(fèi)了相當(dāng)長(zhǎng)的時(shí)間,再加上后來(lái)業(yè)務(wù)爆發(fā)式增長(zhǎng),讓他們?cè)僖矝](méi)能安排下一次升級(jí)。而且Postgres直到9.4之后才有了工具 pglogical 來(lái)幫助減少升級(jí)耗時(shí),可是pglogical又不在Postgres主分支里,讓使用舊版本的人無(wú)所適從。