Redis多執行緒原理詳解
阿新 • • 發佈:2020-11-25
本篇文章為你解答以下問題:
在多執行緒模式下,readQueryFromClient會將client資訊加入server.clients_pending_read佇列,listAddNodeHead(server.clients_pending_read,c);
主執行緒會將server.clients_pending_read中的資料分發到子執行緒的佇列(io_threads_list)中,子執行緒會呼叫readQueryFromClient就行引數解析,主執行緒分發完任務後,會執行具體的操作資料庫的命令,這塊是單執行緒
如果引數解析完成會在client->flags中加一個標記CLIENT_PENDING_COMMAND,在主執行緒中先判斷client->flags & CLIENT_PENDING_COMMAND > 0,說明引數解析完成,才會呼叫processCommandAndResetClient,之前還擔心如果子執行緒還在做引數解析,主執行緒就開始執行命令難道不會有問題嗎?現在一切都清楚了
讀是多次讀:socket讀緩衝區有資料,epoll就會一直觸發讀事件,所以讀可能是多次的
寫是一次寫:往socket寫資料是在子執行緒中執行的,直接迴圈直到資料寫完位置,就算某個執行緒阻塞了,也不會像單執行緒那樣導致所有任務都阻塞
- 0:redis單執行緒的實現流程是怎樣的?
- 1:redis哪些地方用到了多執行緒,哪些地方是單執行緒?
- 2:redis多執行緒是怎麼實現的?
- 3:redis多執行緒是怎麼做到無鎖的?
main -> initServer -> acceptTcpHandler -> anetTcpAccept -> anetGenericAccept -> accept(獲取到socket連線控制代碼) connCreateAcceptedSocket -> connCreateSocket -> 建立一個connection物件 acceptCommonHandler -> createClient建立client連線物件 -> connSetReadHandler -> aeCreateFileEvent -> readQueryFromClient main -> aeMain -> aeProcessEvents -> aeApiPoll(獲取可讀寫的socket) -> readQueryFromClient(如果可讀) -> processInputBuffer -> processCommandAndResetClient(多執行緒下這個方法在當前流程下不會執行,而由主執行緒執行)
main -> aeMain -> aeProcessEvents -> beforeSleep -> handleClientsWithPendingReadsUsingThreads -> processCommandAndResetClient -> processCommand -> call
執行完相關命令後,就是將結果返回給client,回覆client是一組函式,我們以addReply為例,說一下執行流程,執行addReply還是單執行緒的,將client資訊插入全域性佇列server.clients_pending_write。 addReply -> prepareClientToWrite -> clientInstallWriteHandler -> listAddNodeHead(server.clients_pending_write,c) 在主執行緒中將server.clients_pending_write中的資料以輪訓的方式分發到多個子執行緒中 beforeSleep -> handleClientsWithPendingWritesUsingThreads -> 將server.clients_pending_write中的資料以輪訓的方式分發到多個執行緒的佇列中io_threads_list list *io_threads_list[IO_THREADS_MAX_NUM];是陣列雙向連結串列,一個執行緒對應其中一個佇列 子執行緒將client中的資料發給客戶端,所以是多執行緒 server.c -> main -> initThreadedIO(啟動一定數量的執行緒) -> IOThreadMain(執行緒執行的方法) -> writeToClient -> connWrite -> connSocketWrite
網路操作對應的一些方法,所有connection物件的type欄位都是指向CT_Socket
ConnectionType CT_Socket = { .ae_handler = connSocketEventHandler, .close = connSocketClose, .write = connSocketWrite, .read = connSocketRead, .accept = connSocketAccept, .connect = connSocketConnect, .set_write_handler = connSocketSetWriteHandler, .set_read_handler = connSocketSetReadHandler, .get_last_error = connSocketGetLastError, .blocking_connect = connSocketBlockingConnect, .sync_write = connSocketSyncWrite, .sync_read = connSocketSyncRead, .sync_readline = connSocketSyncReadLine };