來源:http://blog.csdn.net/pingnning/archive/2009/10/24/4724377.aspx
"tyrant分析-總體設(shè)計"中已經(jīng)提到,slave起一個線程(do_slave)做主從復制,它和master建立tcp連接,發(fā)送請求命令和起始時間rts +1(上次的更新時間加1秒)給master,然后循環(huán)的從master那里接收一條條的記錄,更新自己db、ulog和rts file。do_slave是以1秒為頻率執(zhí)行的。(實際是等待一次do_slave執(zhí)行完畢后,再等待1秒,然后進入下一次的do_slave,依次循環(huán)。所以"以1秒為頻率執(zhí)行"的表達似乎并不準確。從下面可以看到一次do_slave有可能執(zhí)行較長時間)
主從復制是一個主、從交互的過程。本節(jié)依次描述協(xié)議細節(jié)、slave細節(jié)、master細節(jié)。
------------
協(xié)議細節(jié):
do_slave(slave) do_repl(master)
-------------------
| TTMAGICNUM|
| TTCMDREPL |
| ts (+1) |
| sid | send and recv (with timeout)
------------------- ------------------------>
-----------------
send and cnd wait | NOP |
<--------------------- -----------------
-----------------------
| TCULMAGICNUM |
| rts |
| rsid |
| rsiz |
content send | rsiz-content |
<--------------------- ------------------------
next content send
<---------------------
......
rsiz-content格式:
MAGIC + cmd + ksize + vsize + key + value
其中:
cmd: TTCMDPUT | TTCMDOUT | ...
ksize,vsize分別是本條記錄的key,value的長度;
slave就根據(jù)cmd和key-value對對db進行相應操作。
master的ulog由一條條獨立記錄組成,每條記錄有相同格式:
MAGIC + ts + sid + size + content
其中:
ts : 本條記錄對應的時間戳。slave請求時會帶上上次更新時間戳,master根據(jù)它們來判斷需要傳送哪些記錄給slave;
sid : server id. 唯一標識server。
size : 后面"content"長度
content格式即上面"rsiz-content"的格式,描述了一條key-value對以及對它做的操作命令。
--------------
do_slave流程:
打開rts文件(默認為ttserver.rts),讀取上次的rts(replication timestamp);
和master建立socket連接(參數(shù):-mhost,-mport),并設(shè)置socket選項:
SO_RCVTIMEO、SO_SNDTIMEO - 發(fā)送、接收超時設(shè)置為0.25秒
TCP_NODELAY - 禁止nagle算法
發(fā)送REPL請求(詳見協(xié)議細節(jié));
循環(huán):
用recv接收數(shù)據(jù);
解析接收數(shù)據(jù),根據(jù)數(shù)據(jù)中指定的命令(TTCMDPUT、TTCMDOUT等)更新db和slave自己的ulog;
用接收數(shù)據(jù)里的最新rts更新slave的rts文件;
最后關(guān)閉連接
解釋:
1、slave不能因偶然的網(wǎng)絡(luò)故障之類永遠阻塞在send或recv中,這樣的話更新就會永遠停滯了。所以它要設(shè)置發(fā)送和接收的超時。如果超時,則這次do_slave失敗,等待1秒后進行下一次。send | recv失敗時,它并不會用新的rts(可能壓根就沒請求到它)去更新自己的rts文件,所以下次還是會用舊的rts去請求,所以不會因do_slave失敗而導致slave數(shù)據(jù)不全。
2、禁止nagle算法是因為有小數(shù)據(jù)的命令包的交互,不能拖延。
3、請求只發(fā)送一次,但數(shù)據(jù)是一直循環(huán)接收的。循環(huán)失敗的條件是:recv失敗(或超時),收到SIGINT或SIGTERM,或是更新庫失敗或?qū)懳募〉龋?/p>
---------------
do_repl流程:
根據(jù)slave的請求ts找到合適的ulog文件(文件名使用數(shù)字編號,依次遞增),邏輯是:
從編號最大的文件依次往編號小的文件:(編號越大,ulog內(nèi)容越新,ts越大)
打開文件查看它的第一條記錄的ts,如果請求ts大于它,則該文件即為要找的ulog文件。
循環(huán)。當對端連接未關(guān)閉且沒收到SIGINT、SIGTERM信號時:
發(fā)送NOP(測試對端連接是否關(guān)閉);
pthread_cond_timedwait等待ulog更新信號,超時值為1秒;
循環(huán):
一次讀取一條日志記錄;
加上頭部(MAGIC,rts,rsid,rsiz。見"協(xié)議細節(jié)");
發(fā)送給slave。
當上面讀取日志失敗或發(fā)送失敗時,退出循環(huán)。
解釋:
1、ulog由一條條的記錄組成,每條記錄有相同格式: MAGIC + ts + sid + size + content
2、因為ulog文件有大小上限,所以寫滿一個后會寫下一個。按上面所說那樣,文件名用數(shù)字編號,依次遞增;
3、找合適ulog文件的邏輯。因為是按內(nèi)容從新到舊的順序(也即ts從大到小的順序)查看文件,所以最先找到的其中第一條記錄ts小于slave所請求ts的那個文件就是合適的文件;(該文件里ts會隨著一條條記錄慢慢增加,直到大于等于請求ts,這時就到了slave需要的數(shù)據(jù)處);
4、關(guān)于這兩層循環(huán)的邏輯。內(nèi)層循環(huán)一次發(fā)送一條記錄,它是希望盡可能多地發(fā)送記錄給slave,直到發(fā)送完所有記錄(意外發(fā)送故障不考慮下)。退出到外層邏輯時希望這時又有ulog更新,能繼續(xù)進行發(fā)送。這兩層循環(huán)的目的都是希望能盡可能長地維持與slave的一次連接,從而讓數(shù)據(jù)的同步更及時。
本文來自CSDN博客,轉(zhuǎn)載請標明出處:http://blog.csdn.net/pingnning/archive/2009/10/24/4724377.aspx