Redis

什麼是redis?(Remote Dictionary Server(遠程數據服務))

非阻塞多路複用:
又被稱爲事件驅動,reactor模式
下面舉一個例子,模擬一個tcp服務器處理30個客戶socket。
假設你是一個老師,讓30個學生解答一道題目,然後檢查學生做的是否正確

你站在講臺上等,誰解答完誰舉手。這時C、D舉手,表示他們解答問題完畢,你下去依次檢查C、D的答案,然後繼續回到講臺上等。此時E、A又舉手,然後去處理E和A。。。 這種就是IO複用模型,Linux下的select、poll和epoll就是幹這個的。將用戶socket對應的fd註冊進epoll,然後epoll幫你監聽哪些socket上有消息到達,這樣就避免了大量的無用操作。此時的socket應該採用非阻塞模式。

kv存儲系統,value可以是String,Map,list,sets和sorted sets(zset)等類型
String作爲value最大不能超過512MB
List是基於LinkedList實現的(不用數組是因爲要能非常快的在很大列表上添加元素)(實現生產者/消費者模型)
hash(放入的數量沒有限制)
Set(是String的無序排列)(適用於表示對象間的關係)

key都會自動創建和刪除

redis有哪幾種數據淘汰策略?

(redis中當內存超過限制時,按照配置的策略,淘汰掉相應的kv,使得內存可以繼續留有足夠的空間保存新的數據。redis 確定驅逐某個鍵值對後,會刪除這個數據並將這個數據變更消息發佈到本地(AOF 持久化)和從機(主從連接)。)
1.volatile-lru:從設置了過期時間的數據集中,選擇最近最久未使用的數據釋放;
2.allkeys-lru:從數據集中(包括設置過期時間以及未設置過期時間的數據集中),選擇最近最久未使用的數據釋放;
3.volatile-random:從設置了過期時間的數據集中,隨機選擇一個數據進行釋放;
4.allkeys-random:從數據集中(包括了設置過期時間以及未設置過期時間)隨機選擇一個數據進行釋放;
5.volatile-ttl:從設置了過期時間的數據集中,選擇馬上就要過期的數據進行釋放操作;
6.noeviction:不刪除任意數據(但redis還會根據引用計數器進行釋放),這時如果內存不夠時,會直接返回錯誤。(默認)

redis持久化方法:(AOF與RDB)

RDB的主要原理就是在某個時間點把內存中所有數據保存到磁盤文件中
對於“把內存中的數據轉存到磁盤中”這一過程
1.如何落地存儲協議?也就是如何設計一個存儲協議可以兼容Redis豐富的數據類型(string、hash、list、set、zset)。
2.如何進行數據壓縮?通常一個Redis服務器存放了大量數據,如果要將這些數據轉存到文件中,如何進行數據壓縮,也就是如何儘可能減少文件大小以減輕系統IO壓力和節省磁盤空間。
3.如何解決數據轉存時服務器阻塞問題?Redis是單線程的,當服務器在進行數據轉存操作時就無法對外服務,如何避免這個問題?

save和bgsave
save是在Redis進程中執行的,由於Redis是單線程實現,所以當save命令在執行時會阻塞Redis服務器一直到該命令執行完成爲止。

與save命令不同的是,bgsave命令會先fork出一個子進程,然後在子進程中生成RDB文件。由於在子進程中執行IO操作,所以bgsave命令不會阻塞Redis服務器進程,Redis服務器進程在此期間可以繼續對外提供服務。
爲了提高性能,Redis服務器在bgsave命令執行期間會拒絕執行新到來的其它bgsave命令。
AOF持久化則採用了不同的策略,它將所有寫操作相關的命令記錄到磁盤文件中。一般對Redis的操作命令可以分爲“讀命令”和“寫命令”兩種,只有寫命令纔會改變數據的狀態,所以AOF持久化機制將服務器所執行的寫命令記錄下來,當系統崩潰時,只要重新執行記錄在AOF文件中的寫命令就可以將數據庫還原成原來的狀態。從這個角度看,Redis的AOF機制有點類似於log(記日誌)的過程。

