redis单线程原理___Redis为何那么快-----底层原理浅析
redis單線程原理
redis單線程問題
單線程指的是網絡請求模塊使用了一個線程(所以不需考慮并發安全性),即一個線程處理所有網絡請求,其他模塊仍用了多個線程。
1. 為什么說redis能夠快速執行
(1) 絕大部分請求是純粹的內存操作(非常快速)
(2) 采用單線程,避免了不必要的上下文切換和競爭條件
(3) 非阻塞IO - IO多路復用
2. redis的內部實現
內部實現采用epoll,采用了epoll+自己實現的簡單的事件框架。epoll中的讀、寫、關閉、連接都轉化成了事件,然后利用epoll的多路復用特性,絕不在io上浪費一點時間 這3個條件不是相互獨立的,特別是第一條,如果請求都是耗時的,采用單線程吞吐量及性能可想而知了。應該說redis為特殊的場景選擇了合適的技術方案。
3. Redis關于線程安全問題
redis實際上是采用了線程封閉的觀念,把任務封閉在一個線程,自然避免了線程安全問題,不過對于需要依賴多個redis操作的復合操作來說,依然需要鎖,而且有可能是分布式鎖。
4. IO多路復用
參考:https://www.zhihu.com/question/32163005
要弄清問題先要知道問題的出現原因
原因:
由于進程的執行過程是線性的(也就是順序執行),當我們調用低速系統I/O(read,write,accept等等),進程可能阻塞,此時進程就阻塞在這個調用上,不能執行其他操作.阻塞很正常.
接下來考慮這么一個問題:一個服務器進程和一個客戶端進程通信,服務器端read(sockfd1,bud,bufsize),此時客戶端進程沒有發送數據,那么read(阻塞調用)將阻塞,直到客戶端調用write(sockfd,but,size)發來數據.在一個客戶和服務器通信時這沒什么問題;
當多個客戶與服務器通信時當多個客戶與服務器通信時,若服務器阻塞于其中一個客戶sockfd1,當另一個客戶的數據到達套接字sockfd2時,服務器不能處理,仍然阻塞在read(sockfd1,…)上;此時問題就出現了,不能及時處理另一個客戶的服務,咋么辦?
I/O多路復用來解決!
I/O多路復用:
繼續上面的問題,有多個客戶連接,sockfd1,sockfd2,sockfd3…sockfdn同時監聽這n個客戶,當其中有一個發來消息時就從select的阻塞中返回,然后就調用read讀取收到消息的sockfd,然后又循環回select阻塞;這樣就不會因為阻塞在其中一個上而不能處理另一個客戶的消息
“I/O多路復用”的英文是“I/O multiplexing”,可以百度一下multiplexing,就能得到這個圖:
Q:
那這樣子,在讀取socket1的數據時,如果其它socket有數據來,那么也要等到socket1讀取完了才能繼續讀取其它socket的數據吧。那不是也阻塞住了嗎?而且讀取到的數據也要開啟線程處理吧,那這和多線程IO有什么區別呢?
A:
1.CPU本來就是線性的不論什么都需要順序處理并行只能是多核CPU
2.io多路復用本來就是用來解決對多個I/O監聽時,一個I/O阻塞影響其他I/O的問題,跟多線程沒關系.
3.跟多線程相比較,線程切換需要切換到內核進行線程切換,需要消耗時間和資源.而I/O多路復用不需要切換線/進程,效率相對較高,特別是對高并發的應用nginx就是用I/O多路復用,故而性能極佳.但多線程編程邏輯和處理上比I/O多路復用簡單.而I/O多路復用處理起來較為復雜.
5. 使用Redis有哪些好處?
(1) 速度快,因為數據存在內存中,類似于HashMap,HashMap的優勢就是查找和操作的時間復雜度都是O(1)
(2) 支持豐富數據類型,支持string,list,set,sorted set,hash
(3) 支持事務,操作都是原子性,所謂的原子性就是對數據的更改要么全部執行,要么全部不執行
(4) 豐富的特性:可用于緩存,消息,按key設置過期時間,過期后將會自動刪除
6. Redis相比memcached有哪些優勢?
(1) memcached所有的值均是簡單的字符串,redis作為其替代者,支持更為豐富的數據類型
(2) redis的速度比memcached快很多
(3) redis可以持久化其數據
(4)Redis支持數據的備份,即master-slave模式的數據備份。
(5) 使用底層模型不同,它們之間底層實現方式 以及與客戶端之間通信的應用協議不一樣。Redis直接自己構建了VM 機制 ,因為一般的系統調用系統函數的話,會浪費一定的時間去移動和請求。
(6)value大小:redis最大可以達到1GB,而memcache只有1MB
7. 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,其他不變。
8. 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
9. 五種I/O模型介紹
IO 多路復用是5種I/O模型中的第3種,對各種模型講個故事,描述下區別:
故事情節為:老李去買火車票,三天后買到一張退票。參演人員(老李,黃牛,售票員,快遞員),往返車站耗費1小時。
1.阻塞I/O模型
老李去火車站買票,排隊三天買到一張退票。
耗費:在車站吃喝拉撒睡 3天,其他事一件沒干。
2.非阻塞I/O模型
老李去火車站買票,隔12小時去火車站問有沒有退票,三天后買到一張票。
耗費:往返車站6次,路上6小時,其他時間做了好多事。
3.I/O復用模型
1.select/poll
老李去火車站買票,委托黃牛,然后每隔6小時電話黃牛詢問,黃牛三天內買到票,然后老李去火車站交錢領票。
耗費:往返車站2次,路上2小時,黃牛手續費100元,打電話17次
2.epoll
老李去火車站買票,委托黃牛,黃牛買到后即通知老李去領,然后老李去火車站交錢領票。
耗費:往返車站2次,路上2小時,黃牛手續費100元,無需打電話
4.信號驅動I/O模型
老李去火車站買票,給售票員留下電話,有票后,售票員電話通知老李,然后老李去火車站交錢領票。
耗費:往返車站2次,路上2小時,免黃牛費100元,無需打電話
5.異步I/O模型
老李去火車站買票,給售票員留下電話,有票后,售票員電話通知老李并快遞送票上門。
耗費:往返車站1次,路上1小時,免黃牛費100元,無需打電話
1同2的區別是:自己輪詢
2同3的區別是:委托黃牛
3同4的區別是:電話代替黃牛
4同5的區別是:電話通知是自取還是送票上門
Redis為何那么快-----底層原理淺析
Redis的快速很多人都知道是因為基于內存,但這只是一方面,其實redis在底層是一套很完善的多路復用事件處理機制來保證執行的高效的
線程模型
redis內部使用文件事件處理器file event handler,它包含如下幾個部分
- 多個socket
- IO多路復用程序
- 文件事件分派器
- 事件處理器(連接應答處理器,命令請求處理器,命令回復處理器)
之所以說redis是單線程其實是指這個文件事件處理器是單線程的,它采用多路復用的方式監聽系統上多個socket,將socket上產生的事件壓入隊列中,由文件事件分派器從隊列中取出一個socket根據事件類型發給相應的事件處理器
整個處理過程如圖:
處理過程可以分為以下幾個步驟:
- 客戶端向redis發起一個socket請求,向redis的server socket請求連接,這里命名為socket01
- server socket產生一個AE_READABLE事件,IO多路復用程序監聽到事件后將這個socket01壓入隊列
- 文件事件分派器從隊列中取出socket01,交給連接應答處理器
- 連接應答處理器會將socket01的AE_READABLE事件與命令請求處理器相關聯
- 假設客戶端執行set操作,這時命令請求處理器會從socket01讀取key value,在內存中完成key value的設置
- 在內存中完成設置后,會將socket01的AE_WRITEABLE事件與命令回復處理器相關聯,然后壓入隊列中
- 事件分派器拿到socket01后,交給命令回復處理器,由命令回復處理器向socket01寫入本次操作的結果,比如OK,之后解除關聯
以上就是一個命令在redis中執行的過程
總結一下效率高的原因
Redis內部原理簡介
知道了Redis的各種數據結構,對象結構,那么Redis是如何保存數據的,又是如何操作數據的呢,Redis里面的命令是怎么實現的呢?這一系列問題值得我們思考
一.Redis維護多個數據庫
Redis內部維護一個db數組,每個db都是一個數據庫,默認情況下Redis會創建16個數據庫。我們可以通過select命令來切換數據庫,如select1切換到數據庫號為1的數據庫。select實現是通過修改客戶端的db指針,通過指針指向不同的數據庫來實現數據庫的切換操作的。
需要注意的是,為了不造成操作數據庫號錯誤,最好執行命令之前,手動select一下數據庫。
二.數據庫鍵空間
Redis是一個鍵值對數據庫服務器,Redis通過字典保存了數據庫中的所有鍵值對,我們將這個字典稱為鍵空間。鍵空間的每個鍵都是一個字符串對象,鍵空間的值也就是數據庫的值,可以是字符串對象,列表對象,哈希表對象,集合對象,有序集合對象中的任何一種。
1.添加新鍵
每次添加一個新鍵就是將一個新鍵值對添加到鍵空間里面,其中鍵為字符串對象,值為任意一種類型的Redis對象。
2.刪除鍵
刪除鍵就是在鍵空間里刪除鍵所對應的鍵值對對象。
3.更新鍵
更新鍵就是對鍵空間里面鍵所對應的值對象進行更新。
4.查找鍵
查找鍵就是在鍵空間中取出鍵所對應的值對象。
每次在鍵空間讀取一個鍵之后,服務器會更新鍵的LRU時間,用于計算鍵的閑置時間。如果服務器在讀取一個鍵時發現該鍵已經過期,那么服務器會先刪除這個過期鍵,然后才執行后續操作。如果有客戶端使用watch命令監視了某個鍵,那么服務器在對被監視的鍵進行修改之后,會將這個鍵標記為dirty,從而讓事務注意到這個鍵被修改過。服務器每次修改一個鍵之后,都會對鍵計數器的值+1,這個計數器用來觸發服務器的持久化操作。如果服務器開啟了數據庫通知功能,那么在對鍵進行修改之后,服務器將按配置發送相應的數據庫通知。
三.設置鍵的生存時間和過期時間
我們知道expire命令或者pexpire命令可以對一個鍵設置生存時間,經過指定的時間之后,服務器會自動刪除生存時間為0的鍵。那么Redis是如何實現刪除過期鍵的操作的呢?
Redis有四個命令可以設置鍵的過期時間,包括expire,pexpire,expireat,pexpireat,不過這四個命令最后都會轉化成pexpireat命令來實現。
Redis使用一個過期字典記錄所有帶過期時間的鍵,字典的鍵指向鍵空間中的某個鍵對象,字典的值是一個longlong類型的整數,這個證書保存了鍵空間所指向的數據庫鍵的過期時間。通過過期字典,程序可以檢查一個給定鍵是否過期,檢查給定鍵是否存在于過期字典,如果存在,取得鍵的過期時間,檢查當前時間戳是否大于鍵的過期時間,如果是的話,鍵已經過期,否則鍵未過期。
四.過期鍵的刪除策略
如果一個鍵過期了,那么它什么時候被刪除呢?通常我們可以用三種刪除策略刪除過期鍵
1.定時刪除:在設置鍵過期時間的同時,創建一個定時器,讓定時器在鍵的過期時間來臨時,刪除鍵
2.惰性刪除:放任鍵過期不管,但是每次動鍵空間獲取鍵時,都會檢查鍵是否過期,如果過期,則刪除。
3.定期刪除:每隔一段時間,程序就對數據庫進行一次檢查,刪除里面的過期鍵。
這幾種方式各有利有弊,首先定時刪除對內存最友好,當一個鍵過期時,一定會刪除這個鍵,釋放內存。不過定時刪除對CPU最不友好,在過期鍵比較多的情況下,刪除過期鍵這一行為可能會占用相當一部分CPU時間。此外,創建定時器需要用到Redis服務器中的時間時間,而當前時間時間的實現方式-無序鏈表查找一個事件的時間復雜度為O(N),不能高效地處理大量時間事件。
惰性刪除策略對CPU是最友好的,但是對內存最不友好。如果一個鍵已經過期,這個鍵又保留在數據庫中,那么內存就會一直占用不釋放。
定期刪除算是前兩種策略的一種整合和折中,定期策略每隔一段時間執行一次刪除過期鍵操作,并通過限制刪除操作執行的時長和頻率減少刪除操作對CPU時間的影響。定期刪除過期鍵可以有效地減少因為過期鍵帶來的內存浪費。
Redis過期鍵的刪除使用惰性刪除和定期刪除兩種策略配合使用。惰性策略比較好理解,所有讀寫數據庫的命令執行之前都會對輸入鍵進行檢查,如果鍵過期,那么從數據庫中刪除鍵。定期刪除策略的實現由Redis的serverCron函數來執行,這個函數每100ms執行一次,它在規定的時間內,分多次遍歷服務器中的各個數據庫,從數據庫的expires字典中隨機檢查一部分鍵的過期時間,刪除其中的過期鍵。
五.復制功能對過期鍵的處理
Redis復制主要包括RDB復制和AOF復制,在RDB復制中,每次執行SAVE或BGSAVE命令創建一個新的RDB文件時,程序會對數據庫中的鍵進行檢查,已過期的鍵不會被保存到新創建的RDB文件中。載入RDB文件時,服務器也會對保存的鍵進行檢查,如果鍵已過期,則不會載入。當使用AOF持久化模式運行時,當過期鍵被惰性刪除或者定期刪除之后,程序會向AOF文件追加一條刪除命令,記錄鍵已被刪除。
總結
以上是生活随笔為你收集整理的redis单线程原理___Redis为何那么快-----底层原理浅析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 直播报名 | 科技赋能零售金融业务转型
- 下一篇: java.sql.SQLIntegrit