經常使用的SQL數據庫的數據都是存在磁盤中的,雖然在數據庫底層也作了對應的緩存來減小數據庫的IO壓力,但因爲數據庫的緩存通常是針對查詢的內容,並且粒度也比較小,通常只有表中的數據沒有發生變更的時候,數據庫的緩存纔會產生做用,但這並不能減小業務邏輯對數據庫的增刪改操做的IO壓力,所以緩存技術應運而生,該技術實現了對熱點數據的高速緩存,能夠大大緩解後端數據庫的壓力。react
1.支持簡單數據類型
2.不支持數據持久化存儲
3.不支持主從
4.不支持分片
linux
1.數據類型豐富
2.支持數據磁盤持久化存儲
3.支持主從
4.支持分片
redis
Redis的效率很高,官方給出的數據是100000+QPS(query per second),這是由於:
算法
1.Redis徹底基於內存,絕大部分請求是純粹的內存操做,執行效率高。
2.Redis使用單進程單線程模型的(K,V)數據庫,將數據存儲在內存中,存取均不會受到硬盤IO的限制,所以其執行速度極快,另外單線程也能處理高併發請求,還能夠避免頻繁上下文切換和鎖的競爭,若是想要多核運行也能夠啓動多個實例。
3.數據結構簡單,對數據操做也簡單,Redis不使用表,不會強制用戶對各個關係進行關聯,不會有複雜的關係限制,其存儲結構就是鍵值對,相似於HashMap,HashMap最大的優勢就是存取的時間複雜度爲O(1)。
4.Redis使用多路I/O複用模型,爲非阻塞IO(非阻塞IO會另寫一篇解釋,能夠先行百度)。數據庫
注:Redis採用的I/O多路複用函數:epoll/kqueue/evport/select
選用策略:
1.因地制宜,優先選擇時間複雜度爲O(1)的I/O多路複用函數做爲底層實現。
2.因爲select要遍歷每個IO,因此其時間複雜度爲O(n),一般被做爲保底方案。
3.基於react設計模式監聽I/O事件。
後端
最基本的數據類型,其值最大可存儲512M,二進制安全(Redis的String能夠包含任何二進制數據,包含jpg對象等)。 設計模式
String元素組成的字典,適用於存儲對象。 緩存
列表,按照String元素插入順序排序。其順序爲後進先出。因爲其具備棧的特性,因此能夠實現如「最新消息排行榜」這類的功能。 安全
String元素組成的無序集合,經過哈希表實現(增刪改查時間複雜度爲O(1)),不容許重複。 bash
經過分數來爲集合中的成員進行從小到大的排序。
用於計數的HyperLogLog、用於支持存儲地理位置信息的Geo。
keys test* //返回全部以test爲前綴的key
SCAN 0 MATCH test* COUNT 10 //每次返回10條以test爲前綴的key
分佈式鎖是控制分佈式系統之間共同訪問共享資源的一種鎖的實現。若是一個系統,或者不一樣系統的不一樣主機之間共享某個資源時,每每須要互斥,來排除干擾,知足數據一致性。
分佈式鎖須要解決的問題以下:
1.互斥性:任意時刻只有一個客戶端獲取到鎖,不能有兩個客戶端同時獲取到鎖。
2.安全性:鎖只能被持有該鎖的客戶端刪除,不能由其它客戶端刪除。
3.死鎖:獲取鎖的客戶端由於某些緣由而宕機繼而沒法釋放鎖,其它客戶端再也沒法獲取鎖而致使死鎖,此時須要有特殊機制來避免死鎖。
4.容錯:當各個節點,如某個redis節點宕機的時候,客戶端仍然可以獲取鎖或釋放鎖。
SETNX key value:若是key不存在,則建立並賦值。該命令時間複雜度爲O(1),若是設置成功,則返回1,不然返回0。
EXPIRE key seconds
程序:
RedisService redisService = SpringUtils.getBean(RedisService.class);
long status = redisService.setnx(key,"1");
if(status == 1){
redisService.expire(key,expire);
doOcuppiedWork();
}
複製代碼
這段程序存在的問題:假設程序運行到第二行出現異常,那麼程序來不及設置過時時間就結束了,則key會一直存在,等同於鎖一直被持有沒法釋放。出現此問題的根本緣由爲:原子性得不到知足。
解決:從Redis2.6.12版本開始,咱們就可使用Set操做,將Setnx和expire融合在一塊兒執行,具體作法以下。
SET KEY value [EX seconds] [PX milliseconds] [NX|XX]
複製代碼
EX second:設置鍵的過時時間爲second秒。
PX millisecond:設置鍵的過時時間爲millisecond毫秒。
NX:只在鍵不存在時,纔對鍵進行設置操做。
XX:只在鍵已經存在時,纔對鍵進行設置操做。
注:SET操做成功完成時纔會返回OK,不然返回nil。
有了SET咱們就能夠在程序中使用相似下面的代碼實現分佈式鎖了:
RedisService redisService = SpringUtils.getBean(RedisService.class);
String result = redisService.set(lockKey,requestId,SET_IF_NOT_EXIST,SET_WITH_EXPIRE_TIME,expireTime);
if("OK.equals(result)"){
doOcuppiredWork();
}
複製代碼
使用上文所說的Redis的數據結構中的List做爲隊列 Rpush生產消息,LPOP消費消息。
BLPOP key [key ...] timeout:阻塞直到隊列有消息或者超時。
可否實現生產一次就能讓多個消費者消費呢?
發送者(pub)發送消息,訂閱者(sub)接收消息。 訂閱者能夠訂閱任意數量的頻道
持久化,即將數據持久存儲,而不因斷電或其它各類複雜外部環境影響數據的完整性。因爲Redis將數據存儲在內存而不是磁盤中,因此內存一旦斷電,Redis中存儲的數據也隨即消失,這每每是用戶不指望的,因此Redis有持久化機制來保證數據的安全性。
Redis目前有兩種持久化方式,即RDB和AOF,RDB是經過保存某個時間點的全量數據快照實現數據的持久化,當恢復數據時,直接經過rdb文件中的快照,將數據恢復。
RDB持久化會在某個特定的間隔保存那個時間點的全量數據的快照。 RDB配置文件: redis.conf:
save 900 1 #在900s內若是有1條數據被寫入,則產生一次快照。
save 300 10 #在300s內若是有10條數據被寫入,則產生一次快照
save 60 10000 #在60s內若是有10000條數據被寫入,則產生一次快照
stop-writes-on-bgsave-error yes
#stop-writes-on-bgsave-error :
若是爲yes則表示,當備份進程出錯的時候,
主進程就中止進行接受新的寫入操做,這樣是爲了保護持久化的數據一致性的問題。
複製代碼
SAVE:阻塞Redis的服務器進程,直到RDB文件被建立完畢。SAVE命令不多被使用,由於其會阻塞主線程來保證快照的寫入,因爲Redis是使用一個主線程來接收全部客戶端請求,這樣會阻塞全部客戶端請求。
BGSAVE:該指令會Fork出一個子進程來建立RDB文件,不阻塞服務器進程,子進程接收請求並建立RDB快照,父進程繼續接收客戶端的請求。子進程在完成文件的建立時會向父進程發送信號,父進程在接收客戶端請求的過程當中,在必定的時間間隔經過輪詢來接收子進程的信號。咱們也能夠經過使用lastsave指令來查看bgsave是否執行成功,lastsave能夠返回最後一次執行成功bgsave的時間。
1.根據redis.conf配置裏的SAVE m n 定時觸發(實際上使用的是BGSAVE)
2.主從複製時,主節點自動觸發。
3.執行Debug Reload
4.執行Shutdown且沒有開啓AOF持久化。
啓動:
1.檢查是否存在子進程正在執行AOF或者RDB的持久化任務。若是有則返回false。
2.調用Redis源碼中的rdbSaveBackground方法,方法中執行fork()產生子進程執行rdb操做。
1.內存數據全量同步,數據量大的情況下,會因爲I/O而嚴重影響性能。
2.可能會由於Redis宕機而丟失從當前至最近一次快照期間的數據。
AOF持久化是經過保存Redis的寫狀態來記錄數據庫的。相對RDB來講,RDB持久化是經過備份數據庫的狀態來記錄數據庫,而AOF持久化是備份數據庫接收到的指令。
1.AOF記錄除了查詢之外的全部變動數據庫狀態的指令。
2.以增量的形式追加保存到AOF文件中。
1.打開redis.conf配置文件,將appendonly屬性改成yes。
2.修改appendfsync屬性,該屬性能夠接收三種參數,分別是always,everysec,no,always表示老是即時將緩衝區內容寫入AOF文件當中,everysec表示每隔一秒將緩衝區內容寫入AOF文件,no表示將寫入文件操做交由操做系統決定,通常來講,操做系統考慮效率問題,會等待緩衝區被填滿再將緩衝區數據寫入AOF文件中。
appendonly yes
#appendsync always
appendfsync everysec
# appendfsync no
複製代碼
隨着寫操做的不斷增長,AOF文件會愈來愈大。假設遞增一個計數器100次,若是使用RDB持久化方式,咱們只要保存最終結果100便可,而AOF持久化方式須要記錄下這100次遞增操做的指令,而事實上要恢復這條記錄,只須要執行一條命令就行,因此那一百條命令實際能夠精簡爲一條。Redis支持這樣的功能,在不中斷前臺服務的狀況下,能夠重寫AOF文件,一樣使用到了COW(寫時拷貝)。重寫過程以下:
1.調用fork(),建立一個子進程。
2.子進程把新的AOF寫到一個臨時文件裏,不依賴原來的AOF文件。
3.主進程持續將新的變更同時寫到內存和原來的AOF裏。
4.主進程獲取子進程重寫AOF的完成信號,往新AOF同步增量變更。
5.使用新的AOF文件替換掉舊的AOF文件。
RDB優勢:全量數據快照,文件小,恢復快。
RDB缺點:沒法保存最近一次快照以後的數據。
AOF優勢:可讀性高,適合保存增量數據,數據不易丟失。
AOF缺點:文件體積大,恢復時間長。
redis4.0以後推出了此種持久化方式,RDB做爲全量備份,AOF做爲增量備份,而且將此種方式做爲默認方式使用。
在上述兩種方式中,RDB方式是將全量數據寫入RDB文件,這樣寫入的特色是文件小,恢復快,但沒法保存最近一次快照以後的數據,AOF則將redis指令存入文件中,這樣又會形成文件體積大,恢復時間長等弱點。
在RDB-AOF方式下,持久化策略首先將緩存中數據以RDB方式全量寫入文件,再將寫入後新增的數據以AOF的方式追加在RDB數據的後面,在下一次作RDB持久化的時候將AOF的數據從新以RDB的形式寫入文件。這種方式既能夠提升讀寫和恢復效率,也能夠減小文件大小,同時能夠保證數據的完整性。在此種策略的持久化過程當中,子進程會經過管道從父進程讀取增量數據,在以RDB格式保存全量數據時,也會經過管道讀取數據,同時不會形成管道阻塞。能夠說,在此種方式下的持久化文件,前半段是RDB格式的全量數據,後半段是AOF格式的增量數據。此種方式是目前較爲推薦的一種持久化方式。
Pipeline和Linux的管道相似,它可讓Redis批量執行指令。
Redis基於請求/響應模型,單個請求處理須要一一應答。若是須要同時執行大量命令,則每條命令都須要等待上一條命令執行完畢後才能繼續執行,這中間不只僅多了RTT,還屢次使用了系統IO。Pipeline因爲能夠批量執行指令,因此能夠節省屢次IO和請求響應往返的時間。可是若是指令之間存在依賴關係,則建議分批發送指令。
Redis通常是使用一個Master節點來進行寫操做,而若干個Slave節點進行讀操做,Master和Slave分別表明了一個個不一樣的RedisServer實例,另外按期的數據備份操做也是單獨選擇一個Slave去完成,這樣能夠最大程度發揮Redis的性能,爲的是保證數據的弱一致性和最終一致性。另外,Master和Slave的數據不是必定要即時同步的,可是在一段時間後Master和Slave的數據是趨於同步的,這就是最終一致性。
主從模式弊端:當Master宕機後,Redis集羣將不能對外提供寫入操做。Redis Sentinel可解決這一問題。
解決主從同步Master宕機後的主從切換問題:
1.監控:檢查主從服務器是否運行正常。
2.提醒:經過API向管理員或者其它應用程序發送故障通知。
3.自動故障遷移:主從切換(在Master宕機後,將其中一個Slave轉爲Master,其餘的Slave從該節點同步數據)。
按照某種規則去劃分數據,分散存儲在多個節點上。經過將數據分到多個Redis服務器上,來減輕單個Redis服務器的壓力。
既然要將數據進行分片,那麼一般的作法就是獲取節點的Hash值,而後根據節點數求模,但這樣的方法有明顯的弊端,當Redis節點數須要動態增長或減小的時候,會形成大量的Key沒法被命中。因此Redis中引入了一致性Hash算法。該算法對2^32 取模,將Hash值空間組成虛擬的圓環,整個圓環按順時針方向組織,每一個節點依次爲0、一、2...2^32-1,以後將每一個服務器進行Hash運算,肯定服務器在這個Hash環上的地址,肯定了服務器地址後,對數據使用一樣的Hash算法,將數據定位到特定的Redis服務器上。若是定位到的地方沒有Redis服務器實例,則繼續順時針尋找,找到的第一臺服務器即該數據最終的服務器位置。
Hash環在服務器節點不多的時候,容易遇到服務器節點不均勻的問題,這會形成數據傾斜,數據傾斜指的是被緩存的對象大部分集中在Redis集羣的其中一臺或幾臺服務器上。
這篇準(tou)備(lan)了至關久的時間,由於有些東西總感受本身拿不許不敢往上寫,差點自閉,就算如今發出來了也感受有不少地方是須要改動的。若是有同窗以爲哪裏寫的不對勁的,評論區或者私聊我...嗯,我不要你以爲,我要我以爲。
本文圖片來自網絡,侵刪。
歡迎你們訪問個人我的博客:Object's Blog