(看完這個文章,想對源碼有進一步瞭解,可以到我的git裏面看註釋,或者郵件我討論。附上地址:https://github.com/PhoneLi/beansdb)
在開始看這篇文章之前,建議花幾分鐘看下beansdb的結構分析。
beansdb的結構分析:http://blog.csdn.net/l402398703/article/details/19371515
簡述:
1:數據存儲實現。bitcask儲存模型,HashTree 實現數據索引。
2:協議實現。使用memcached的代碼;修改redis的AE事件異步驅動庫;多worker線程, leader/follower 模式;
(leader/follower模式介紹:http://www.habadog.com/2012/01/11/leader-follower-thread-model
bitcask模型介紹:http://blog.nosqlfan.com/html/955.html)
(由上圖看出,數據冗餘直接在服務server之間實現。採用3爲冗餘數量。)
--------------------------==============水平分割線=================------------------------------
運行流程:
1:beansdb.c的main函數啓動;
2:getopt處理各參數;
3:一些資源的初始化,例如 getrlimit() , item_init(); stats_init(); conn_init();(資源參數很多,建議不要花事件耗在這裏。等看完後面的函數代碼,便會自然理解參數的含義。);
4:hs_open函數 {
打開數據目錄(沒有則創建),hstore的初始化。函數結尾調用parallelize函數;
parallelize函數:啓動一些線程運行scan_thread,每個scan_thread 運行對應的bc_scan。
bc_scan:加載目錄裏面的 XXX.[data|hint.qlz|htree]文件。(ht_open 加載HTREE_FILE 文件,sanHintFile 加載 hintpath文件)
};
5:server_socket:socket的初始化。
6:loop_run:{
創建子線程,各線程進入worker_main函數。
worker_main裏面實現了leader/follwer模式。當有conn的網絡事件,調用driver_machine函數。
driver_machine:{
(c-state default is conn_listening;)
switch:
conn_listening: accept client socket ,and new std conn.
conn_read:{
try_read_network讀取命令,try_read_command處理命令。
如果是set命令,process_update_command函數處理,則將狀態轉入conn_nread,從而接受二進制數據;
如果是get命令,process_get_command函數處理,則從beansdb中提取key對應的value,並將狀態轉入conn_mwrite,從而發送查詢結果數據;
如果是其他的命令,例如delete,version,stats等,由於迴應消息都比較簡單,都是直接調用out_string,跳轉到conn_write狀態。
}
conn_nread:conn_nread標誌着正在從客戶端讀取二進制數據。這種情況發生在客戶端發送set命令的時候。如果全部二進制數據讀取完成,則將
其存儲到beansdb中,並調用 out_string將存儲結果返回給客戶。
conn_write. 當服務器需要發送一些簡單的迴應的時候(out_string函數),會將連接的狀態設置爲conn_write. conn_write狀態的處理機制就是
首先調用add_iov來將對應的迴應消息添 加到msghdr中的iov中,然後調用transmit函數將未發送的數據發送出去,最後全部數據傳輸完成時,將狀態置爲conn_read。
conn_mwrite. 這種情況一般是發生在需要向用戶返回大量的二進制查詢結果時。處理動作也是調用transmit將緩存的數據發送出去。
conn_closing. 對應的動作就是清除連接。
}
}
--------------------------==============水平分割線=================------------------------------
hint.c , hint.h :hash init。重建hash tree時的根據
bitcask.c , bitcask.h : bitcask 模型。定義BitCask的基本操作
record.c , record.h : 用來實現對datafile和hintfile的操作
--------------------------==============水平分割線=================------------------------------
總結:
1:Key-Value 數據庫(CAP理論)
分佈式的,伸縮性比較好(P):性能和容量
最終一致的(C):可能出現短時間內的數據不一致
高可用的(A):部分節點出現故障不影響服務
beansdb的數據路由和服務端是分開的。這樣的設計,也是一個好的亮點。對於CAP理論,作者根據使用場景,採用最終一致性的實現:多機冗餘(N=3) , 同步寫(W=1), 依次讀(R=1)
2:典型用法
圖片文件,小媒體文件(mp3); 大文本字段(通常> 1kb); profile , properties
3:網上有關beansdb源碼分析的文章不多。而且那些文章對應的beansdb的版本不是最新的。
所以。如果想了解最新版本beansdb(0.6.0)的源碼,可以看我的git。裏面有一定數量的註釋。日後有時間,我會規範整理的。
我的git項目地址:https://github.com/PhoneLi/beansdb
--------------------------==============水平分割線=================------------------------------
美圖欣賞:
舉例一個查找流程:
hs_get先對key做hash,決定value是在哪個bitcast。然後調用bitcast模塊的bc_get
bc_get先調用htree模塊的ht_get,找到對應的Item。
Item中有版本信息ver,位置信息pos。pos是bucket的id和偏移量拼成的一個uint32,於是得到了bucket和offset然後就可以讀出數據了。
(讀取的行爲默認是隨機選一個節點讀到一個有效的版本就返回。讀不到的話會重試其它節點。 )