Tommy 碎碎念

Tommy Wu's blog

« 上一篇 | 下一篇 »

PostgreSQL Standby Server
post by tommy @ 12 八月, 2007 09:51

隨著資料量的增加, 通常資料庫的重要性也就跟著增加, 這時, 我們要如何確保資料庫可以正常運作呢? 使用比較好的機器? 使用高級的 RAID? 把資料備份好? 這些做的再好, 也都還是有風險. 也就是, 只要是硬體, 就可能會壞. 機器就是那一台, 出了問題, 就得找台機器, 重新開始安裝, 然後由備份的資料還原. 資料一多, 這些動作可以短到幾小時, 也可能是兩三天才能處理好. 也就是, 在這段時間內, 如果你的公司運作, 對資料庫有極大的依賴性, 可能這段時間的運作完全停擺, 損失不可說不小.

一般的資料庫系統, 為了避免這樣的情形發生, 除了應有的備份動作外, 通常也會複製的機制, 可以讓交易存到別的機器上頭, 只要有需要, 可以很快的切換到另外一台有相同資料的機器上頭.

PostgreSQL, 依據這幾年使用的經驗來看, "複製" (replication) 這件事情, 實在是做的不怎麼樣. 以往多數的方式, 都是利用 trigger 的方式來處理, 但是, 這樣的做法, 就我來看, 只能做到已存在 table 內資料的複製, 如果有新的 table, 或 schema 有變動, 都可能導致複製的結果出了問題, 等真的要用那些資料時, 會不會和原本的資料之間有了出入呢?

上一篇文章中說過, 雖然 7.x 的 PostgreSQL 有 WAL 的功能, 不過只能拿來本身 process crash 時做 recovery 使用. 而在 8.0 之後, 終於可以做到 online backup, 也就是我們可以弄出一份基本的備份, 加上後續的 WAL 交易檔, 就可以還原出原本的資料庫. 換個方向來看, 這不也是一種 replication? 而且,  不再是依賴 trigger 來處理了, 而是完完整整資料庫的複製了. 不管你對資料庫做了什麼動作, 都會在 WAL 交易檔中, 都可以在 recovery 時重新把這些動作再做一次. 只要我們把存好這個備份與之後的 WAL 交易檔, 就表示我們可以複製出一個完整的資料庫了.

在前面提到, 如果我們要讓資料庫出問題停擺的時間變短. 只要我們能事先準備一台機器, 把上頭的備份放到該機器上頭. 如果原本的機器出問題時, 我們只要把這些 WAL 交易檔都 recovery 一次, 就可以切換到這台備份的主機. 這也就是一般 standby server 的概念.

所以, 既然 PostgreSQL 在 8.0 之後, 可以利用 backup 與 WAL 來複製原本的資料庫了, 是否也表示可以做出 standby server 了呢?  只要我們還原時的 recovery.conf 中的 restore_command 中, 把原本簡單的 cp 指令, 寫成一個可以等待的 script, 等收到下一個 WAL 檔案時, 再做 cp 的動作. 這樣子, 這個還原動作就不會結束, 這樣子, 看起來就成了一個 standby server 了.

不過, 在 8.2 發表之前, 上頭的概念在沒問題時是可以運作的, 不過... 如果我們那台機器有需要關機時, 會發現在 restore_command 被強迫結束時,  PostgreSQL 會認為沒有新的  WAL 了 , 就結束 recovery 的動作, 然後把資料庫變成一個正常的資料庫了, 之後就再也不能匯入新的 WAL 檔案了.

這個問題在 8.2 中獲得的解決. 只要 restore_command 的結束, 是經由 signal 觸發來結束的, 或傳回值大於 125 時, 就會把 recovery 的動作中斷並結束程式, 而不會把資料庫的狀態改變. 下一次執行時, 仍然是在 recovery 的狀態, 也可以接受後續的 WAL 檔案. 這也就表示在 PostgreSQL 8.2 中, 我們就可以做到完整的 standby server 的功能了.

參考 8.2 中有關 standby server 的說明, 先弄出一個備份出來, 然後把 WAL 存到某個路徑中, 這時, 在 standby 的機器上把該備份放上去, 做出一個 recovery.conf, 上頭的 restore_command 改成一個可以等待的 script, 然後利用別的方法 (mount? rsync? ftp?), 把之後產生的 WAL 檔案複製過來使用. 這樣子, 就變成一個 standby server 了.

這個 script, 我們是用 php 寫了一個簡單的版本 pgstandby.php:

#!/usr/bin/php -Cq
<?php
 
$trigger_filename = 'standby.stop';
$sleep_time = 5;
 
$argc = $_SERVER['argc'];
$argv = $_SERVER['argv'];
 
if ($argc != 3) {
writelog("Syntax ".$argv[0]." sourcefile targetfile");
// we need to use exit code > 125, to tell postgresql just exit, don't change the role to normal server
exit(127);
}
 
$source = $argv[1];
$target = $argv[2];
$filename = basename($source);
 
if (strpos($filename, '.history') !== false) {
// ignore file *.history
if (!file_exists($source)) {
writelog("skip non-exists $source");
exit(0);
}
}
else {
$trigger_file = dirname($source).'/'.$trigger_filename;
// we should wait until the WAL generated
if (!file_exists($source)) {
writelog("waiting for $source");
while (1) {
clearstatcache();
if (file_exists($source)) break;
// wait for 5 seconds...
if (file_exists($trigger_file)) {
writelog("stop standby, change the role to normal server");
// return non-zero if we want to change standby to normal server
exit(1);
}
sleep($sleep_time);
//exit(127);
}
}
}
 
writelog("copy $source to $target");
@unlink($target);
copy($source, $target);
// return 0, we have new WAL here...
exit(0);
 
function writelog($buf)
{
echo $buf."\n";
$fp = fopen('/var/log/postgresql/pgstandby-'.strftime('%Y%m%d').'.log', 'at');
if ($fp) {
fputs($fp, strftime('%D %T [').posix_getpid().'] '.$buf."\n");
fclose($fp);
}
return;
}
 
?>

只要把 restore_command 改成 'pgstandby.php /db/pgarchive/archivelog/%f %p', 就可以做到 standby 的功能了.

要注意的是,  在 replay 某個  WAL 交易檔之後, 並不能馬上刪除, 因為如果中斷 recovery 的動作時, 通常下一次做 recovery 時, 會回溯之前好幾個 WAL 檔, 所以建議這些 WAL 檔案要保留一段時間後再刪除.

當然, 你也可以不用上頭的 script, 自己寫一個類似的 bash script 也是可以. 或者, 可以到官方的網站, 抓目前 8.3 開發版本中  contrib 裡頭的 pg_standby 程式回來用.

這樣子, 我們就完成了一個 PostgreSQL 的 standby server 了.

目前, PostgreSQL 的 standby server 就只是單純的 standby, 但是在官方的 todo list 中, 是有一個把 standby server 加上可以做 readonly query 的功能, 不過.... 也不知道多久才會加上這個功能. 因為在該 todo list 中, 有另一個我想要的功能 pg_upgrade (可以讓升級不再是使用  pg_dump 來處理, 直接由 binary file 升級), 好像由 2004 年到現在, 也沒什麼進展.

Del.icio.us Furl HEMiDEMi Technorati MyShare
迴響
暱稱:
標題:
個人網頁:
電子郵件:
authimage

迴響

  

Bad Behavior 已經阻擋了 48 個過去 7 天試圖闖關的垃圾迴響與引用。
Power by LifeType. Template design by JamesHuang. Valid XHTML and CSS