單執行緒的redis為什麼這麼快
ofollow,noindex">為什麼說Redis是單執行緒的並且這麼快
其它開源軟體採用的模型
Nginx:多程序單執行緒模型
Memcached:單程序多執行緒模型
Redis:單程序單執行緒
單執行緒的redis為什麼這麼快
主要是以下三點
(一)純記憶體操作
資料存在記憶體中,類似於HashMap,HashMap的優勢就是查詢和操作的時間複雜度都是O(1);
(二)單執行緒操作 ,避免了頻繁的上下文切換
避免了不必要的上下文切換和競爭條件,也不存在多程序或者多執行緒導致的切換而消耗 CPU,不用去考慮各種鎖的問題 ,不存在加鎖釋放鎖操作,沒有因為可能出現死鎖而導致的效能消耗;
多執行緒處理可能涉及到鎖
多執行緒處理會涉及到執行緒切換而消耗CPU
多執行緒對同一個Key操作時,Redis服務是根據先到先作的原則,其他排隊(可設定為直接丟棄),因為是單執行緒。
修改預設的超時時間,預設2秒。但是大部份的操作都在30ms以內。
(三)採用了非阻塞I/O多路複用機制
內部實現採用epoll,採用了epoll+自己實現的簡單的事件框架。epoll中的讀、寫、關閉、連線都轉化成了事件,然後利用epoll的多路複用特性,絕不在io上浪費一點時間
(四)資料結構簡單,對資料操作也簡單 ,Redis中的資料結構是專門進行設計的;
我們使用單執行緒的方式是無法發揮多核CPU 效能,不過我們可以通過在單機開多個Redis 例項來完善!
警告:這裡 我們一直在強調的單執行緒,只是在處理我們的網路請求的時候只有一個執行緒來處理 ,一個正式的Redis Server執行的時候肯定是不止一個執行緒的,這裡需要大家明確的注意一下!
例如Redis進行持久化的時候會以子程序或者子執行緒的方式執行(具體是子執行緒還是子程序待讀者深入研究)
Redis為什麼是單執行緒的?
因為CPU不是Redis的瓶頸。 Redis的瓶頸最有可能是機器記憶體或者網路頻寬。
既然單執行緒容易實現,而且CPU不會成為瓶頸,那就順理成章地採用單執行緒的方案了。
關於redis的效能,官方網站也有,普通筆記本輕鬆處理每秒幾十萬的請求,參見:How fast is Redis?
如果萬一CPU成為你的Redis瓶頸了,或者,你就是不想讓伺服器其他核閒置,那怎麼辦?
那也很簡單,你 多起幾個Redis程序就好了 。Redis是keyvalue資料庫,又不是關係資料庫,資料之間沒有約束。只要客戶端分清哪些key放在哪個Redis程序上就可以了。 redis-cluster 可以幫你做的更好。
單執行緒可以處理高併發請求嗎?
當然可以了,Redis都實現了。有一點概念需要澄清,併發並不是並行。
(相關概念:併發性I/O流,意味著能夠讓一個計算單元來處理來自多個客戶端的流請求。並行性,意味著伺服器能夠同時執行幾個事情,具有多個計算單元)
單執行緒處理的缺點?
無法發揮多核CPU效能 ,不過 可以通過在單機開多個Redis例項來完善
單程序單執行緒的Redis如何能夠高併發
採用 多路 I/O 複用技術 可以讓單個執行緒高效的處理多個連線請求(儘量減少網路IO的時間消耗)
Redis不存線上程安全問題?
Redis採用了執行緒封閉的方式,把任務封閉在一個執行緒,自然避免了執行緒安全問題,不過對於需要依賴多個redis操作的複合操作來說,依然需要鎖,而且有可能是分散式鎖
redis的一些其他特點:
(1)Redis是單程序單執行緒的
redis利用佇列技術將併發訪問變為序列訪問,消除了傳統資料庫序列控制的開銷
(2)讀寫分離模型
通過增加Slave DB的數量,讀的效能可以線性增長。為了避免Master DB的單點故障,叢集一般都會採用兩臺Master DB做雙機熱備,所以整個叢集的讀和寫的可用性都非常高。
讀寫分離架構的缺陷在於,不管是Master還是Slave,每個節點都必須儲存完整的資料,如果在資料量很大的情況下,叢集的擴充套件能力還是受限於單個節點的儲存能力,而且對於Write-intensive型別的應用,讀寫分離架構並不適合。
(3)資料分片模型
為了解決讀寫分離模型的缺陷,可以將資料分片模型應用進來。
可以將每個節點看成都是獨立的master,然後通過業務實現資料分片。
結合上面兩種模型,可以將每個master設計成由一個master和多個slave組成的模型。
(4)Redis的回收策略
volatile-lru:從已設定過期時間的資料集(server.db[i].expires)中挑選最近最少使用的資料淘汰
volatile-ttl:從已設定過期時間的資料集(server.db[i].expires)中挑選將要過期的資料淘汰
volatile-random:從已設定過期時間的資料集(server.db[i].expires)中任意選擇資料淘汰
allkeys-lru:從資料集(server.db[i].dict)中挑選最近最少使用的資料淘汰
allkeys-random:從資料集(server.db[i].dict)中任意選擇資料淘汰
no-enviction(驅逐):禁止驅逐資料
注意這裡的6種機制,volatile和allkeys規定了是對已設定過期時間的資料集淘汰資料還是從全部資料集淘汰資料,後面的lru、ttl以及random是三種不同的淘汰策略,再加上一種no-enviction永不回收的策略。
使用策略規則:
1、如果資料呈現冪律分佈,也就是一部分資料訪問頻率高,一部分資料訪問頻率低,則使用allkeys-lru
2、如果資料呈現平等分佈,也就是所有的資料訪問頻率都相同,則使用allkeys-random
redis常見效能問題和解決方案:
(1) Master最好不要做任何持久化工作 ,如RDB記憶體快照和AOF日誌檔案
(Master寫記憶體快照,save命令排程rdbSave函式,會阻塞主執行緒的工作,當快照比較大時對效能影響是非常大的,會間斷性暫停服務,所以Master最好不要寫記憶體快照;
AOF檔案過大會影響Master重啟的恢復速度)
(2) 如果資料比較重要,某個Slave開啟AOF備份資料,策略設定為每秒同步一次
(3) 為了主從複製的速度和連線的穩定性, Master和Slave最好在同一個區域網內
(4) 儘量 避免在壓力很大的主庫上增加從庫
(5) 主從複製不要用圖狀結構,用單向連結串列結構更為穩定,即:Master <- Slave1 <- Slave2 <- Slave3...
這樣的結構方便解決單點故障問題,實現Slave對Master的替換。如果Master掛了,可以立刻啟用Slave1做Master,其他不變。
1. Redis服務端是個單執行緒的架構,不同的Client雖然看似可以同時保持連線,但發出去的命令是序列化執行的,這在通常的資料庫理論下是最高級別的隔離(serialize)
2. 用MULTI/EXEC 來把多個命令組裝成一次傳送,達到原子性
3. 用WATCH提供的樂觀鎖功能,在你EXEC的那一刻,如果被WATCH的鍵發生過改動,則MULTI到EXEC之間的指令全部不執行,不需要rollback
4. 其他回答中提到的DISCARD指令只是用來撤銷EXEC之前被暫存的指令,並不是回滾