A. Redis --- 八種數據類型(基本命令)
String、Hash、List、Set和Zset。
等同於java中的, Map<String,String> string 是redis裡面的最基本的數據類型,一個key對應一個value。
應用場景 :String是最常用的一種數據類型,普通的key/value存儲都可以歸為此類,如用戶信息,登錄信息和配置信息等;
實現方式 :String在redis內部存儲默認就是一個字元串,被redisObject所引用,當遇到incr、decr等操作(自增自減等原子操作)時會轉成數值型進行計算,此時redisObject的encoding欄位為int。
Redis雖然是用C語言寫的,但卻沒有直接用C語言的字元串,而是自己實現了一套字元串。目的就是為了提升速度,提升性能。 Redis構建了一個叫做簡單動態字元串(Simple Dynamic String),簡稱SDS。
Redis的字元串也會遵守C語言的字元串的實現規則,即 最後一個字元為空字元。然而這個空字元不會被計算在len里頭。
Redis動態擴展步驟:
Redis字元串的性能優勢
常用命令 :set/get/decr/incr/mget等,具體如下;
ps:計數器(字元串的內容為整數的時候可以使用),如 set number 1。
補充:
等同於java中的: Map<String,Map<String,String>> ,redis的hash是一個string類型的field和value的映射表, 特別適合存儲對象。 在redis中,hash因為是一個集合,所以有兩層。第一層是key:hash集合value,第二層是hashkey:string value。所以判斷是否採用hash的時候可以參照有兩層key的設計來做參考。並且注意的是, 設置過期時間只能在第一層的key上面設置。
應用場景 :我們要存儲一個用戶信息對象數據,其中包括用戶ID、用戶姓名、年齡和生日,通過用戶ID我們希望獲取該用戶的姓名或者年齡或者生日;
實現方式 :Redis的Hash實際是內部存儲的Value為一個HashMap,並提供了直接存取這個Map成員的介面。如,Key是用戶ID, value是一個Map。 這個Map的key是成員的屬性名,value是屬性值 。這樣對數據的修改和存取都可以直接通過其內部Map的Key(Redis里稱內部Map的key為field), 也就是通過 key(用戶ID) + field(屬性標簽) 就可以操作對應屬性數據。 當前HashMap的實現有兩種方式 :當HashMap的成員比較少時Redis為了節省內存會採用類似一維數組的方式來緊湊存儲,而不會採用真正的HashMap結構,這時對應的value的redisObject的encoding為zipmap,當成員數量增大時會自動轉成真正的HashMap,此時redisObject的encoding欄位為int。
常用命令 :hget/hset/hgetall等,具體如下:
等同於java中的 Map<String,List<String>> ,list 底層是一個鏈表,在redis中,插入list中的值,只需要找到list的key即可,而不需要像hash一樣插入兩層的key。 list是一種有序的、可重復的集合。
應用場景 :Redis list的應用場景非常多,也是Redis最重要的數據結構之一,比如twitter的關注列表,粉絲列表等都可以用Redis的list結構來實現;
實現方式 :Redis list的實現為一個 雙向鏈表 ,即可以支持反向查找和遍歷,更方便操作,不過帶來了部分額外的內存開銷,Redis內部的很多實現,包括 發送緩沖隊列 等也都是用的這個數據結構。
常用命令 :lpush/rpush/lpop/rpop/lrange等,具體如下:
性能總結 :
它是一個字元串鏈表,left、right都可以插入添加。
等同於java中的 Map<String,Set<String>> ,Set 是一種無序的,不能重復的集合。並且在redis中,只有一個key它的底層由hashTable實現的,天生去重。
應用場景 :Redis set對外提供的功能與list類似是一個列表的功能,特殊之處在於set是可以自動去重的,當你需要存儲一個列表數據,又不希望出現重復數據時,set是一個很好的選擇,並且 set提供了判斷某個成員是否在一個set集合內的重要介面 ,這個也是list所不能提供的;如保存一些標簽的名字。標簽的名字不可以重復,順序是可以無序的。
實現方式 :set 的內部實現是一個 value永遠為null的HashMap,實際就是通過計算hash的方式來快速排重的,這也是set能提供判斷一個成員是否在集合內的原因。
常用命令 :sadd/spop/smembers/sunion等,具體如下:
ZSet(Sorted Set:有序集合) 每個元素都會關聯一個double類型的分數score,分數允許重復,集合元素按照score排序( 當score相同的時候,會按照被插入的鍵的字典順序進行排序 ),還可以通過 score 的范圍來獲取元素的列表。
應用場景 :Redis sorted set的使用場景與set類似,區別是set不是自動有序的,而sorted set可以 通過用戶額外提供一個優先順序(score)的參數來為成員排序,並且是插入有序的,即自動排序。 當你需要一個有序的並且不重復的集合列表,那麼可以選擇sorted set數據結構,比如twitter 的public timeline可以以發表時間作為score來存儲,這樣獲取時就是自動按時間排好序的。
底層實現 : zset 是 Redis 提供的一個非常特別的數據結構,常用作排行榜等功能,以用戶 id 為 value ,關注時間或者分數作為 score 進行排序。實現機制分別是 zipList 和 skipList 。規則如下:
zipList:滿足以下兩個條件
skipList:不滿足以上兩個條件時使用跳錶、組合了hash和skipList
為什麼用skiplist不用平衡樹?
主要從內存佔用、對范圍查找的支持和實現難易程度這三方面總結的原因。
拓展:mysql為什麼不用跳錶?
常用命令 :zadd/zrange/zrem/zcard等;
官網地址: https://redis.io/commands/geoadd
可以用來推算兩地之間的距離,方圓半徑內的人。
關於經度緯度的限制: https://www.redis.net.cn/order/3685.html
一般我們使用Hyperloglog做基數統計。
什麼是基數?就是一個集合中不重復的數的個數。
集合A:{1,3,5,7,9,7}
集合B:{1,3,5,7,9}
AB集合的基數都是5
應用:統計網站的訪問量(一個人訪問網站很多次仍然算作一次)。
優點:佔用的內存是固定的,找2^64次方個數的基數,只需要12KB內存。
缺點:有0.81%的錯誤率,可以忽略不計
概述: bitmap 存儲的是連續的二進制數字(0 和 1),通過 bitmap, 只需要一個 bit 位來表示某個元素對應的值或者狀態,key 就是對應元素本身 。 我們知道 8 個 bit 可以組成一個 byte,所以 bitmap 本身會極大的節省儲存空間。
應用場景: 適合需要保存狀態信息(比如是否簽到、是否登錄...)並需要進一步對這些信息進行分析的場景。比如用戶簽到情況、活躍用戶情況、用戶行為統計(比如是否點贊過某個視頻)。
針對上面提到的一些場景,這里進行進一步說明。
使用場景一:用戶行為分析 很多網站為了分析你的喜好,需要研究你點贊過的內容。
使用場景二:統計活躍用戶
使用時間作為 key,然後用戶 ID 為 offset,如果當日活躍過就設置為 1
那麼我該如果計算某幾天/月/年的活躍用戶呢(暫且約定,統計時間內只有有一天在線就稱為活躍),有請下一個 redis 的命令
使用場景三:用戶在線狀態
對於獲取或者統計用戶在線狀態,使用 bitmap 是一個節約空間效率又高的一種方法。
只需要一個 key,然後用戶 ID 為 offset,如果在線就設置為 1,不在線就設置為 0。
補充 :
巨人的肩膀:
https://www.cnblogs.com/Small-sunshine/p/11687809.html
https://mp.weixin.qq.com/s/CMu7oXVIKp2s-PXTdMlimA
B. redis鏀鎸佹湇鍔$閿佸畾鍚
Redis鏈韜涓嶆敮鎸佹湇鍔$閿佸畾銆
Redis鏄涓縐嶉珮鎬ц兘鐨勫唴瀛樻暟鎹搴擄紝瀹冭騫挎硾鐢ㄤ簬鍚勭嶉渶瑕佸揩閫熻誨啓鏁版嵁鐨勫満鏅銆傜劧鑰岋紝Redis騫舵病鏈夊湪鏈嶅姟絝鎻愪緵鏄懼紡鐨勯攣瀹氭満鍒躲
鍦ㄥ疄璺典腑錛屽紑鍙戣呴氬父浣跨敤Redis鐨勫叾浠栫壒鎬ф潵瀹炵幇閿佸畾鐨勬晥鏋溿備緥濡傦紝浣犲彲浠ヤ嬌鐢≧edis鐨凷ETNX錛圫et if Not Exists錛夊懡浠ゆ潵灝濊瘯鑾峰彇涓涓閿併傝繖涓鍛戒護浼氬皾璇曡劇疆涓涓閿鍊煎癸紝濡傛灉閿宸茬粡瀛樺湪錛屽垯璁劇疆澶辮觸錛涘惁鍒欙紝璁劇疆鎴愬姛銆傞氳繃榪欑嶆柟寮忥紝浣犲彲浠ュ湪涓涓鍒嗗竷寮忕幆澧冧腑瀹炵幇涓縐嶁滃厛鍒板厛寰椻濈殑閿佸畾鏈哄埗銆傚綋涓涓榪涚▼鑾峰彇浜嗛攣涔嬪悗錛屽叾浠栬繘紼嬪氨鏃犳硶鍐嶈幏鍙栧悓涓涓閿侊紝鐩村埌閿佽閲婃斁銆
鍏蜂綋瀹炵幇鏃訛紝鍙浠ヨ繖鏍鋒搷浣滐細
1. 榪涚▼灝濊瘯浣跨敤SETNX鍛戒護璁劇疆涓涓閿佸畾鐨勯敭錛堟瘮濡傚彨鍋"lock"錛夛紝濡傛灉榪欎釜閿宸茬粡瀛樺湪錛岄偅涔堣存槑宸茬粡鏈夊叾浠栬繘紼嬭幏鍙栦簡閿侊紝榪欎釜榪涚▼灝遍渶瑕佺瓑寰呮垨鑰呭皾璇曞仛鍏朵粬澶勭悊銆
2. 濡傛灉SETNX鍛戒護鎴愬姛錛岄偅涔堣繘紼嬪氨鑾峰彇浜嗛攣錛屽彲浠ヨ繘琛屼竴浜涢渶瑕佺嫭鍗犺祫婧愮殑鎿嶄綔銆
3. 鍦ㄥ畬鎴愰渶瑕佺殑鎿嶄綔鍚庯紝榪涚▼搴旇ヤ嬌鐢―EL鍛戒護鍒犻櫎榪欎釜閿佸畾鐨勯敭錛屼互閲婃斁閿併傝繖鏍鳳紝鍏朵粬榪涚▼灝辮兘澶熻幏鍙栭攣騫惰繘琛屾搷浣滀簡銆
闇瑕佹敞鎰忕殑鏄錛岃繖鍙鏄涓縐嶅熀紜鐨勯攣瀹氬疄鐜版柟寮忥紝瀹冨苟娌℃湁澶勭悊涓浜涘彲鑳藉嚭鐜扮殑闂棰橈紝姣斿傝繘紼嬪湪鎸佹湁閿佺殑鏃跺欏穿婧冿紝瀵艱嚧閿佹棤娉曡閲婃斁銆傞拡瀵硅繖浜涢棶棰橈紝浣犲彲鑳介渶瑕佹洿澶嶆潅鐨勮В鍐蟲柟妗堬紝姣斿備嬌鐢≧edis鐨凟XPIRE鍛戒護涓洪攣璁劇疆涓涓榪囨湡鏃墮棿錛屾垨鑰呬嬌鐢≧edis鐨勪簨鍔★紙MULTI/EXEC錛夋潵淇濊瘉閿佸畾鍜屾搷浣滅殑鍘熷瓙鎬с傜劧鑰岋紝榪欎簺閮介渶瑕佷綘鐨勫簲鐢ㄧ▼搴忔潵澶勭悊錛岃屼笉鏄鐢盧edis鏈嶅姟絝鐩存帴鎻愪緵銆
浠ヤ笂淇℃伅浠呬綔鍙傝冿紝浣犲彲浠ユ牴鎹鍏蜂綋闇奼傚拰鐜澧冩潵璁捐″拰瀹炵幇閫傚悎浣犵殑閿佸畾鏈哄埗銆
C. Redis 學習總結(3) Redis 哨兵模式
在實際開發中不會僅僅部署一個 Redis 伺服器,為了獲得高可用,Redis 哨兵模式 則是高可用的一種選擇。
本文先介紹下 哨兵模式,再介紹了如何在 springboot 項目中使用。
這意味著使用 Sentinel (哨兵模式),您可以創建一個 Redis 部署,它可抵抗某些類型的故障(進行故障遷移)而無需人工干預。
它有這些功能:
Sentinel 的分布式特性
Redis Sentinel 是一個分布式系統,多個 Sentinel 進程協同工作,有這些優勢:
部署前需要了解:
三個節點的基本配置
法定人數和仲裁
在配置 哨兵模式時,要指定一個 quorum,它可理解為「法定人數」。
假設有3 個 哨兵,法定人數為2。那麼:
哨兵和副本的自動發現
Sentinel 與其他 Sentinel 保持連接,以便相互檢查彼此的可用性並交換消息。
但是,您不需要在您運行的每個 Sentinel 實例中配置其他 Sentinel 地址的列表,因為 Sentinel 使用 Redis 實例的 Pub/Sub 功能來發現正在監視相同主節點和副本的其他 Sentinel。
類似地,您不需要配置附加到主伺服器的副本地址在哪裡,因為 Sentinel 會通過查詢 Redis 自動發現它們。
參考我的另一篇文章:
一般需要三個節點,每個節點有一個 redis 和一個哨兵。
下面再分別描述。
我這里按三個 節點,先配置 redis 的主從復制。1個節點作為 master ,2個副本。
配置節點1:master
這里的 redis 作為 master 主redis,其他兩個節點作為從節點。
我的文件夾名字叫 box1,這里編輯一個 box1/redis.conf 文件,主要配置內容如下:
配置節點2:副本
編輯一個 box2/redis.conf 文件,主要配置內容如下:
配置節點3:副本
編輯一個 box3/redis.conf 文件,主要配置內容如下:
分別啟動這三個redis
命令行執行 redis-server ,並指定 配置文件的路徑參數。
如何查看「主從復制」是否配置成功?
使用 info replication 命令,操作如下:
副本節點設置為只讀?
從 Redis 2.6 開始,副本已被默認設置為 只讀,無需額外配置。.
一般情況下,至少會需要三個哨兵對redis 進行監控,我們可以通過修改埠啟動多個sentinel 服務。
第一個哨兵:
哨兵的 默認埠是 26379 ,這里不改。
第二個哨兵:
修改哨兵埠。
第三個哨兵:
修改哨兵埠。
啟動哨兵
使用 redis-sentinel 命令,分別啟動這三個哨兵
哨兵的自動發現
當三個哨兵都啟動後,在各個哨兵的列印日誌里可以看到, 三個哨兵已互相發現了彼此的存在 。
至此,配置完畢了,我們有三個 redis,和三個哨兵,看下截圖。
模擬 master 宕機
按 ctrl+c 停止 master ,其位於 6379 。停止後,從日誌可以看到,哨兵和 redis副本先努力繼續連接 6379,反復幾次失敗後,開始選舉出新的 master。截圖如下:
至此,配置完畢。
我們看下 springboot 項目的客戶端如何配置 以訪問 哨兵模式的 redis。
Redis 哨兵支持
對於處理高可用Redis,Spring Data Redis 已經支持Redis Sentinel,使用RedisSentinelConfiguration,如下例所示:
Jedis 和 Lettuce 兩種 redis 驅動都可以支持。
RedisSentinelConfiguration 也可以用可以 通過 PropertySource 來設置,它允許您設置以下屬性:
配置application.yml
比如我這里修改我的 application.yml 文件如下:
我的配置文件示例: https://github.com/vir56k/java_demo/tree/master/redis-sentinel
我的 springboot 配置實例: https://github.com/vir56k/java_demo/tree/master/redis-sentinel/springboot_redis_demo
Redis官網 sentinel 介紹
https://redis.io/topics/sentinel
spring-data/data-redis
https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/#redis:sentinel
https://www.cnblogs.com/jaycekon/p/6237562.html
END
D. redis 命令執行過程
redis數據淘汰原理
redis過期數據刪除策略
redis server事件模型
redis cluster mget 引發的討論
redis 3.x windows 集群搭建
redis 命令執行過程
redis string底層數據結構
redis list底層數據結構
redis hash底層數據結構好鬧
redis set底層數據結構
redis zset底層數據結構
redis 客戶端管理
redis 主從同步-slave端
redis 主從同步-master端
redis 主從超時檢測
redis aof持久化
redis rdb持久化
redis 數據恢復過程
redis TTL實現原理
redis cluster集群建立
redis cluster集群選主
這篇文章的目的是為了描述redis server在處理client命令的執行過程,大概包括流程圖、源碼、以及redis的命令格式說明,redis的通信協議參考自redis的則頌 官網 。
整個redis的server端命令執行過程就如下面這個流程圖:
nread = read(fd, c->querybuf+qblen, readlen);負責讀取命令數,通過processInputBuffer進行下一步處理。
核心在於processInlineBuffer處理內聯命令,processMultibulkBuffer處理批量命令包括get/set等,核心的processCommand用於執行命令。
執行命令的過程其實主要是尋找命令對應的執行函數,通過lookupCommand查找對應的執行命令,通過call執行命令。
負責執行命令 c->cmd->proc 並更新統計信息,執行完成後負責同步數據 propagate 。
主要是負責同步數據到AOF文件和slave節點,feedAppendOnlyFile負責友盯罩同步到AOF文件,replicationFeedSlaves負責同步
AOF涉及的緩存有多份,包括
包含了命令和對應執行函數的映射關系,應該看上去很清晰命令。
協議的一般格式如下,注意前面的*或者$等字元,結尾的 是分隔符。
其中, 回復中的第二個元素為空。