Redis的單線程模型指的是redis只使用一個線程來出來所有的命令式指令,但是不是意味著redis內部就只使用一個線程來處理所有的任務。都知道redis是一個客戶端-服務器的程序,那么redis就只有一個服務器,但是有多個客戶端,就像mysql一樣,mysql也是一個客戶端-服務器程序;
既然redis有多個客戶端,哪有人就會問了:會不會有線程安全問題呢?比如兩個客戶端都同時來修改redis的一個變量,比如都對變量進行+1操作,如果在java中,你使用兩個線程來同時修改一個變量,那就有可能是只加了一次,但是在redis中是不會有線程安全問題的,因為redis是單線程的,同一個時刻只能處理一個命令,其他的命令都得阻塞等待;
redis的單線程有好處也有壞處,好處就是不會出現線程安全問題,那我們日常寫代碼的時候為什么也不就使用單個線程呢?雖然單個線程不會出現線程安全問題,但是執行效率慢呀,但是慢是相比較多線程來說的,一個任務你可以分為10個線程來執行,那你只需要1/10的時間,但是你使用單線程就得多花費9倍的時間;單線程還有一個好處就是沒有線程競爭的開銷,所以會更快一點?有人就要質疑了,一會說redis快,一會說redis慢,那到底是快還是慢呢?這個快和慢都是相比較而言的;回歸主題,為什么redis的單線程模型快呢?
1、redis操作的是內存的,而mysql操作的是硬盤
? ? ? ? 都知道操作內存的速度肯定是比操作硬盤的速度快的,而且快的不是一點點;
2、redis的核心業務更簡單
? ? ? ? redis相比較mysql的增刪查改任務更簡單,mysql的數據的變化都會受到約束,所以就更慢了,redis的邏輯就更簡單了,通常來說redis插入鍵值對受到的約束非常少,受到的約束少就說明開銷更少,速度當然就跟快了;
3、redis的單線程模型沒有線程競爭開銷
? ? ? ? 線程競爭的開銷是很大的,涉及到鎖的創建,鎖的競爭,鎖的釋放等等,單線程模式避免了這樣的競爭開銷了,所以會更快;
4、redis處理網絡IO時,使用epoll這樣的IO多路復用機制
? ? ? ? Linux的IO多路復用機制其實有三種:select,poll,epoll,但是自從3.2開始,redis就堅持使用epoll這樣的機制處理網絡IO;
? ? ? ? 這個機制說來也簡單,redis服務器和客戶端是通過TCP連接通訊的,redis服務器的客戶端可以有很多個,服務器就會對每一個客戶端創建一個socket,而且redis只使用一個線程來管理多個socket。難道一個線程來管理多個socket不會處理不過來嗎?其實redis服務器是處理得過來的,雖然每一個客戶端都分配一個socket,但是不是每一個客戶端都每時每刻跟服務器保持通訊的,有可能連接一分鐘,實際通訊只有100ms,那么其他的時刻其他客戶端就可以和服務器進行通訊,通過這樣的機制就避免了一個socket分配一個線程的開銷,一個線程的開銷不大,但是多個socket,多個線程開銷就很大了;
舉個例子來說明一下:
如果只有我一個人來處理三件事情,事件A-買炒飯、事件B-買水餃、事件C-買煎餅果子,我有三種辦法來處理這件事情,第一種方法就是我先去買炒飯,等炒飯好了我再去買水餃,等水餃好了,我再去買煎餅果子,雖然這樣能完成任務,但是開銷很大,等的時間太長了;
第二種辦法就是:我去買炒飯,再安排兩個人,一個人去買水餃,一個人去買煎餅果子,雖然這樣總的時間變少了,但是使用了三個人,開銷也很大了,這里說的就是一個socket分配一個線程;
第三種辦法就是:我先去買炒飯,讓老板炒好了叫我一聲,在老板炒飯的過程中,我去買水餃,也讓老板水餃煮好了叫我一聲,這個過程我去買煎餅果子,然后我就等,誰好了就叫我一聲我去拿,這樣開銷最,而且效率最高,使用的機制就是epoll的事件通知/回調機制
epoll屬于是操作系統為程序員提供的一種機制,提供了一組API,內部的功能都是操作系統實現;
C\C++使用的及時poll,epoll這樣的機制,而Java使用的是NIO,是標準庫提供了一組類,底層也是封裝了epoll;