1. 單線程模型
現在開啟了三個redis-cli
客戶端同時執行命令。
- 客戶端1設置一個字符串鍵值對:
127.0.0.1:6379> set hello world
- 客戶端2對counter做自增操作:
127.0.0.1:6379> incr counter
- 客戶端3對counter做自增操作:
127.0.0.1:6379> incr counter
我們已經知道從客戶端發送的命令經歷了:發送命令、執行命令、返回結果三個階段,其中我們重點關注第2步。我們所謂的Redis是采用單線程模型執行命令的是指:雖然三個客戶端看起來是同時要求Redis去執行命令的,但微觀角度,這些命令還是采用線性方式去執行的,只是原則上命令的執行順序是不確定的,但一定不會有兩條命令被同步執行,如圖2 - 3、2 - 4、2 - 5所示,可以想象Redis內部只有一個服務窗口,多個客戶端按照它們達到的先后順序被排隊在窗口前,依次接受Redis的服務,所以兩條incr命令無論執行順序,結果一定是2,不會發生并發問題,這個就是Redis的單線程執行模型。
Redis的單線程模型
2. 為什么單線程還能這么快
通常來講,單線程處理能力要比多線程差,例如有10000公斤貨物,每輛車的運載能力是每次200公斤,那么要50次才能完成;但是如果有50輛車,只要安排合理,只需要依次就可以完成任務。那么為什么Redis使用單線程模型會達到每秒萬級別的處理能力呢?可以將其歸結為三點:
- 純內存訪問:Redis將所有數據放在內存中,內存的響應時長大為約100納秒,這是Redis達到每秒萬級別訪問的重要基礎。
- 非阻塞IO:Redis使用epoll作為I/O多路復用技術的實現,再加上Redis自身的事件處理模型將epoll中的連接、讀寫、關閉都轉換為事件,不在網絡I/O上浪費過多的時間,如圖2 - 6所示。
- 單線程避免了線程切換和競態產生的消耗:單線程可以簡化數據結構和算法的實現,讓程序模型更簡單;其次多線程避免了在線程競爭同一份共享數據時帶來的切換和等待消耗。
雖然單線程給Redis帶來很多好處,但還是有一個致命的問題:對于單個命令的執行時間都是有要求的。如果某個命令執行過長,會導致其他命令全部處于等待隊列中,遲遲等不到響應,造成客戶端的阻塞,對于Redis這種高性能的服務來說是非常嚴重的,所以Redis是面向快速執行場景的數據庫。
鍵的過期機制
一個Redis中可能會同時存在很多很多的key,這些key中有很大一部分都有過期時間,此時Redis如何 GET key是否過期?
Redis的整體策略:
- 定期刪除
- 每取一部分驗證過期時間
- 惰性刪除
- 發送DEL指令后,服務器端并不會立即刪除,如果再訪問已標記刪除的key,才會刪除,同時返回nil.
Redis中并沒有采取定時器的方式來實現過期key刪除
- 若有多個key過期,也可以通過一個定時器來高效/節省CPU的前提下處理多個key,(優先級隊列,時間輪)
- 若是基于定時器實現,就要引入多線程