AOF緩衝區,也就是說當Redis執行一條寫命令後,先將該命令追加到AOF緩衝區中,在以後的某個時刻再將AOF緩衝區中的內容同步到文件中。

Redis服務器在每個事件循環都將AOF緩衝區server.aof_buf中的數據寫入AOF文件中,且每秒執行一次AOF文件同步操作。該模式效率和安全性(如果機器崩潰只丟失前一秒處理的新數據)比較適中,是Redis的默認同步策略。(AOF_FSYNC_EVERYSEC)

Redis服務器在每個事件循環都將AOF緩衝區server.aof_buf中的數據寫入AOF文件中,且執行一次AOF文件同步操作。該模式速度最慢(每個事件循環都要執行同步操作)但也最安全(如果機器崩潰只丟失當前事件循環中處理的新數據)。(AOF_FSYNC_ALWAYS)

Redis服務器在每個事件循環都將AOF緩衝區server.aof_buf中的數據寫入AOF文件中,但不執行同步fsync方法,由操作系統決定何時同步。該模式速度最快(無需執行同步操作)但也最不安全(如果機器崩潰將丟失上次同步後的所有數據)。(AOF_FSYNC_NO)

redis集羣

投票機制:
投票過程是集羣中所有master參與,如果半數以上master節點與master節點通信超時(cluster-node-timeout),認爲當前master節點掛掉.

1.Redis官方集羣方案 Redis Cluster
主從節點,一個主節點,n個從節點,如果主節點失效,會從slave節點中選擇一個升級爲主節點
對於客戶端來說,整個Cluster被看作是一個整體,客戶端可以鏈接任意一個node進行操作,就像操作單一Redis實例一樣,當客戶端操作的key沒有分配到該node時,Redis會返回轉向指令,指向另一個可用的node

2.Redis Sharding集羣
採用哈希算法將Redis數據的key進行散列,通過hash函數,特定的key會映射到特定的redis節點上

3.利用代理中間件實現大規模Redis集羣(twemproxy也叫nutcracker)
降低客戶端直接連接後對服務器的鏈接數量,但性能降低20%

4.Redis Sentinel哨兵模式(監控master)
當redis在做master-slave的高可用方案時,假如master宕機了,redis本身(以及其很多客戶端)都沒有實現自動進行主備切換,而redis-sentinel本身也是獨立運行的進程,可以部署在其他與redis集羣可通訊的機器中監控redis集羣。
1、不時地監控redis是否按照預期良好地運行;
2、如果發現某個redis節點運行出現狀況,能夠通知另外一個進程(例如它的客戶端);
3、能夠進行自動切換。當一個master節點不可用時,能夠選舉出master的多個slave(如果有超過一個slave的話)中的一個來作爲新的master,其它的slave節點會將它所追隨的master的地址改爲被提升爲master的slave的新地址。
4、哨兵爲客戶端提供服務發現,客戶端鏈接哨兵,哨兵提供當前master的地址然後提供服務,如果出現切換,也就是master掛了,哨兵會提供客戶端一個新地址。
單個哨兵會存在自己掛掉而無法監控整個集羣的問題,所以哨兵也是支持集羣的,我們通常用三臺哨兵機器來監控一組redis集羣。

RDB的優缺點
優:不會進行任何IO操作,保證了Redis的高性能
缺:間隔一段時間進行持久化,持久化時,產生故障,造成數據丟失

AOF的優缺點
優:保證數據的完整性
缺:AOF文件比RDB文件大,且恢復速度慢

主從複製模式
存RDB快照和在RDB快照期間產生的命令緩存起來一起發給從數據庫
主從複製是異步的,保證的是最終一致性

