今天給大家介紹的是KeyDB,KeyDB專案是從redis fork出來的分支,眾所周知redis是一個單執行緒的kv記憶體存盤系統,而KeyDB在100%兼容redis API的情況下將redis改造成多執行緒,
執行緒模型
KeyDB將redis原來的主執行緒拆分成了主執行緒和worker執行緒,每個worker執行緒都是io執行緒,負責監聽埠,accept請求,讀取資料和決議協議,如圖所示:
KeyDB使用了SO_REUSEPORT特性,多個執行緒可以系結監聽同個埠,
每個worker執行緒做了cpu綁核,讀取資料也使用了SO_INCOMING_CPU特性,指定cpu接收資料,決議協議之后每個執行緒都會去操作記憶體中的資料,由一把全域鎖來控制多執行緒訪問記憶體資料,主執行緒其實也是一個worker執行緒,包括了worker執行緒的作業內容,同時也包括只有主執行緒才可以完成的作業內容,在worker執行緒陣列中下標為0的就是主執行緒,
主執行緒的主要作業在實作serverCron,包括:
-
處理統計
-
客戶端鏈接管理
-
db資料的resize和reshard
-
處理aof
-
replication主備同步
-
cluster模式下的任務
鏈接管理
在redis中所有鏈接管理都是在一個執行緒中完成的,在KeyDB的設計中,每個worker執行緒負責一組鏈接,所有的鏈接插入到本執行緒的鏈接串列中維護,鏈接的產生、作業、銷毀必須在同個執行緒中,每個鏈接新增一個欄位
int iel; /* the event loop index we're registered with */
用來表示鏈接屬于哪個執行緒接管,
KeyDB維護了三個關鍵的資料結構做鏈接管理:
-
clients_pending_write:執行緒專屬的鏈表,維護同步給客戶鏈接發送資料的佇列
-
clients_pending_asyncwrite:執行緒專屬的鏈表,維護異步給客戶鏈接發送資料的佇列
-
clients_to_close:全域鏈表,維護需要異步關閉的客戶鏈接
分成同步和異步兩個佇列,是因為redis有些聯動api,比如pub/sub,pub之后需要給sub的客戶端發送訊息,pub執行的執行緒和sub的客戶端所在執行緒不是同一個執行緒,為了處理這種情況,KeyDB將需要給非本執行緒的客戶端發送資料維護在異步佇列中,
同步發送的邏輯比較簡單,都是在本執行緒中完成,以下圖來說明如何同步給客戶端發送資料:
如上文所提到的,一個鏈接的創建、接收資料、發送資料、釋放鏈接都必須在同個執行緒執行,異步發送涉及到兩個執行緒之間的互動,KeyDB通過管道在兩個執行緒中傳遞訊息:
int fdCmdWrite; //寫管道
int fdCmdRead; //讀管道
本地執行緒需要異步發送資料時,先檢查client是否屬于本地執行緒,非本地執行緒獲取到client專屬的執行緒ID,之后給專屬的執行緒管到發送AE_ASYNC_OP::CreateFileEvent的操作,要求添加寫socket事件,專屬執行緒在處理管道訊息時將對應的請求添加到寫事件中,如圖所示:
redis有些關閉客戶端的請求并非完全是在鏈接所在的執行緒執行關閉,所以在這里維護了一個全域的異步關閉鏈表,
鎖機制
KeyDB實作了一套類似spinlock的鎖機制,稱之為fastlock,
fastlock的主要資料結構有:
struct ticket
{
uint16_t m_active; //解鎖+1
uint16_t m_avail; //加鎖+1
};
struct fastlock
{
volatile struct ticket m_ticket;
volatile int m_pidOwner; //當前解鎖的執行緒id
volatile int m_depth; //當前執行緒重復加鎖的次數
};
使用原子操作atomic_load_2,atomic_fetch_add,__atomic_compare_exchange來通過比較m_active=m_avail判斷是否可以獲取鎖,
fastlock提供了兩種獲取鎖的方式:
-
try_lock:一次獲取失敗,直接回傳
-
lock:忙等,每1024 * 1024次忙等后使用sched_yield 主動交出cpu,挪到cpu的任務末尾等待執行,
在KeyDB中將try_lock和事件結合起來,來避免忙等的情況發生,每個客戶端有一個專屬的lock,在讀取客戶端資料之前會先嘗試加鎖,如果失敗,則退出,因為資料還未讀取,所以在下個epoll_wait處理事件回圈中可以再次處理,
Active-Replica
KeyDB實作了多活的機制,每個replica可設定成可寫非只讀,replica之間互相同步資料,主要特性有:
-
每個replica有個uuid標志,用來去除環形復制
-
新增加rreplay API,將增量命令打包成rreplay命令,帶上本地的uuid
-
key,value加上時間戳版本號,作為沖突校驗,如果本地有相同的key且時間戳版本號大于同步過來的資料,新寫入失敗,采用當前時間戳向左移20位,再加上后44位自增的方式來獲取key的時間戳版本號,
小編還為大家準備了一套最新的零基礎學前端資料,需要的無償自取
1、點贊+評論(勾選“同時轉發”),沒有點贊也可以獲取哦,看我的文章的都是朋友
2、給個關注不迷路,獲取方式:https://shimo.im/docs/JPRdJDVHxKJ3yQGY/ ,部分資料如下
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/36901.html
標籤:JavaScript
