你們好,我是南橘,從接觸java到如今也有差很少兩年時間了,兩年時間,從一名連java有幾種數據結構都不懂超級小白,到如今懂了一點點的進階小白,學到了很多的東西。知識越分享越值錢,我這段時間總結(包括從別的大佬那邊學習,引用)了一些日常學習和麪試中的重點(自我認爲),但願給你們帶來一些幫助java
這篇文章的出現,首先要感謝一我的三太子敖丙 ,就是他的文章讓我發現,原來Redis的知識如此的多姿多彩。恩恩,他的文章,我是期期都看mysql
這是這篇文章的思惟導圖,由於用的是免費版的軟件,因此有很多水印,須要原版的能夠問我要程序員
要學習Redis基礎知識,首先要知道Redis的五種基礎數據結構,咱們先從這五種數據結構的使用場景和一些工做中(面試中)容易出現的點來介紹面試
是redis中最基本的數據類型,一個key對應一個valueredis
適用狀況:算法
一、緩存: 經典使用場景,把經常使用信息,字符串,圖片或者視頻等信息放到redis中,redis做爲緩存層,mysql作持久化層,下降mysql的讀寫壓力。
2.計數器:redis是單線程模型,一個命令執行完纔會執行下一個,同時數據能夠一步落地到其餘的數據源。
3.session:經過redis實現session共享
複製代碼
對於Java中的HashMap,自己是一種KV對結構,如 value={{field1,value1},......fieldN,valueN}},很是好理解sql
適用狀況:數據庫
HashMap做爲緩存,相比於string更節省空間的維護緩存信息,適合存儲如用戶信息,視頻信息等
複製代碼
底層用字典dict實現數組
Redis 的鏈表至關於 Java 語言裏面的 LinkedList緩存
適用狀況:
一、List在Redis中既能夠作隊列也能夠作棧使用,它的插入和刪除操做很是快,時間複雜度爲 0(1),可是索引定位很慢,時間 複雜度爲 O(n)。
二、能夠做爲微博的時間軸,有人發佈微博,用lpush加入時間軸,展現新的列表信息。
三、能夠實現阻塞隊列,左進右出的隊列組完成設計
複製代碼
list底層使用quickList(快速鏈表)實現
在早期的設計中, 當列表對象中元素的長度比較小或者數量比較少的時候,採用ziplist來存儲,當列表對象中元素的長度比較大或者數量比較多的時候,則會轉而使用雙向列表linkedlist來存儲。
這兩種存儲方式都各有優缺點
3.2版本更新以後,list的底層實現變成了quickList
quickList是 zipList 和 linkedList 的混合體,它將 linkedList 按段切分,每一段使用 zipList 來緊湊存儲,多個 zipList 之間使用雙向指針串接起來。
Redis 的集合至關於 Java 語言裏面的 HashSet ,它內部的鍵值對是無序的、惟一 的。 它的內部實現至關於一個特殊的字典,字典中全部的 value 都是一個值 NULL 當集合中最後一個元素被移除以後,數據結構被自動刪除,內存被回收。
適用狀況:
一、標籤(tag),給用戶添加標籤,或者用戶給消息添加標籤,這樣有同一標籤或者相似標籤的能夠給推薦關注的事或者關注的人。
二、點贊,或點踩,收藏等,能夠放到set中實現
三、能夠用來存儲在某活動中中獎的用戶 ID ,由於有去重功能,能夠保證同 一個用戶不會中獎兩次。
複製代碼
它相似於 Java SortedSet HashMap 的結合體, 方面它是個 set ,保證 了內部 value 的惟性,另方面它可 以給每一個 value 賦予一個 score ,表明 這個 value 的排序權重。它的內部實現 用的是一種叫做「跳躍表」的數據 結構。
仍是和上期同樣,從一位大佬那邊借過來一張圖片給你們展現,什麼是跳躍表
從這張圖片,咱們能夠看出來:跳躍表的底層是一個順序鏈表,每隔一個節點有一個上層的指針指向下一個節點,並層層向上遞歸。這樣設計成相似樹形的結構,可使得對於鏈表的查找能夠到達二分查找的時間複雜度。
skiplist他不要求上下兩層鏈表之間個數的嚴格對應關係,他爲每一個節點隨機出一個層數。好比上圖第三個節點的隨機出的層數是4,那麼就把它插入到四層的空間上,而第四個節點隨機出的層數是1,那它就只存在第一層空間上。
ziplist是由一系列特殊編碼的連續內存塊組成的順序存儲結構,相似於數組,ziplist在內存中是連續存儲的,可是不一樣於數組,爲了節省內存 ziplist的每一個元素所佔的內存大小能夠不一樣
ziplist將一些必要的偏移量信息記錄在了每個節點裏,使之能跳到上一個節點或下一個節點。
除了這五大基礎數據結構,Redis還有更加專業的數據結構 HyperLogLog(基數統計的算法)、Geo(地理位置系列)、Pub\Sub(消息隊列)、Pipeline(管道)、BloomFiler(布隆過濾器),都在不一樣的地方有用到,有些我會在下文向你們介紹,還有一些深刻的我沒有了解這麼多,你們能夠去敖丙的文章中進一步瞭解
能夠將屢次IO往返時間縮減爲一次,前提是pipleline執行的指令之間沒有因果關係
管道(pipeline)能夠一次性發送多條命令並在執行完後一次性將結果返回,pipeline 經過減小客戶端與redis的通訊次數來實現下降往返延時時間,並且Pipeline 實現的原理是隊列,而隊列的原理是時先進先出,這樣就保證數據的順序性。
注意:pipeline機制能夠優化吞吐量,但沒法提供原子性/事務保障
隨着互聯網技術的飛速發展,愈來愈多的單體架構已經轉型成了分佈式架構,,分佈式架構確實能帶來性能和效率上的提高,可是也會帶來數據一致性的問題。
分佈式鎖,就是解決分佈式架構中數據一致性的專用武器,分佈式鎖須要知足一下三個方面方可放心使用:
排他性:在同一時間只會有一個客戶端能獲取到鎖,其它客戶端沒法同時獲取
避免死鎖:這把鎖在一段有限的時間以後,必定會被釋放(正常釋放或異常釋放)
高可用:獲取或釋放鎖的機制必須高可用且性能佳
目前,我所知道的分佈式鎖大概有三種主流方式實現,分別是zookpeer,redis,還有本地數據庫,今天我就介紹一下如何用redis實現分佈式鎖。
基於Redis實現的鎖機制,主要是依賴redis自身的原子操做
setnx爭搶鎖,再用expire添加過時時間
你沒有看錯,就是這麼簡單,若是懼怕不妥,好比爭搶鎖的時候尚未設置過時時間就忽然宕機之類的問題,能夠直接用jedis等封裝好的RedisTemplate把setnx和expire合成一條指令使用。
首先,單點故障的問題不可避免 其次,由於使用鎖的客戶端,和redis服務器,不在一塊兒啊!時間是有延遲的,咱們只能依靠redis的TTL命令來查詢鎖的剩餘時間,而後根據這個剩餘時間來判斷鎖是否超時。 然而在一般的計算機系統中,很難獲取到一個可靠的時間。
其實若是咱們使用的場景不須要那麼強的安全性精確性,這樣也足夠用了,至少我如今的公司夠用了(一個排名前列的第三方支付企業)。可是,精益求精是程序員們的本質,RedLock的出如今必定程度上解決了這個問題。
我不是很瞭解RedLock,只能稍微給你們介紹一下,流程以下:
- 客戶端獲取當前時間,生成一個隨機值做爲鎖的值(目的是更加精確的得到時間)
- 依次嘗試在全部5個redis上取得同一個鎖(使用相似單機redis鎖的方法, 使用一樣的key和同一個隨機值) 獲取鎖的操做自己須要設定一個比較小的超時時間(如5-50ms), 防止在一個掛掉的redis上浪費太多時間 若是一個redis不可用,要儘快開始嘗試下一個
- 客戶端計算獲取鎖一共用了多長時間,經過用當前時間減去第1步獲得的時間 若是客戶端獲取了多數redis上的這個鎖(3到五個5),而且這時尚未超過鎖的超時時間, 這個鎖就算是獲取成功了
- 若是鎖獲取成功了,有效時間就按鎖超時時間-獲取鎖花費時間算
- 若是失敗,嘗試在全部redis上解除鎖 (解除鎖的操做是一段lua script,刪除一個key若是key的value是第1步生成的隨機值)
固然,它也不能解決問題,可是, redis鎖只會在比較極端的狀況下出錯,若是不是須要特別精確,只須要保證絕大多數可靠的時候,能夠放心使用redis集羣或者redlock。
解決了分佈式鎖的問題,可是仍是沒有解決各類天災人禍的問題,因此,這就須要Redis集羣出馬了
Redis中有主從機制,一個主節點對應一個或多個從節點,主節點提供數據存取,從節點則是從主節點拉取數據備份,當這個主節點掛掉後,就會有這個從節點選取一個來充當主節點,從而保證集羣不會掛掉。先講一下Redis主從同步的流程:
同時,在2.8版本以後,Redis能夠自動判斷是須要全量同步仍是增量同步,效率比較高,增量同步其實就是在完成全量同步後,開始新複製時向主服務器發送PSYNC( )命令(runid是上次複製的主服務器id,offset是從服務器的複製偏移量),主服務器會根據這個兩個參數來決定作哪一種同步,判斷服務器id是否和本機相同,複製偏移量是否在緩衝區中。
關於這些集羣的東西一章內容確定寫不完,我會在之後的文章裏向你們介紹
Redis的本職工做是緩存,可是因爲它多才多藝,成爲隊列也不錯,有一些阻塞式的API讓其有能力作消息隊列;另外,作消息隊列的其餘特性例如FIFO(先入先出)也很容易實現,只須要一個List對象從頭取數據,從尾部塞數據便可
在Redis中,能夠利用 sorted-set 來作延時隊列
zadd key score1 value1 score2 value2
可是,Redis的延時隊列沒法返回ACK,因此須要本身實現
Redis有兩種持久化的方式,分別是RDB和AOF
RDB作鏡像全量持久化、AOF作增量持久化,由於RDB會耗費較長時間,不夠實時,在停機的時候會致使大量有效數據丟失,因此須要AOF來配合使用,在redis實例重啓時,會使用RDB持久化文件從新構建內存,再使用AOF重放近期的操做指令來實現完整恢復重啓以前的狀態。
RDB持久化是指在指定的時間間隔內將內存中的數據集快照寫入磁盤。也是默認的持久化方式,這種方式是就是將內存中數據以快照的方式寫入到二進制文件中,默認的文件名爲dump.rdb。RDB提供了三種機制來觸發持久化
一、save觸發方式---客戶端發起save請求
執行save命令期間,Redis不能處理其餘命令,直到RDB過程完成爲止
二、bgsave觸發方式--客戶端發起bgsave請求
執行該命令時,Redis會在後臺異步進行快照操做,快照同時還能夠響應客戶端請求
三、自動觸發
自動觸發是由咱們的配置文件來完成的,在redis.conf文件中配置,你們能夠去了解一下,這裏就不寫那麼多東西了
全量備份老是耗時的(隨機的傳說老是好的???),有時候咱們提供一種更加高效的方式AOF,Redis會將每個收到的寫命令都經過write函數追加到文件中,就是日誌記錄。
和RDB同樣,AOF也有三種同步機制:
一、always:同步持久化 每次發生數據變動會被當即記錄到磁盤 性能較差但數據完整性比較好
二、everysec:每秒同步,異步操做,每秒記錄 若是一秒內宕機,有數據丟失
三、no:從不一樣步
Redis自己的機制是AOF持久化開啓存在AOF文件時,優先加載AOF文件。AOF文件不存在時候,加載RDB文件。加載AOF\RDB文件後,Redis啓動成功。AOF\RDB文件存在錯誤時,Redis啓動失敗並打印錯誤信息。
不要問AOF和RDB用哪一個,個人經驗就是,全都用。 RDB同步快,可是要損失最多五分鐘的內容,AOF同步慢,可是每秒同步的狀況下最多損失1s的內容,損失的內容也能夠經過日誌去找回。
AOF日誌中能夠進行sync屬性的配置,若是不要求性能,在每條寫指令時都sync一下磁盤,就不會丟失數據,但在高性能要求下每次都sync是不現實的,通常都使用定時sync,好比1s一次,這個時候就最多丟失1s的數據。
emmmm,第二篇文章也慢慢的寫完了,在寫文章的過程當中,真的是發現本身懂得越多,不懂越多,並且由於公司週末要加班,因此這將Redis分紅兩篇來完成,但願你們能夠喜歡~~ 同時須要思惟導圖的話,能夠聯繫我,畢竟知識越分享越香!
![]()