寫操作丟失
當一個寫到達master,master已經回覆client,但是master還沒來的及複製到slave就宕機了,那麼這個寫操作就會丟失。

修改配置不重啓Redis會實時生效嗎?實時生效
redis 127.0.0.1:6379> config get maxmemory
1) “maxmemory”
2) “3221225472”
redis 127.0.0.1:6379> config set maxmemory 4294967296
OK

redis與memcached的區別????

redis可擴展1000個節點

redis常見性能問題和解決方案:

(1) Master最好不要做任何持久化工作,如RDB內存快照和AOF日誌文件

(2) 如果數據比較重要,某個Slave開啓AOF備份數據,策略設置爲每秒同步一次

(3) 爲了主從複製的速度和連接的穩定性,Master和Slave最好在同一個局域網內

(4) 儘量避免在壓力很大的主庫上增加從庫

(5) 主從複製不要用圖狀結構,用單向鏈表結構更爲穩定,即:Master <- Slave1 <- Slave2 <- Slave3…

這樣的結構方便解決單點故障問題,實現Slave對Master的替換。如果Master掛了,可以立刻啓用Slave1做Master,其他不變。

redis適用場景:

1.少量數據存儲,高速讀寫訪問,它使用內存提供主存儲支持,而僅使用硬盤做持久性的存儲
2.那些你現有的數據庫處理起來感到緩慢的任務

BSD協議:給與使用者很大自由的協議,可以自由的使用,修改源代碼,也可以將修改後的代碼作爲開源或者專有軟件再發布

用作存儲用還是緩存:
儘管數據分區對於Redis來說無論是數據持久化存儲還是緩存,在概念上都是一樣的,然而對於數據持久化存儲還是有一個很大的限制。當我們使用Redis來作爲持久化存儲的時候,每一個key必須一直被映射到同一個Redis實例。
而當Redis被當做緩存使用的時候,對於這個key,如果一個實例不能用了,這個key還可以被映射到其他的實例中。

緩存穿透

查詢一個不存在的數據,導致這個不存在的數據每次都要求存儲層去查詢,失去了緩存的意義,流量大時,可能DB就掛了
如果有人利用這個不存在的key頻繁攻擊,這就是漏洞
解決方案:
1.將所有可能存在的數據哈希到一個足夠大的bitmap中,一個一定不存在的數據就會被這個bitmap攔截掉,從而避免了對底層存儲的查詢壓力
2.簡單粗暴,如果一個查詢返回的數據爲空,仍然把這個結果緩存,但是它的過期時間設置的比較短

緩存雪崩
設置緩存時間採用了相同的過期時間,導致緩存在某一時刻同時失效,請求全部轉發到DB,DB瞬間壓力過重雪崩
解決方案:
1.將緩存的失效時間分散開,失效時間基礎上加上一個隨機值,使重複率降低

緩存擊穿
一些設置過期時間的key,恰好在這個時間點對這個key有大量請求,這些請求發現緩存過期一般會從DB加載數據並設回到緩存,這個時候大併發的請求可能會瞬間把後端DB壓垮
解決方案:
1.使用互斥鎖(存在死鎖風險,線程池阻塞)
簡單地來說,就是在緩存失效的時候(判斷拿出來的值爲空),不是立即去load db,而是先使用緩存工具的某些帶成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一個mutex key,當操作返回成功時,再進行load db的操作並回設緩存;否則,就重試整個get緩存的方法。
public String get(key) {
String value = redis.get(key);
if (value == null) { //代表緩存值過期
//設置3min的超時,防止del操作失敗的時候,下次緩存過期一直不能load db
if (redis.setnx(key_mutex, 1, 3 * 60) == 1) { //代表設置成功
value = db.get(key);
redis.set(key, value, expire_secs);
redis.del(key_mutex);
} else { //這個時候代表同時候的其他線程已經load db並回設到緩存了,這時候重試獲取緩存值即可
sleep(50);
get(key); //重試
}
} else {
return value;
}
}
2.提前使用互斥鎖
在value內部設置1個超時值(timeout1), timeout1比實際的memcache timeout(timeout2)小。當從cache讀取到timeout1發現它已經過期時候,馬上延長timeout1並重新設置到cache。然後再從數據庫加載數據並設置到cache中。

