Redis本質上是一個Key-Value類型的內存數據庫,很像memcached,整個數據庫統(tǒng)統(tǒng)加載 在內存當中進行操作,定期通過異步操作把數據庫數據flush到硬盤上進行保存。
因為是純內存操作,Redis的性能非常出色,每秒可以處理超過 10萬次讀寫操作,是已知性能 最快的Key-Value DB。
Redis的出色之處不僅僅是性能,Redis最大的魅力是支持保存多種數據結構,此外單個value 的最大限制是1GB,不像 memcached只能保存1MB的數據,因此Redis可以用來實現(xiàn)很多有用的功能。比方說用他的List來做FIFO雙向鏈表,實現(xiàn)一個輕量級的高性 能消息隊列服務,用他的Set可 以做高性能的tag系統(tǒng)等等。
另外Redis也可以對存入的Key-Value設置expire時間,因此也可以被當作一 個功能加強版的 memcached來用。 Redis的主要缺點是數據庫容量受到物理內存的限制,不能用作海量數據 的高性能讀寫,因此Redis適合的場景主要局限在較小數據量的高性能操作和運算上。
字符串(String):二進制安全字符串。
列表(List):根據插入順序排序的字符串元素列表,基于鏈表實現(xiàn)。
集合(Set):唯一的亂序的字符串元素的集合。
有序集合(Sorted Set):與集合類似,但是每個字符串元素都與一個稱為score的數字相關聯(lián)。元素總是按其score排序,并且可以檢索一定score范圍的元素。
哈希(Hash):由字段與值相關聯(lián)組成的映射,字段和值都是字符串。
位圖(Bitmap):像操作位數組一樣操作字符串值,可以設置和清除某個位,對所有為1的位進行計數,找到第一個設置1的位,找到第一個設置0的位等等。
HyperLogLogs:一種概率數據結構,使用較小的內存空間來統(tǒng)計唯一元素的數量,誤差小于1%。
1.完全基于內存,絕大部分請求是純粹的內存操作,非常快速。數據存在內存中,類似于 HashMap,HashMap 的優(yōu)勢就是查找和操作的時間復雜度都是O(1);
2.數據結構簡單,對數據操作也簡單,Redis 中的數據結構是專門進行設計的;
3.采用單線程,避免了不必要的上下文切換和競爭條件,也不存在多進程或者多線程導致的切換而消耗 CPU,不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作,沒有因為可能出現(xiàn)死鎖而導致的性能消耗;
4.使用多路 I/O 復用模型,非阻塞 IO;
5.使用底層模型不同,它們之間底層實現(xiàn)方式以及與客戶端之間通信的應用協(xié)議不一樣,Redis 直接自己構建了 VM 機制 ,因為一般的系統(tǒng)調用系統(tǒng)函數的話,會浪費一定的時間去移動和請求;
1.緩存:減輕數據庫的壓力,提高系統(tǒng)性能。
2.排行榜:利用 Redis 的 SortSet(有序集合)實現(xiàn);
3.計數器/限速器:利用 Redis 中原子性的自增操作,我們可以統(tǒng)計類似用戶點贊數、用戶訪問數等。這類操作如果用 MySQL,頻繁的讀寫會帶來相當大的壓力;限速器比較典型的使用場景是限制某個用戶訪問某個 API 的頻率,常用的有搶購時,防止用戶瘋狂點擊帶來不必要的壓力;
4.好友關系:利用集合的一些命令,比如求交集、并集、差集等。可以方便解決一些共同好友、共同愛好之類的功能;
5.消息隊列:除了 Redis 自身的發(fā)布/訂閱模式,我們也可以利用 List 來實現(xiàn)一個隊列機制,比如:到貨通知、郵件發(fā)送之類的需求,不需要高可靠,但是會帶來非常大的 DB 壓力,完全可以用 List 來完成異步解耦;
6.Session 共享:Session 是保存在服務器的文件中,如果是集群服務,同一個用戶過來可能落在不同機器上,這就會導致用戶頻繁登陸;采用 Redis 保存 Session 后,無論用戶落在那臺機器上都能夠獲取到對應的 Session 信息。
緩存穿透指查詢一個一定不存在的數據,由于緩存是不命中時需要從數據庫查詢,查不到數據則不寫入緩存,這將導致這個不存在的數據每次請求都要到數據庫去查詢,進而給數據庫帶來壓力。
解決方案:
1)將空數據也緩存:占有一定的空間,可能帶來短期的數據不一致。
如果一個查詢返回的數據為空(不管是數據不存在,還是系統(tǒng)故障),我們仍然把這個空結果進行緩存,但它的過期時間會很短,最長不超過五分鐘,
2)使用布隆過濾器bloom filter:是一種預防的方案,占用空間少、誤差可控。
將所有可能存在的數據哈希到一個足夠大的bitmap中,一個一定不存在的數據會被這個bitmap攔截掉,從而避免了對底層存儲系統(tǒng)的查詢壓力。
緩存雪崩是指在某一個時間段,緩存集中過期失效。當某一個時刻出現(xiàn)大規(guī)模的緩存失效的情況,那么就會導致大量的請求直接打在數據庫上面,導致數據庫壓力巨大,如果在高并發(fā)的情況下,可能瞬間就會導致數據庫宕機。這時候如果運維馬上又重啟數據庫,馬上又會有新的流量把數據庫打死。這就是緩存雪崩。
解決方案
1)過期時間設置隨機值:在原有的失效時間上加上一個隨機值,比如,1-5分鐘隨機。這樣就避免了同一時間大量數據過期現(xiàn)象的發(fā)生而導致緩存雪崩。
2)分布式部署且均勻分布熱點數據:如果緩存數據庫是分布式部署,將熱點數據均勻分布在不同搞得緩存數據庫中。同時,分布式集群可以防止Redis宕機導致緩存雪崩的問題。
3)熱點數據永不過期:設置熱點數據永遠不過期。
4)使用熔斷機制。當流量到達一定的閾值時,就直接返回“系統(tǒng)擁擠”之類的提示,防止過多的請求打在數據庫上。至少能保證一部分用戶是可以正常使用,其他用戶多刷新幾次也能得到結果。
5)提高數據庫的容災能力,可以使用分庫分表,讀寫分離的策略。
造成緩存雪崩的關鍵在于在同一時間大規(guī)模的key失效。出現(xiàn)這個問題有下面幾種可能:
第一種可能是Redis宕機,
第二種可能是采用了相同的過期時間。
某一個熱點 key,在緩存過期的一瞬間,同時有大量的請求打進來,由于此時緩存過期了,所以請求最終都會走到數據庫,造成瞬時數據庫請求量大、壓力驟增,甚至可能打垮數據庫。
解決方案:
1.加互斥鎖。在并發(fā)的多個請求中,只有第一個請求線程能拿到鎖并執(zhí)行數據庫查詢操作,其他的線程拿不到鎖就阻塞等著,等到第一個線程將數據寫入緩存后,直接走緩存。
2.JVM 鎖保證了在單臺服務器上只有一個請求走到數據庫,通常來說已經足夠保證數據庫的壓力大大降低,同時在性能上比分布式鎖更好。
需要注意的是,無論是使用“分布式鎖”,還是“JVM 鎖”,加鎖時要按 key 維度去加鎖。
Redis通過MULTI、EXEC、WATCH等一組命令集合,來實現(xiàn)事務機制。事務支持一次執(zhí)行多個命令,一個事務中所有命令都會被序列化。在事務執(zhí)行過程,會按照順序串行化執(zhí)行隊列中的命令,其他客戶端提交的命令請求不會插入到事務執(zhí)行命令序列中。簡言之,Redis事務就是順序性、一次性、排他性的執(zhí)行一個隊列中的一系列命令。
Redis執(zhí)行事務的流程如下:開始事務(MULTI)、命令入隊、執(zhí)行事務(EXEC)、撤銷事務(DISCARD )。
可以的,Redis提供兩個指令生成RDB,分別是save和bgsave。
如果是save指令,會阻塞,因為是主線程執(zhí)行的。
如果是bgsave指令,是fork一個子進程來寫入RDB文件的,快照持久化完全交給子進程來處理,父進程則可以繼續(xù)處理客戶端的請求。
一般來說, 如果想達到足以媲美PostgreSQL的數據安全性,你應該同時使用兩種持久化功能。在這種情況下,當 Redis 重啟的時候會優(yōu)先載入AOF文件來恢復原始的數據,因為在通常情況下AOF文件保存的數據集要比RDB文件保存的數據集要完整。
如果你非常關心你的數據, 但仍然可以承受數分鐘以內的數據丟失,那么你可以只使用RDB持久化。有很多用戶都只使用AOF持久化,但并不推薦這種方式,因為定時生成RDB快照(snapshot)非常便于進行數據庫備份, 并且 RDB 恢復數據集的速度也要比AOF恢復的速度要快,除此之外,使用RDB還可以避免AOF程序的bug。
如果你只希望你的數據在服務器運行的時候存在,你也可以不使用任何持久化方式。
緩存預熱就是系統(tǒng)上線后,將相關的緩存數據直接加載到緩存系統(tǒng)。這樣就可以避免在用戶請求的時候,先查詢數據庫,然后再將數據緩存的問題!用戶直接查詢事先被預熱的緩存數據!
解決方案
1.直接寫個緩存刷新頁面,上線時手工操作一下;
2.數據量不大,可以在項目啟動的時候自動進行加載;
3.定時刷新緩存;
Redis6.0采用多線程IO,不過命令的執(zhí)行還是單線程的。
Redis6.0之前,IO線程和執(zhí)行線程都是單線程的。
分表是EXPIRE和PERSIST命令進行設置。
加鎖重鍵(互斥鎖):
熱鍵不過期:在緩存中創(chuàng)建一個時間戳,先判斷時間戳是否過期,如果沒有過期返回原數據,過期了則訪問數據源。
布隆過濾器是一個叫“布隆”的人提出的,它本身是一個很長的二進制向量,既然是二進制的向量,那么顯而易見的,存放的不是0,就是1。布隆過濾器是一種由位數組和多個哈希函數組成概率數據結構,返回兩種結果可能存在和一定不存在。布隆過濾器里的一個元素由多個狀態(tài)值共同確定。位數組存儲狀態(tài)值,哈希函數計算狀態(tài)值的位置。
優(yōu)點:由于存放的不是完整的數據,所以占用的內存很少,而且新增,查詢速度夠快;
缺點: 隨著數據的增加,誤判率隨之增加;無法做到刪除數據;只能判斷數據是否一定不存在,而無法判斷數據是否一定存在。
常用命令: set,get,decr,incr,mget 等。
含義:String數據結構是簡單的Key-Value類型,value不僅可以是String,也可以是數字。
數據結構:內部結構實現(xiàn)上類似于 Java 的 ArrayList,采用預分配冗余空間的方式來減少內存的頻繁分配,如圖所示:
len 是當前字符串實際長度,capacity 是為字符串分配的可用空間,當字符串長度小于 1M 時,擴容都是加倍現(xiàn)有的空間,如果超過 1M,擴容時一次只會多擴 1M 的空間。字符串最大長度為 512M。
應用場景: 常規(guī)計數,微博數,粉絲數等。
常用命令: hget,hset,hgetall 等。
含義:Redis中的哈希結構就如同Java中的map一樣,Hash是一個string類型的field和value的映射表,hash特別適合用于存儲對象。
數據結構:Redis Hash通過分桶的方式解決 hash 沖突。它是無序字典。內部實現(xiàn)結構是同樣的數組 + 鏈表二維結構。第一維 hash 的數組位置碰撞時,就會將碰撞的元素使用鏈表串接起來。第一維是數組,第二維是鏈表。數組中存儲的是第二維鏈表的第一個元素的指針。
應用場景:存儲用戶信息,商品信息等等。例如修真院的首頁的職業(yè)信息,只是簡單的信息集合,我們可以直接將它儲存到Redis中,在讀取的過程中就不用序列化對象,直接操作。
常用命令: lpush,rpush,lpop,rpop,lrange等
含義:list就是鏈表,Redis list的實現(xiàn)為一個雙向鏈表,即可以支持反向查找和遍歷,更方便操作,不過帶來了部分額外的內存開銷。
數據結構:Redis 的列表相當于 Java 語言中的 LinkedList,注意它是鏈表而不是數組。這意味著 list 的插入和刪除操作非常快,時間復雜度為 O(1),但是索引定位很慢,時間復雜度為 O(n)。
list的特點是:
1)有序
2)可以重復
3)右邊進左邊出或者左邊進右邊出,則列表可以充當隊列
4)左邊進左邊出或者右邊進右邊出,則列表可以充當棧
應用場景:微博的關注列表,粉絲列表,最新消息排行等功能
常用命令:sadd,spop,smembers,sunion 等
含義:set對外提供的功能與list類似,是一個列表的功能,特殊之處在于set是可以自動排重的。 并且set提供了判斷某個成員是否在一個set集合內的重要接口,這個也是list所不能提供的。
數據結構:set和字典非常類似,其內部實現(xiàn)就是上述的hashTable的特殊實現(xiàn),與字典不同的地方有兩點:
1)只關注key值,所有的value都是NULL。
2)在新增數據時會進行去重。
場景應用:
1.共同好友、二度好友
2.利用唯一性,可以統(tǒng)計訪問網站的所有獨立 IP
3.好友推薦的時候,根據 tag 求交集,大于某個 threshold 就可以推薦
常用命令: zadd,zrange,zrem,zcard等
含義:和set相比,sorted set增加了一個權重參數score,使得集合中的元素能夠按score進行有序排列。
數據結構:zset是Redis非常有特色的數據結構,它是基于Set并提供排序的有序集合。其中最為重要的特點就是支持通過score的權重來指定權重。一些排行榜、延遲任務比如指定1小時后執(zhí)行, 就是使用這個數據結構實現(xiàn)的。
應用場景:在直播系統(tǒng)中,實時排行信息包含直播間在線用戶列表,各種禮物排行榜,彈幕消息(可以理解為按消息維度的消息排行榜)等信息,適合使用Redis中的SortedSet結構進行存儲。
Redis 作為一個K-V的內存數據庫,它使用用一張全局的哈希來保存所有的鍵值對。這張哈希表,有多個哈希桶組成,哈希桶中的entry元素保存了key和value指針,其中*key指向了實際的鍵,*value指向了實際的值。
所謂的哈希沖突通是指過不同的key,計算出一樣的哈希值,導致落在同一個哈希桶中。
Redis為了解決哈希沖突,采用了鏈式哈希。鏈式哈希是指同一個哈希桶中,多個元素用一個鏈表來保存,它們之間依次用指針連接。
因為哈希沖突鏈上的元素只能通過指針逐一查找再操作,所以當往哈希表插入數據很多,沖突也會越多,沖突鏈表就會越長,那查詢效率就會降低了。為了保持高效,Redis 會對哈希表做rehash操作,也就是增加哈希桶,減少沖突。為了rehash更高效,Redis還默認使用了兩個全局哈希表,一個用于當前使用,稱為主哈希表,一個用于擴容,稱為備用哈希表。
Redis集群沒有使用一致性hash,而是引入了哈希槽的概念,Redis集群有16384個哈希槽,每個key通過CRC16校驗后對16384取模來決定放置哪個槽,集群的每個節(jié)點負責一部分hash槽。
我們在項目中使用Redis,肯定不會是單點部署Redis服務的。因為,單點部署一旦宕機,就不可用了。為了實現(xiàn)高可用,通常的做法是,將數據庫復制多個副本以部署在不同的服務器上,其中一臺掛了也可以繼續(xù)提供服務。Redis 實現(xiàn)高可用有三種部署模式:主從模式,哨兵模式,集群模式。
主從模式中Redis部署了多臺機器,有負責讀寫操作主節(jié)點和只負責讀操作從節(jié)點,從節(jié)點的數據來自主節(jié)點,實現(xiàn)原理就是主從復制機制。主從復制包括全量復制,增量復制兩種。一般當slave第一次啟動連接master,或者認為是第一次連接,就采用全量復制,全量復制流程如下:
1.slave發(fā)送sync命令到master。
2.master接收到SYNC命令后,執(zhí)行bgsave命令,生成RDB全量文件。
3.master使用緩沖區(qū),記錄RDB快照生成期間的所有寫命令。
4.master執(zhí)行完bgsave后,向所有slave發(fā)送RDB快照文件。
5.slave收到RDB快照文件后,載入、解析收到的快照。
6.master使用緩沖區(qū),記錄RDB同步期間生成的所有寫的命令。
7.master快照發(fā)送完畢后,開始向slave發(fā)送緩沖區(qū)中的寫命令;
8.salve接受命令請求,并執(zhí)行來自master緩沖區(qū)的寫命令
redis2.8版本之后,已經使用psync來替代sync,因為sync命令非常消耗系統(tǒng)資源,psync的效率更高。
slave與master全量同步之后,master上的數據,如果再次發(fā)生更新,就會觸發(fā)增量復制。
當master節(jié)點發(fā)生數據增減時,就會觸發(fā)replicationFeedSalves()函數,接下來在 Master節(jié)點上調用的每一個命令會使用replicationFeedSlaves()來同步到Slave節(jié)點。執(zhí)行此函數之前呢,master節(jié)點會判斷用戶執(zhí)行的命令是否有數據更新,如果有數據更新的話,并且slave節(jié)點不為空,就會執(zhí)行此函數。這個函數作用就是:把用戶執(zhí)行的命令發(fā)送到所有的slave節(jié)點,讓slave節(jié)點執(zhí)行。流程如下:
Redis的哨兵(sentinel) 系統(tǒng)用于管理多個 Redis 服務器,該系統(tǒng)執(zhí)行以下三個任務:
1)監(jiān)控(Monitoring): 哨兵(sentinel) 會不斷地檢查你的Master和Slave是否運作正常。
2)提醒(Notification):當被監(jiān)控的某個 Redis出現(xiàn)問題時, 哨兵(sentinel) 可以通過 API 向管理員或者其他應用程序發(fā)送通知。
3)自動故障遷移(Automatic failover):當一個Master不能正常工作時,哨兵(sentinel) 會開始一次自動故障遷移操作,它會將失效Master的其中一個Slave升級為新的Master, 并讓失效Master的其他Slave改為復制新的Master; 當客戶端試圖連接失效的Master時,集群也會向客戶端返回新Master的地址,使得集群可以使用Master代替失效Master。
監(jiān)控主數據庫和從數據庫是否正常運行。
主數據庫出現(xiàn)故障時,可以自動將從數據庫轉換為主數據庫,實現(xiàn)自動切換。
當主節(jié)點出現(xiàn)故障時,由Redis Sentinel自動完成故障發(fā)現(xiàn)和轉移,并通知應用方,實現(xiàn)高可用性。
其實整個過程只需要一個哨兵節(jié)點來完成,首先使用Raft算法(選舉算法)實現(xiàn)選舉機制,選出一個哨兵節(jié)點來完成轉移和通知
1)哨兵集群至少要 3 個節(jié)點,來確保自己的健壯性
2)redis主從 + sentinel的架構,是不會保證數據的零丟失的,它是為了保證redis集群的高可用.
會有,主要考慮下面兩種情況。
1)主從異步復制導致的數據丟失:redis master 和slave 數據復制是異步的,這樣就有可能會出現(xiàn)部分數據還沒有復制到slave中,master就掛掉了,那么這部分的數據就會丟失了
2)腦裂導致的數據丟失:腦裂其實就是網絡分區(qū)導致的現(xiàn)象,比如,我們的master機器網絡突然不正常了發(fā)生了網絡分區(qū),和其他的slave機器不能正常通信了,其實master并沒有掛還活著好好的呢,但是哨兵可不是吃閑飯的啊,它會認為master掛掉了啊,那么問題來了,client可能還在繼續(xù)寫master的呀,還沒來得及更新到新的master呢,那這部分數據就會丟失。
如果一個master被認為宕機了,而且majority多數哨兵都允許了主備切換,那么某個哨兵就會執(zhí)行主備切換操作,此時首先要選舉一個slave來,主要通過下面幾個步驟
1)slave跟master斷開連接的時長(斷開時間越短優(yōu)先級越高)
2)slave優(yōu)先級(在配置文件中的配置,slave priority越低,優(yōu)先級就越高。)
3)復制offset(哪個slave復制了越多的數據,offset越靠后,優(yōu)先級就越高。)
4)run id(如果上面兩個條件都相同,那么選擇一個run id比較小的那個slave)
redis從3.0開始支持集群功能。redis集群采用無中心節(jié)點方式實現(xiàn),無需proxy代理,客戶端直接與redis集群的每個節(jié)點連接,根據同樣的hash算法計算出key對應的slot,然后直接在slot對應的redis節(jié)點上執(zhí)行命令。在redis看來,響應時間是最苛刻的條件,增加一層帶來的開銷是redis不能接受的。因此,redis實現(xiàn)了客戶端對節(jié)點的直接訪問,為了去中心化,節(jié)點之間通過gossip協(xié)議交換互相的狀態(tài),以及探測新加入的節(jié)點信息。redis集群支持動態(tài)加入節(jié)點,動態(tài)遷移slot,以及自動故障轉移。
redis內存數據集大小上升到一定大小的時候,就會施行數據淘汰策略。
可以好好利用Hash,list,sorted set,set等集合類型數據,因為通常情況下很多小的Key-Value可以用更緊湊的方式存放到一起。盡可能使用散列表(hashes),散列表(是說散列表里面存儲的數少)使用的內存非常小,所以你應該盡可能的將你的數據模型抽象到一個散列表里面。比如你的web系統(tǒng)中有一個用戶對象,不要為這個用戶的名稱,姓氏,郵箱,密碼設置單獨的key,而是應該把這個用戶的所有信息存儲到一張散列表里面。
使用keys指令可以掃出指定模式的key列表。
對方接著追問:如果這個redis正在給線上的業(yè)務提供服務,那使用keys指令會有什么問題?
這個時候你要回答redis關鍵的一個特性:redis的單線程的。keys指令會導致線程阻塞一段時間,線上服務會停頓,直到指令執(zhí)行完畢,服務才能恢復。這個時候可以使用scan指令,scan指令可以無阻塞的提取出指定模式的key列表,但是會有一定的重復概率,在客戶端做一次去重就可以了,但是整體所花費的時間會比直接用keys指令長。
Redis2.6開始redis-cli支持一種新的被稱之為pipe mode的新模式用于執(zhí)行大量數據插入工作。
1.Master最好不要做任何持久化工作,包括內存快照和AOF日志文件,特別是不要啟用內存快照做持久化。
2.如果數據比較關鍵,某個Slave開啟AOF備份數據,策略為每秒同步一次。
3.為了主從復制的速度和連接的穩(wěn)定性,Slave和Master最好在同一個局域網內。
4.盡量避免在壓力較大的主庫上增加從庫
5.Master調用BGREWRITEAOF重寫AOF文件,AOF在重寫的時候會占大量的CPU和內存資源,導致服務load過高,出現(xiàn)短暫服務暫停現(xiàn)象。
6.為了Master的穩(wěn)定性,主從復制不要用圖狀結構,用單向鏈表結構更穩(wěn)定,即主從關系為:Master<–Slave1<–Slave2<–Slave3…,這樣的結構也方便解決單點故障問題,實現(xiàn)Slave對Master的替換,也即,如果Master掛了,可以立馬啟用Slave1做Master,其他不變。
所謂 Redis 的并發(fā)競爭 Key 的問題也就是多個系統(tǒng)同時對一個 key 進行操作,但是最后執(zhí)行的順序和我們期望的順序不同,這樣也就導致了結果的不同!
推薦一種方案:分布式鎖(zookeeper 和 redis 都可以實現(xiàn)分布式鎖,如果不存在 Redis 的并發(fā)競爭 Key 問題,不要使用分布式鎖,這樣會影響性能)。基于zookeeper臨時有序節(jié)點可以實現(xiàn)的分布式鎖。大致思想為:每個客戶端對某個方法加鎖時,在zookeeper上的與該方法對應的指定節(jié)點的目錄下,生成一個唯一的瞬時有序節(jié)點。判斷是否獲取鎖的方式很簡單,只需要判斷有序節(jié)點中序號最小的一個。當釋放鎖的時候,只需將這個瞬時節(jié)點刪除即可。同時,其可以避免服務宕機導致的鎖無法釋放,而產生的死鎖問題。完成業(yè)務流程后,刪除對應的子節(jié)點釋放鎖。
在實踐中,當然是從以可靠性為主。所以首推Zookeeper。
Redis為單進程單線程模式,采用隊列模式將并發(fā)訪問變成串行訪問,且多客戶端對Redis的連接并不存在競爭關系Redis中可以使用SETNX命令實現(xiàn)分布式鎖。
當且僅當 key 不存在,將 key 的值設為 value。若給定的 key 已經存在,則 SETNX 不做任何動作。其中SETNX 是『SET if Not eXists』(如果不存在,則 SET)的簡寫。
返回值:設置成功,返回 1 。設置失敗,返回 0 。
從理論上說,只要我們設置了合理的鍵的過期時間,我們就能保證緩存和數據庫的數據最終是一致的。因為只要緩存數據過期了,就會被刪除。隨后讀的時候,因為緩存里沒有,就可以查數據庫的數據,然后將數據庫查出來的數據寫入到緩存中。除了設置過期時間,我們還可以通過新增、更改、刪除數據庫操作時同步更新 Redis,可以使用事物機制來保證數據的一致性。一般有如下四種方案,具體如下:
1.先更新數據庫,后更新緩存
2.先更新緩存,后更新數據庫
3.先刪除緩存,后更新數據庫
4先更新數據庫,后刪除緩存
第一種方案存在問題是:并發(fā)更新數據庫場景下,會將臟數據刷到緩存。
第二種方案存在的問題是:如果先更新緩存成功,但是數據庫更新失敗,則肯定會造成數據不一致。
目前主要用第三和第四種方案。
Redis的內存淘汰策略是指在Redis的用于緩存的內存不足時,怎么處理需要新寫入且需要申請額外空間的數據。
全局的鍵空間選擇性移除:
noeviction:當內存不足以容納新寫入數據時,新寫入操作會報錯。
allkeys-lru:當內存不足以容納新寫入數據時,在鍵空間中,移除最近最少使用的key。(這個是最常用的)
allkeys-random:當內存不足以容納新寫入數據時,在鍵空間中,隨機移除某個key。
設置過期時間的鍵空間選擇性移除
volatile-lru:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,移除 近 少使用的key。
volatile-random:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,隨機移除某個key。
volatile-ttl:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,有更早過期時間的key優(yōu)先移除。
Redis的過期鍵的刪除策略是指當Redis中的緩存的key過期了,Redis要如何處理。
Redis中提供了三種刪除策略:
1.定時刪除
當放入數據后,設置一個定時器,當定時器讀秒完畢后,將對應的數據從dict中刪除。
優(yōu)點: 內存友好,數據一旦過期就會被刪除
缺點: CPU不友好,定時器耗費CPU資源,并且頻繁的執(zhí)行清理操作也會耗費CPU資源。用時間換空間
2.惰性刪除
當數據過期的時候,不做任何操作。當訪問數據的時候,查看數據是否過期,如果過期返回null,并且將數據從內存中清除。如果沒過期,就直接返回數據。
優(yōu)點: CPU友好,數據等到過期并且被訪問的時候,才會刪除。
缺點: 內存不友好,會占用大量內存。用空間換時間
3.定期刪除
定期刪除是定時刪除和惰性刪除的折中方案。每隔一段時間對redisServer中的所有redisDb的expires依次進行隨機抽取檢查。
Redis中有一個server.hz定義了每秒鐘執(zhí)行定期刪除的次數,每次執(zhí)行的時間為250ms/server.hz。Redis中會維護一個current_db變量來標志當前檢查的數據庫。current_db++,當超過數據庫的數量的時候,會重新從0開始。
定期檢查就是執(zhí)行一個循環(huán),循環(huán)中的每輪操作會從current_db對應的數據庫中隨機依次取出w個key,查看其是否過期。如果過期就將其刪除, 并且記錄刪除的key的個數。如果過期的key個數大于w25%,就會繼續(xù)檢查當前數據庫,當過期的key小于w25%,會繼續(xù)檢查下一個數據庫。當執(zhí)行時間超過規(guī)定的最大執(zhí)行時間的時候,會退出檢查。一次檢查中可以檢查多個數據庫,但是最多檢查數量是redisServer中的數據庫個數,也就是最多只能從當前位置檢查一圈。
可以在同一個服務器部署多個 Redis 的實例,并把他們當作不同的服 務器來使用,在某些時候,無論如何一個服務器是不夠的, 所以, 如果你想使用多個 CPU,你可以考慮一下分片(shard)。
分區(qū)可以讓Redis管理更大的內存,Redis將可以使用所有機器的內存。如果沒有分區(qū),你最多只能使用一臺機器的內存。分區(qū)使Redis的計算能力通過簡單地增加計算機得到成倍提升,Redis的網絡帶寬也會隨著計算機和網卡的增加而成倍增長。
1.客戶端分區(qū)就是在客戶端就已經決定數據會被存儲到哪個redis節(jié)點或者從哪個redis節(jié)點讀取。大多數客戶端已經實現(xiàn)了客戶端分區(qū)。
2.代理分區(qū) 意味著客戶端將請求發(fā)送給代理,然后代理決定去哪個節(jié)點寫數據或者讀數據。代理根據分區(qū)規(guī)則決定請求哪些Redis實例,然后根據Redis的響應結果返回給客戶端。redis和memcached的一種代理實現(xiàn)就是Twemproxy。
3.查詢路由(Query routing) 的意思是客戶端隨機地請求任意一個redis實例,然后由Redis將請求轉發(fā)給正確的Redis節(jié)點。Redis Cluster實現(xiàn)了一種混合形式的查詢路由,但并不是直接將請求從一個redis節(jié)點轉發(fā)到另一個redis節(jié)點,而是在客戶端的幫助下直接redirected到正確的redis節(jié)點。
1.涉及多個key的操作通常不會被支持。例如你不能對兩個集合求交集,因為他們可能被存儲到不同的Redis實例(實際上這種情況也有辦法,但是不能直接使用交集指令)。
2.同時操作多個key,則不能使用Redis事務。
3.分區(qū)使用的粒度是key,不能使用一個非常長的排序key存儲一個數據集(The partitioning granularity is the key, so it is not possible to shard a dataset with a single huge key like a very big sorted set)
4.當使用分區(qū)的時候,數據處理會非常復雜,例如為了備份你必須從不同的Redis實例和主機同時收集RDB / AOF文件。
5.分區(qū)時動態(tài)擴容或縮容可能非常復雜。Redis集群在運行時增加或者刪除Redis節(jié)點,能做到最大程度對用戶透明地數據再平衡,但其他一些客戶端分區(qū)或者代理分區(qū)方法則不支持這種特性。然而,有一種預分片的技術也可以較好的解決這個問題。