3.永遠不過期(缺點:構建緩存的時候,其餘線程可能訪問的是老數據)
從功能上看,如果不過期,那不就成靜態的了嗎?所以我們把過期時間存在key對應的value裏,如果發現要過期了,通過一個後臺的異步線程進行緩存的構建,也就是“邏輯”過期

4.資源保護????
採用netflix的hystrix,可以做資源的隔離保護主線程池

Redis是單線程的,如何提高多核CPU的利用率?
要發揮多核CPU性能,可以通過在單機開多個Redis core實例來完善,一樣實現分佈式;
實現讀寫分離
組成master-master或者master-slave。

Consistent hashing實現通常使得當一個key被映射到的實例不能用的時候將這個key映射到其他實例成爲可能。類似,如果增加了一臺機器,一部分的key將會被映射到這臺新的機器上,我們需要了解的兩點如下:

如果Redis被用來當做緩存,且要求容易增加或刪除機器,使用consistent hashing是非常簡單的。
如果Redis被用來當做(持久)存儲,一個固定的key到實例的映射是需要的,因此我們不能夠再靈活的添加或刪除機器。否則,我們需要在增加或刪除機器的時候系統能夠rebalace,當前Redis Cluster已經支持。

Redis支持數據的持久化,可以將內存中的數據保存在磁盤中,重啓的時候可以再次加載進行使用。
Redis不僅僅支持簡單的key-value類型的數據,同時還提供list,set,zset,hash等數據結構的存儲。
Redis支持數據的備份,即master-slave模式的數據備份。

所有操作都是原子性,支持事務

redis與其他kv存儲有什麼不同?
redis的數據類型基於基本類型結構,無需進行額外的抽象
運行在內存中,但也可以持久化到磁盤中
發佈/訂閱(pub/sub)
發佈者發送消息,訂閱者接受消息
redis客戶端可以訂閱任意數量的頻道

redis事務:

3個階段(不支持回滾,事務執行時會阻塞其他客戶端的請求執行)
1.事務開始
2.命令入隊
3.事務執行

原子性
分2種情況:
1.當執行的指令被服務器拒絕的時候,所以事務中的所有命令都不會執行,因爲
前面我們有介紹到,redis的事務命令是統一先放到事務隊列裏,在用戶輸入EXEC命令的時候再統一執行。
但是我們錯誤的使用”GET”命令,在命令
放入事務隊列的時候被檢測到事務,這時候還沒有接收到EXEC命令,所以全部不執行
redis > MULTI
OK

redis > SET pwd 123456
QUEUED

redis > GET
(error) ERR wrong number of arguments for ‘get’ command

redis > GET username
QUEUED

redis > EXEC
(error) EXECABORT Transaction discarded because of previous errors
2.與傳統型數據庫的事務不同的是:不支持回滾事務,即使隊列中的操作發生錯誤,也會繼續執行後續的隊列中的命令

一致性:
事務具有一致性指的是,如果數據庫在執行事務之前是一致的,那麼在事務執行之後,無論事務是否執行成功,數據庫也應該仍然一致的。
”一致“指的是數據符合數據庫本身的定義和要求,沒有包含非法或者無效的錯誤數據。redis通過謹慎的錯誤檢測和簡單的設計來保證事務一致性。

隔離性:(因爲redis是單線程)
事務的隔離性指的是,即使數據庫中有多個事務併發在執行,各個事務之間也不會互相影響,並且在併發狀態下執行的事務和串行執行的事務產生的結果完全
相同。
因爲redis使用單線程的方式來執行事務(以及事務隊列中的命令),並且服務器保證,在執行事務期間不會對事物進行中斷,因此,redis的事務總是以串行
的方式運行的,並且事務也總是具有隔離性的
持久性:
事務的耐久性指的是,當一個事務執行完畢時,執行這個事務所得的結果已經被保持到永久存儲介質裏面。
因爲redis事務不過是簡單的用隊列包裹起來一組redis命令,redis並沒有爲事務提供任何額外的持久化功能,所以redis事務的耐久性由redis使用的模式決定
當服務器在無持久化的內存模式下運行時,事務不具有耐久性,一旦服務器停機,包括事務數據在內的所有服務器數據都將丟失
當服務器在RDB持久化模式下運作的時候,服務器只會在特定的保存條件滿足的時候纔會執行BGSAVE命令,對數據庫進行保存操作,並且異步執行的BGSAVE不
能保證事務數據被第一時間保存到硬盤裏面,因此RDB持久化模式下的事務也不具有耐久性
當服務器運行在AOF持久化模式下,並且appedfsync的選項的值爲always時,程序總會在執行命令之後調用同步函數,將命令數據真正的保存到硬盤裏面,因此這種配置下的事務是具有耐久性的。
當服務器運行在AOF持久化模式下,並且appedfsync的選項的值爲everysec時,程序會每秒同步一次命令數據到磁盤因爲停機可能會恰好發生在等待同步的那一秒內,這種可能造成事務數據丟失,所以這種配置下的事務不具有耐久性

redis客戶端連接
1.客戶端 socket 會被設置爲非阻塞模式,因爲 Redis 在網絡事件處理上採用的是非阻塞多路複用模型。
2.爲這個 socket 設置 TCP_NODELAY 屬性,禁用 Nagle 算法
3.創建一個可讀的文件事件用於監聽這個客戶端 socket 的數據發送

通常情況下的客戶端/服務端
客戶端向服務端發送一個查詢請求,並監聽Socket返回,通常是以阻塞模式,等待服務端響應。
服務端處理命令,並將結果返回給客戶端。
但是redis管道技術可以在服務端未響應時,客戶端可以繼續向服務端發送請求,並最終一次性讀取所有服務端的響應

分區:(水平分區與垂直分區)
分區是分割數據到多個Redis實例的處理過程,因此每個實例只保存key的一個子集。
水平分區:通過某個屬性進行分區,譬如按年份
垂直分區:把不經常使用的列,劃分到另一個分區

分割數據到多個Redis實例的過程,每個實例只保存key的一個子集

redis分區:
優點:1.通過利用多臺計算機內存的和值,允許我們構造更大的數據庫。
2.通過多核和多臺計算機,允許我們擴展計算能力;通過多臺計算機和網絡適配器,允許我們擴展網絡帶寬
缺點:1.涉及多個key的redis事務不能使用
2.涉及多個key的操作通常是不被支持的。舉例來說,當兩個set映射到不同的redis實例上時,你就不能對這兩個set執行交集操作。
3.當使用分區時,數據處理較爲複雜,比如你需要處理多個rdb/aof文件,並且從多個實例和主機備份持久化文件
4.增加或刪除容量也比較複雜。redis集羣大多數支持在運行時增加、刪除節點的透明數據平衡的能力,但是類似於客戶端分區、代理等其他系統則不支持這項特性。然而,一種叫做presharding的技術對此是有幫助的。

Redis 提供了接口(hgetall)可以直接取到全部的屬性數據,但是如果內部 Map 的成員很多,那麼涉及到遍歷整個內部 Map 的操作,由於 Redis 單線程模型的緣故,這個遍歷操作可能會比較耗時,而另其它客戶端的請求完全不響應,這點需要格外注意。 Redis服務端是個單線程的架構,不同的Client雖然看似可以同時保持連接,但發出去的命令是序列化執行的,這在通常的數據庫理論下是最高級別的隔離

發佈了125 篇原創文章 · 獲贊 36 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章