Redis学习笔记·
目錄
?
第一章 nosql概述(not only sql)
?1.1為什么需要nosql
1.2 nosql數據庫的四大分類
NoSQL數據庫的四大分類表格分析
第二章 redis概述
2.1概述:
2.2 redis特性:
第三章?API
redis中文網:Redis命令中心(Redis commands) -- Redis中國用戶組(CRUG)
3.1全局命令
3.2字符串
3.3?hash:
3.4?列表list
3.5集合set
3.6有序集合zset
3.7?位圖(bit)
3.8?GEO
3.9?Pub/Sub?發布訂閱
3.10?Stream
第四章 redis管理器
4.1?全局命令
4.2.1.鍵的遷移:把部分數據遷移到另一臺redis服務器
4.3.1?redis數據庫管理
第五章 redis持久化
第六章?redis主從復制
6.1方式:
6.2相關命令:
6.3注意事項:
6.4Redis主從拓撲:
6.5復制原理:
6.6數據同步:
6.7 主從的缺點:
6.8?主從故障如何故障轉移
第七章?部署模式
7.1單節點模式
7.2主從模式
7.3集群模式
7.4讀寫分離?
7.5哨兵機制(sentinel)的高可用
第八章?redis集群高可用
8.1?分區規則
9.總結
10. 布隆過濾器
11. 策略?
11.1. 過期策略:?
11.2. 內存回收
11.3. LRU(最近最少使用)
11.4.?LFU(最近最不常用)
12.多路復用
第一章 nosql概述(not only sql)
非關系型數據庫
?1.1為什么需要nosql
????????? ? High Performance-高并發讀寫
? ? ? ? ? ? ? Huge Storage - 海量數據的高效率存儲和訪問
? ? ? ? ? ? ? High Scalability && High Availabitiy - 高可擴展性和高可用性
?NoSQL數據庫在以下的這幾種情況下比較適用:
????????????1?、數據模型比較簡單;
????????????2、需要靈活性更強的IT系統;
????????????3、對數據庫性能要求較高;
????????????4、不需要高度的數據一致性;
????????????5、對于給定key,比較容易映射復雜值的環境
nosql存在的一些問題
1.2 nosql數據庫的四大分類
- 鍵值對(Key-Value)存儲 redis
????這一類數據庫主要會使用到一個哈希表,這個表中有一個特定的鍵和一個指針指向特定的數據。Key/value模型對于IT系統來說的優勢在于簡單、易部署。但是如果DBA只對部分值進行查詢或更新的時候,Key/value就顯得效率低下了。舉例如:Tokyo Cabinet/Tyrant, Redis, Voldemort, Oracle BDB.
- ?列存儲:
???????這部分數據庫通常是用來應對分布式存儲的海量數據。鍵仍然存在,但是它們的特點是指向了多個列。這些列是由列家族來安排的。如:Cassandra, HBase, Riak.
- 文檔數據庫:mongodb
??????文檔型數據庫的靈感是來自于Lotus Notes辦公軟件的,而且它同第一種鍵值存儲相類似。該類型的數據模型是版本化的文檔,半結構化的文檔以特定的格式存儲,比如JSON。文檔型數據庫可 以看作是鍵值數據庫的升級版,允許之間嵌套鍵值。而且文檔型數據庫比鍵值數據庫的查詢效率更高。如:CouchDB, MongoDb. 國內也有文檔型數據庫SequoiaDB,已經開源。
- 圖形數據庫(多用于社交網絡)InfoGrid
??????圖形結構的數據庫同其他行列以及剛性結構的SQL數據庫不同,它是使用靈活的圖形模型,并且能夠擴展到多個服務器上。NoSQL數據庫沒有標準的查詢語言(SQL),因此進行數據庫查詢需要制定數據模型。許多NoSQL數據庫都有REST式的數據接口或者查詢API。如:Neo4J, InfoGrid, Infinite Graph.
NoSQL數據庫的四大分類表格分析
??
| 鍵值(key-value) | Tokyo Cabinet/Tyrant, Redis, Voldemort, Oracle BDB | 內容緩存,主要用于處理大量數據的高訪問負載,也用于一些日志系統等等。 | Key 指向 Value 的鍵值對,通常用hash table來實現 | 查找速度快 | 數據無結構化,通常只被當作字符串或者二進制數據 |
| 列存儲數據庫 | Cassandra, HBase, Riak | 分布式的文件系統 | 以列簇式存儲,將同一列數據存在一起 | 查找速度快,可擴展性強,更容易進行分布式擴展 | 功能相對局限 |
| 文檔型數據庫 | CouchDB, MongoDb | Web應用(與Key-Value類似,Value是結構化的,不同的是數據庫能夠了解Value的內容) | Key-Value對應的鍵值對,Value為結構化數據 | 數據結構要求不嚴格,表結構可變,不需要像關系型數據庫一樣需要預先定義表結構 | 查詢性能不高,而且缺乏統一的查詢語法。 |
| 圖形(Graph)數據庫 | Neo4J, InfoGrid, Infinite Graph | 社交網絡,推薦系統等。專注于構建關系圖譜 | 圖結構 | 利用圖結構相關算法。比如最短路徑尋址,N度關系查找等 | 需要對整個圖做計算才能得出需要的信息,而且這種結構不太好做分布式的集群方案。 |
第二章 redis概述
2.1概述:
用C語言開發的一個開源的、高性能的鍵值對(key-value)數據庫,通過提供多種鍵值數據類型來適應不同場景的數據需求。高性能鍵值對數據庫。其中value可以為string、hash、list、set、zset等多種數據結構,可以滿足很多應用場景。還提供了鍵過期,發布訂閱,事務,流水線,等附加功能,
流水線: Redis 的流水線功能允許客戶端一次將多個命令請求發送給服務器, 并將被執行的多個命令請求的結果在一個命令回復中全部返回給客戶端, 使用這個功能可以有效地減少客戶端在執行多個命令時需要與服務器進行通信的次數。
2.2 redis特性:
1〉速度快,數據放在內存中,官方給出的讀寫性能10萬/S,與機器性能也有關
? ? ? ? a,數據放內存中是速度快的主要原因
? ? ? ? b,C語言實現,與操作系統距離近
? ? ? ? c,使用了單線程架構,預防多線程可能產生的競爭問題
2〉鍵值對的數據結構服務器
3〉簡單穩定:單線程
4〉持久化:發生斷電或機器故障,數據可能會丟失,持久化到硬盤
?????? 6〉主從復制:實現多個相同數據的redis副本
?????? 8〉高可用和分布式:哨兵機制實現高可用,保證redis節點故障發現和自動轉移
?????? 9〉客戶端語言多:java php python c c++ nodejs等
redis支持的鍵值數據類型
- 字符串類型
- 列表類型
- 有序集合類型
- 散列類型
- 集合類型
應用場景
- 緩存:合理使用緩存加快數據訪問速度,降低后端數據源壓力
- 任務隊列(不推薦使用)
- 數據過期處理(精確到毫秒)
- 分布式集群架構中的session分離
- 社交網絡:贊、踩、粉絲、下拉刷新
- 計數器
- 排行榜:按照熱度排名,按照發布時間排行,主要用到列表和有序集合
redis的優點
非常快速:每一秒鐘可執行大約110000次的set操作,81000次的get操作。并發大概十萬。
支持多種數據類型:字符串,列表,有序/三列集合,集合等等類型。
操作具有原子性:原子性保證了并發情況下,服務器能接收更新的值。
注:單線程。
單線程架構
?? 列舉例子:三個客戶端同時執行命令
???????? 客戶端1:set name test
???????? 客戶端2:incr num
???????? 客戶端3:incr num
執行過程:發送指令-〉執行命令-〉返回結果
執行命令:單線程執行,所有命令進入隊列,按順序執行,使用I/O多路復用解決I/O問題,后面有介紹(由于讀寫操作等待用戶輸入或輸出都是阻塞的,所以 I/O 操作在一般情況下往往不能直接返回,這會導致某一文件的 I/O 阻塞導致整個進程無法對其它客戶提供服務?,IO多路復用模型是建立在內核提供的多路分離函數select基礎之上的,使用select函數可以避免同步非阻塞IO模型中輪詢等待的問題)
單線程快原因:純內存訪問, 非阻塞I/O(使用多路復用),單線程避免線程切換和競爭產生資源消耗
問題:如果某個命令執行,會造成其它命令的阻塞
第三章?API
redis中文網:Redis命令中心(Redis commands) -- Redis中國用戶組(CRUG)
3.1全局命令
切換數據庫:select 2
| 命令 | 描述 |
| keys * | 查看所有鍵,如果存在大量鍵,線上禁止使用此指令 |
| dbsize | 鍵總數 |
| exists key??? | 檢查鍵是否存在,存在返回1,不存在返回0 |
| expire key seconds? | 鍵過期 |
| ttl?key | ?查看剩余的過期時間 |
| type key? | 鍵的數據結構類型,鍵不存在返回none |
| flushall | 清空所有數據庫節點,redis默認16個節點 |
| flushdb | 清空當前數據庫節點(0) |
| select datanode | 切換節點,總共16個節點,默認是0節點。 |
3.2字符串
? ? ?3.2.1字符串類型:字符串(xml,json),數字(整形,浮點數),二進制(圖片,音、視頻)最大不能超過512MB。
? ? ?3.2.2?API:
| set | |
| set key value?? | 設值 |
| set key value? ex 10 | // ex表示過期,單位是秒; |
| setnx?key?value | 不存在key時才能插入,存在key返回0失敗,不存在key返回1成功;注:setnx可用做分布式鎖 |
| mset?key1?value1?key2?value2....... | 批量操作設值 |
| get | |
| ?get?key? | 獲取值 |
| mget key1 key2..... | 批量獲取 |
| 運算 | |
| incr?key | 自增,必須為整數自加1,非整數返回錯誤,無key鍵從0自增返回1 |
| decr?key | 遞減整數 |
| incrby key value | 加法,整數值+value |
| incrbyfloat? key?1.1 | 浮點數?加法 |
| ?decrby?key?value? | 減法整數 |
| 其它 | |
| append?key?value | 追加值 |
| strlen?key | 獲取字符串長度 |
| getrange?key?index1,index2 | 截取字符串,返回下標第index1個到?第index2個,下標從0開始。 |
| object encoding?key | 內部編碼 int :8個字長;embstr:小于等于39字節字符串;raw大于39字節的字符串 |
3.3?hash:
是一個string類型的field和value和value的映射表,hash特別適合用于存儲對象;
| hset?key?field?value | 設值,field?value類似于map中的key?value |
| hget?key?field | 取值 |
| hdel?key?field | 刪值 |
| hlen?key | 長度 |
| hmset?key field1 value1 field2 value2....? | 批量設值 |
| hmget?key?field1?field2... | 批量取值 |
| hkeys key | 獲取所有field |
| hvals key | 獲取所有value |
| hgetall?key | 獲取所有field?value |
| hincrby key field value hincrbyfloat key field value | 加法 |
| hdecrby?key?field value? | 減法 |
內部編碼:ziplist<壓縮列表>和hashtable<哈希表>
?當field個數少且沒有大的value時,內部編碼為ziplist
?當value大于64字節,內部編碼由ziplist變成hashtable
1.字符串:
優點:簡單直觀,每個鍵對應一個值
缺點:鍵數過多,占用內存多,用戶信息過于分散,不用于生產環境
HASH類型是稀疏,每個鍵可以有不同的filed, 若用redis模擬做關系復雜查詢開發因難,維護成本高
2.將對象序列化存入reids:set user:1 serialize(userInfo);
優點:編程簡單,若使用序列化合理內存使用率高
缺點:序列化與反序列化有一定開銷,更新屬性時需要把userInfo全取出來進行反序列化,更新后再序列化到redis
3.使用hash類型:?hmset user:1 name james age 23 sex boy
優點:簡單直觀,使用合理可減少內存空間消耗
缺點:要控制ziplist與hashtable兩種編碼轉換,且hashtable會消耗更多內存
總結:對于更新不多的情況下,可以使用序列化,對于VALUE值不大于64字節可以使用hash類型
3.4?列表list
用來存儲多個有序的字符串,一個列表最多可存2的32次方減1個元素
因為有序,可以通過索引下標獲取元素或某個范圍內元素列表,列表元素可以重復
| rpush key val1 val2 val3 | 從右向左添加val1 val2 val3 |
| lpush?key?val1?val2?val3 | 從左向右添加 |
| linsert?key before val1 val2 | 在val1之前插入val2 ,after為之后 |
| lrange?key 0 -1 | 從左到右獲取列表的所有元素 |
| lindex?key?index | index下標,正數從左到右,負數-1表示最末尾 |
| llen?key | 返回當前列表長度 |
| lpop?key | 把最左邊的第一個元素刪除?返回刪除的值 |
| rpop?key | 刪除最右邊的元素,返回刪除的值 |
| lrem?key?count?valu | 刪除指定元素value,刪除count個 |
| ltrim?key?index1 index2 | 只保留從第index1到index2的元素,其它全部刪除,下標從0開始 |
| set?key?index?value | 把下標為index的值改成value。 |
內部編碼:
object encoding?key
1,ziplist:當元素個數少且沒大元素,編碼為ziplist,減少內存的使用
2,linkedlist:當元素超過512個,或元素超過64字節,內部編碼變成linkedlist鏈表;
3.5集合set
保存多元素,與列表不一樣的是不允許有重復元素,且集合是無序,一個集合最多可存2的32次方減1個元素,除了支持增刪改查,還支持集合交集、并集、差集;
| sadd?key?v1 v2 v3 | 當插入相同的元素,則重復無效,返回0, |
| smember? key | 獲取key集合的所有元素,返回結果無序 |
| srem? key?value | 刪除指定value |
| scard?key | 計算元素個數 |
| sismember? key?value | 判斷元素value在key集合中是否存在,存在返回1,不存在返回0 |
| srandmember?key?count | 隨機返回count個元素 |
| spop?key?count | 隨機獲取count個元素,并刪除 |
| sinter?key1?key2? key3..... | 求多個結合的交集 |
| sunion? key1 key2 key3...... | 求并集? |
| sdiff key1 key2..... | 差集 |
| sinterstore? key? key1 key2... | 將key1 key2...交集保存到key |
| sunionstore key? key1 key2.. | 將key1 key2...合集保存到key |
| sdiffstore key? key1 key2... | 將key1 key2...差集保存到key |
內部編碼:??object encoding?key
1.intset:當元素個數少(小于512個)且都為整數,redis使用intset減少內存的使用
2.hashtable:當超過512個或不為整數(比如a b)時,編碼為hashtable
使用場景:
? 標簽,社交,查詢有共同興趣愛好的人,智能推薦
3.6有序集合zset
有序集合,可排序的,但是唯一。
| zadd key score member [score member......] | 如果該元素已經存在則會用新的分數替換原有的分數。返回值是新加入到集合中的元素個數,不包含之前已經存在的元素。? |
| zadd?key?value?member? | 點贊數1, 返回操作成功的條數1 |
| ?zadd?key?nx?value?member? | 添加的必須不存在,主用于添加 |
| ?zadd?key?xx incr?value?member? | 必須存在,主用于修改 |
| zadd? key xx ch incr?value?member | ? |
| zscore key member | 獲取元素的分數? |
| zrem key member [member …] | 移除有序集key中的一個或多個成員,不存在的成員將被忽略。當key存在但不是有序集類型時,返回一個錯誤。 |
| zrange key start stop [withscores] | 按照元素分數從小到大的順序返回索引從start到stop之間的所有元素(包含兩端的元素)? |
| zrevrange key start stop [withscores] | 按照元素分數從大到小的順序返回索引從start到stop之間的所有元素(包含兩端的元素)? |
| zrange scoreboard 0 1 withscores | 如果需要獲得元素的分數的可以在命令尾部加上WITHSCORES參數 |
| zrange key member | 獲取元素的排名,從小到大 |
| zrevrange key member | 獲取元素的排名,從大到小 |
| zrangebyscore key min max [withscores] [LIMIT offset count] | 獲得指定分數范圍的元素? |
| zincrby key increment member | 增加某個元素的分數? |
| zcard key | 獲得集合中元素的數量? |
| zcount key min max | 獲得指定分數范圍內的元素個數? |
| ZREMRANGEBYRANK key start stop | 按照排名范圍刪除元素? |
| ZREMRANGEBYSCORE key min max | 按照分數范圍刪除元素? |
| zinterstore destination numkeys key ... [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX] | 有序集合交集 ?destination:交集產生新的元素存儲鍵名稱 ???????? numkeys:? 要做交集計算的鍵個數 ???????? key :元素鍵值 ????? ???weights:每個被選中的鍵對應值乘weight, 默認為1 |
| zunionstore destination numkeys key ... [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX] | 有序集合并集(合并去重): ?destination:交集產生新的元素存儲鍵名稱 ???????? numkeys:? 要做交集計算的鍵個數 ???????? key :元素鍵值 ???????? weights:每個被選中的鍵對應值乘weight, 默認為1 |
有序集合內部編碼 object encoding?key?
1.ziplist:當元素個數少(小于128個),元素值小于64字節時,使用ziplist編碼,可有效減少內存的使用
2.skiplist:? ?大于128個元素或元素值大于64字節時為skiplist編碼與LIST SET 對比:
| 列表 | 是 | 是 | 索引下標 | 時間軸,消息隊列 |
| 集合 | 否 | 否 | 無 | 標簽,社交 |
| 有序結合 | 否 | 是 | 分值 | 排行榜,點贊數 |
3.7?位圖(bit)
3.8?GEO
? ? ? ? GEO 3.2版本開始對GEO(地理位置)的支持| 命令 | 描述 |
| GEOADD | 增加地理位置的坐標,可以批量添加地理位置 |
| GEODIST | 獲取兩個地理位置的距離 |
| GEOHASH | 獲取某個地理位置的geohash值 |
| GEOPOS | 獲取指定位置的坐標,可以批量獲取多個地理位置的坐標 |
| GEORADIUS | 根據給定地理位置坐標獲取指定范圍內的地理位置集合 (注意:該命令的中心點由輸入的經度和緯度決定) |
| GEORADIUSBYMEMBER | 根據給定成員的位置獲取指定范圍內的位置信息集合 (注意:該命令的中心點是由給定的位置元素決定) |
3.9?Pub/Sub?發布訂閱
生產者發布消息到頻道
消費者訂閱頻道,從頻道接收消息
生產者、消費者彼此相互不了解
3.10?Stream
類似kafka
第四章 redis管理器
4.1?全局命令
?4.1.1?鍵重命名 rename oldKey newkey??? //格式
???????????? rename oldKey newKey???? //若oldKey之前存在則被覆蓋
???????????? set name james ;set name1 mike //數據初始化
???????????? renamenx name name1 //重命名失敗,只有當name1不存在才能改名
?4.1.2? 返回隨機鍵? dbsize? //redis有16個庫,查看當前庫的鍵值對總數
?????????????? randomkey? //返回隨機鍵
?4.1.3 鍵過期:expire name:03 20? //鍵name:03 在10秒后過期
?????????? ttl name:03??????? //查看過期按秒到計時,當返回-2說明已刪除
?????????? pttl name:03?????? //查看過期按毫秒到時計
?????????? set name:05 james? //初始化數據
?????????? pexpire name:05 20000? //20000毫秒(20S)后過期
?????????? expire name:06 -2? //直接過期,和del一樣
?????????
?expireat name:04 1516971599? //設置在2018/01/26 20:59:59過期
????????? 時間轉時間戳:網址http://tool.chinaz.com/Tools/unixtime.aspx?
???????? ?persist? key? ? ? ?//去掉過期?
????????? 注意:對于字符串重設值后,expire無效,
????????? set name james
????????? expire name 50
????????? ttl name
????????? set name james1 //此時expire取消
????????? ttl name?? //返回-1, 長期有效
4.2.1.鍵的遷移:把部分數據遷移到另一臺redis服務器
? 1, move key db? //reids有16個庫, 編號為0-15
這種模式不建議在生產環境使用,在同一個reids里可以玩
?2, dump key;
?restore key ttl value//實現不同redis實例的鍵遷移,ttl=0代表沒有過期時間
3,migrate指令遷移到其它實例redis,在1.111服務器上將test移到118
4.2.2 鍵的遍歷:
? redis提供了兩個命令來遍歷所有的鍵
?1,鍵全量遍歷:
mset country china city bj name james? //設置3個字符串鍵值對
keys? * //返回所有的鍵, *匹配任意字符多個字符
keys *y //以結尾的鍵,
keys n*e //以n開頭以e結尾,返回name
keys n?me? //? ?問號代表只匹配一個字符? 返回name,全局匹配
keys n?m*?? //返回name
keys [j,l]*? //返回以j l開頭的所有鍵? keys [j]ames 全量匹配james
考慮到是單線程, 在生產環境不建議使用,如果鍵多可能會阻塞
如果鍵少,可以
2,漸進式遍歷
mset? a a b b c c d d e e f f g g h h i i j j k k l l m m n n o o p p q q r r s s t t u u v v w w x x y y z z??? //初始化26個字母鍵值對
字符串類型:
?scan 0 match n* count 20 //匹配以n開頭的鍵,取20條,第一次scan 0開始
第二次從游標4096開始取20個以n開頭的鍵,相當于一頁一頁的取
當最后返回0時,鍵被取完
比如將old:user開頭的元素全刪掉
注:可有效地解決keys命令可能產生的阻塞問題
- 除scan字符串外:還有以下
- SCAN?命令用于迭代當前數據庫中的數據庫鍵。
- SSCAN?命令用于迭代集合鍵中的元素。
- HSCAN?命令用于迭代哈希鍵中的鍵值對。
- ZSCAN?命令用于迭代有序集合中的元素(包括元素成員和元素分值)。
用法和scan一樣
4.3.1?redis數據庫管理
select 0?? //共16個庫, 0 --15, select切換數據庫
set name james
select 1
get name? //隔離了,取不到,和mysql不同庫一樣
其中redis3.0以后的版本慢慢弱化了這個功能,如在redis cluster中只允許0數據庫
?????? 原因:1,redis單線程,如果用多個庫,這些庫使用同一個CPU,彼此會有影響
2,多數據庫,調試與運維麻煩,若有一個慢查詢,會影響其它庫查詢速度
3,來回切換,容易混亂
?????? flushdb: 只清空當前數據庫的鍵值對? dbsiz? 0
?????? flushall:? 清空所有庫的鍵值對? (這兩個指令慎用!!!!)
4.4 redis功能細解
4.4.1?慢查詢原因分析:與mysql一樣:當執行時間超過閥值,會將發生時間 耗時 命令記錄
?redis命令生命周期:發送 排隊 執行 返回
????? 慢查詢只統計第3個執行步驟的時間
??
????? 預設閥值:兩種方式,默認為10毫秒
?? 使用config set完后,若想將配置持久化保存到redis.conf,要執行config rewrite
???????? 2,redis.conf修改:找到slowlog-log-slower-than 10000 ,修改保存即可
????? 注意:slowlog-log-slower-than =0記錄所有命令 -1命令都不記錄
????? 原理:慢查詢記錄也是存在隊列里的,slow-max-len 存放的記錄最大條數,比如設置的slow-max-len=10,當有第11條慢查詢命令插入時,隊列的第一條命令就會出列,第11條入列到慢查詢隊列中, 可以config set動態設置,也可以修改redis.conf完成配置。
?獲取隊列里慢查詢的命令:slowlog get???? 查詢返回的結構如下
?獲取慢查詢列表當前的長度:slowlog len? //以上只有1條慢查詢,返回1
? 對慢查詢列表清理(重置):slowlog reset //再查slowlog len 此時返回0 清空
? 對于線上slow-max-len配置的建議:線上可加大slow-max-len的值,記錄慢查詢存長命令時redis會做截斷,不會占用大量內存,線上可設置1000以上
? 對于線上slowlog-log-slower-than配置的建議:默認為10毫秒,根據redis并發量來調整,對于高并發比建議為1毫秒
? 注意:1,慢查詢只記錄命令在redis的執行時間,不包括排隊、網絡傳輸時間
4.5?redis-cli詳解
? ./redis-cli -r 3 -h 192.168.1.111 -a 12345678 ping //返回pong表示127.0.0.1:6379能通,r代表次數
./redis-cli -r 100 -i 1 info |grep used_memory_human //每秒輸出內存使用量,輸100次,i代表執行的時間間隔
./redis-cli -p 6379? -h 192.168.1.111 -a 12345678?
第五章 redis持久化
redis支持RDB和AOF兩種持久化機制,持久化可以避免因進程退出而造成數據丟失;
????? 5.1RDB持久化把當前進程數據生成快照(.rdb)文件保存到硬盤的過程,有手動觸發和自動觸發
??????? 手動觸發有save和bgsave兩命令
???????? save命令:阻塞當前Redis,直到RDB持久化過程完成為止,若內存實例比較大會造成長時間阻塞,線上環境不建議用它
???????? bgsave命令:redis進程執行fork操作創建子線程,由子線程完成持久化,阻塞時間很短(微秒級),是save的優化,在執行redis-cli shutdown關閉redis服務時,如果沒有開啟AOF持久化,自動執行bgsave;
???????? 顯然bgsave是對save的優化。
5.2RDB文件的操作
?? 命令:config set dir /usr/local? //設置rdb文件保存路徑
?? 備份:bgsave? //將dump.rdb保存到usr/local下
?? 恢復:將dump.rdb放到redis安裝目錄與redis.conf同級目錄,重啟redis即可
?? 優點:1,壓縮后的二進制文,適用于備份、全量復制,用于災難恢復
???????? 2,加載RDB恢復數據遠快于AOF方式
?? 缺點:1,無法做到實時持久化,每次都要創建子進程,頻繁操作成本過高
???????? 2,保存后的二進制文件,存在老版本不兼容新版本rdb文件的問題?????????
5.3AOF持久化
? 針對RDB不適合實時持久化,redis提供了AOF持久化方式來解決
? 開啟:redis.conf設置:appendonly yes? (默認不開啟,為no)
? 默認文件名:appendfilename "appendonly.aof"??
????? 流程說明:1,所有的寫入命令(set hset)會append追加到aof_buf緩沖區中
???????? 2,AOF緩沖區向硬盤做sync同步
???????? 3,隨著AOF文件越來越大,需定期對AOF文件rewrite重寫,達到壓縮
??? ?????4,當redis服務重啟,可load加載AOF文件進行恢復
AOF持久化流程:命令寫入(append),文件同步(sync),文件重寫(rewrite),重啟加載(load)
redis的AOF配置詳解:
appendonly yes???? //啟用aof持久化方式
# appendfsync always //每收到寫命令就立即強制寫入磁盤,最慢的,但是保證完全的持久化,不推薦使用
appendfsync everysec //每秒強制寫入磁盤一次,性能和持久化方面做了折中,推薦
# appendfsync no??? //完全依賴os,性能最好,持久化沒保證(操作系統自身的同步)
no-appendfsync-on-rewrite? yes? //正在導出rdb快照的過程中,要不要停止同步aof
auto-aof-rewrite-percentage 100? //aof文件大小比起上次重寫時的大小,增長率100%時,重寫
auto-aof-rewrite-min-size 64mb?? //aof文件,至少超過64M時,重寫
如何從AOF恢復?
1. 設置appendonly yes;
2. 將appendonly.aof放到dir參數指定的目錄;
3. 啟動Redis,Redis會自動加載appendonly.aof文件。
redis重啟時恢復加載AOF與RDB順序及流程:
1,當AOF和RDB文件同時存在時,優先加載
2,若關閉了AOF,加載RDB文件
3,加載AOF/RDB成功,redis重啟成功
4,AOF/RDB存在錯誤,redis啟動失敗并打印錯誤信息
5.4 Redis 4.0 混合持久化
重啟 Redis 時不使用 rdb 來恢復內存狀態,就會丟失大量數據。
使用 AOF 日志重放相對rdb要慢很多,redis重啟就會非常慢
Redis 4.0 帶來了一個新的持久化選項——混合持久化。將 rdb 文件的內容和增量的 AOF 日志文件存在一起。這里的 AOF 日志不再是全量的日志,而是自持久化開始到持久化結束的這段時間發生的增量 AOF 日志,通常這部分 AOF 日志很小。
?Redis 重啟的時先加載 rdb ,再重放增量 AOF 日志。比起以前的?AOF 全量文件重放,重啟效率因此得到大幅提升。
第六章?redis主從復制
6.1方式:
- 1.新增redis6380.conf, 加入? slaveof 192.168.1.111 6379,? 在6379啟動完后再啟6380,完成配置;
- 2.redis-server --slaveof 192.168.1.111 6379
6.2相關命令:
- 查看狀態:info replication
- 斷開主從復制:在slave節點,執行6380:>slaveof no one
- 斷開后再變成主從復制:6380:> slaveof 127.0.0.1 6379
- 數據較重要的節點,主從復制時使用密碼驗證: requirepass
- 從節點建議用只讀模式slave-read-only=yes, 若從節點修改數據,主從數據不一致??????
- 傳輸延遲:主從一般部署在不同機器上,復制時存在網絡延時問題,redis提供repl-disable-tcp-nodelay參數決定是否關閉TCP_NODELAY,默認為關閉
6.3注意事項:
參數關閉時:
???? 無論大小都會及時發布到從節點,占帶寬,適用于主從網絡好的場景,
參數啟用時:
???? 主節點合并所有數據成TCP包節省帶寬,默認為40毫秒發一次,取決于內核,主從的同步延遲40毫秒,適用于網絡環境復雜或帶寬緊張,如跨機房
6.4Redis主從拓撲:
一主一從:用于主節點故障轉移從節點,當主節點的“寫”命令并發高且需要持久化,可以只在從節點開啟AOF(主節點不需要)
一主多從:針對“讀”較多的場景,“讀”由多個從節點來分擔,但節點越多,主節點同步到多節點的次數也越多,影響帶寬,也加重主節點的穩定
樹狀主從:一主多從的缺點(主節點推送次數多壓力大)可用些方案解決,
????????? 主節點只推送一次數據到從節點1,再由從節點2推送到11,減輕主節點推送的壓力
6.5復制原理:
執行slave master port后,
與主節點連接,同步主節點的數據,6380:>info replication:查看主從及同步信息
6.6數據同步:
redis 2.8版本以上使用psync命令完成同步,過程分“全量”與“部分”復制
全量復制:
???? 一般用于初次復制場景(第一次建立SLAVE后全量)
部分復制:
??? 網絡出現問題,從節占再次連主時,主節點補發缺少的數據,每次數據增加同步
心跳:
???? 主從有長連接心跳,主節點默認每10S向從節點發ping命令,repl-ping-slave-period控制發送頻率
6.7 主從的缺點:
1,主從復制,若主節點出現問題,則不能提供服務,需要人工修改配置將從變主
2,主從復制主節點的寫能力單機,能力有限
3,單機節點的存儲能力也有限
6.8?主從故障如何故障轉移
??? A,主節點(master)故障,從節點slave-1端執行 slaveof no one后變成新主節點;
??? B,其它的節點成為新主節點的從節點,并從新節點復制數據;
??? C,需要人工干預,無法實現高可用。
第七章?部署模式
7.1單節點模式
7.2主從模式
提供高性能的緩存服務和數據高可靠。
主從架構-雙副本:
? 主節點提供日常服務訪問,從節點提供HA高可用,當主節點發生故障,系統會自動在30秒內切換至從節點,保證業務平穩運行。
可靠性
-
主從節點位于不同物理機。主節點對外提供寫入訪問。當主節點出現故障,HA系統會自動進行主從切換,保證業務平穩運行。
-
默認開啟數據持久化功能。支持數據備份功能,用戶可以針對備份集回滾實例或者克隆實例,有效地解決數據誤操作等問題。
使用場景
-
Redis作為持久化數據存儲使用的業務標準版提供持久化機制及備份恢復機制,極大地保證數據可靠性。
-
單個Redis性能壓力可控的業務由于Redis原生采用單線程機制,性能在10萬QPS以下的業務建議使用。如果需要更高的性能要求,請選用集群版本。
-
Redis命令相對簡單,排序、計算類命令較少的業務由于Redis的單線程機制,CPU會成為主要瓶頸。如排序、計算類較多的業務建議選用集群版配置。
主從架構-單副本:
可以在沒有數據可靠性要求的純緩存場景充分發揮性能優勢。
使用場景
-
純緩存類業務場景
單副本版本只有一個數據庫節點,節點出現故障時,系統會重新拉起一個Redis進程(沒有數據),當節點故障業務自動切換完成后,應用程序需要將數據重新預熱,以免對后端數據庫產生訪問壓力沖擊。單副本架構不能提供數據可靠性,如果發生節點故障,您需要重新對業務進行預熱,因此,在對數據可靠性要求較高的敏感性業務中,建議選用雙副本架構。
-
單個Redis性能壓力可控
由于Redis原生采用單線程機制,CPU為單核能力,性能在8萬QPS的業務建議使用。如果需要更高的性能要求,請選用集群版配置。
-
Redis命令相對簡單,排序、計算類命令較少
由于Redis的單線程機制,CPU為主要瓶頸。如排序、計算類較多的業務建議選用集群版配置。
7.3集群模式
? ? ? ?突破了redis自身單線程瓶頸,滿足大容量、高性能的業務需求。
集群版-雙副本:
7.4讀寫分離?
? ? ?提供高可用,高性能,靈活度高,解決熱點數據集中及高并發讀取的業務需求。節約用戶運維成本。
? ? ? 針對讀多寫少的業務場景,提供高可用、高性能、靈活的讀寫分離服務,滿足熱點數據集中及高并發讀取的業務需求,最大化地節約運維成本。讀寫分離版主要由主備節點、只讀節點、Proxy(代理)節點和高可用系統組成。
7.5哨兵機制(sentinel)的高可用
原理:當主節點出現故障時,由Redis Sentinel自動完成故障發現和轉移,并通知應用方,實現高可用性。
哨兵機制-故障轉移流程A
A,由Sentinel節點定期監控發現主節點是否出現了故障
sentinel會向master發送心跳PING來確認master是否存活,如果master在“一定時間范圍”內不回應PONG 或者是回復了一個錯誤消息,那么這個sentinel會主觀地(單方面地)認為這個master已經不可用了
B,當主節點出現故障,此時假設3個Sentinel節點共同選舉了Sentinel3節點為領導者sentinel,負載處理主節點的故障轉移
C,由Sentinel3領導者節點執行故障轉移,過程和主從復制一樣,但是自動執行
RedisSentinel如何安裝與部署
我們以3個Sentinel節點、2個從節點、1個主節點為例進行安裝部署
安裝與部署1(先搭主從)
前提:
先搭建好一主兩從redis的主從復制,和之前復制的搭建一樣,搭建方式如下:
A主節點6379節點(/usr/local/bin/conf/redis6379.conf):
????? 修改 requirepass 12345678,注釋掉#bind 127.0.0.1
B從節點redis6380.conf和redis6381.conf:
????? 修改 requirepass 12345678 ,注釋掉#bind 127.0.0.1,
????? 加上masterauth 12345678 ,加上slaveof 127.0.0.1 6379
注意:當主從起來后,主節點可讀寫,從節點只可讀不可寫
哨兵機制核心配置
redis sentinel哨兵機制配置(也是3個節點):
?? /usr/local/bin/conf/sentinel_26379.conf?
?? /usr/local/bin/conf/sentinel_26380.conf
?? /usr/local/bin/conf/sentinel_26381.conf
將三個文件的端口改成: 26379?? 26380?? 26381
sentinel monitor mymaster 192.168.1.111 6379 2? //監聽主節點6379
sentinel auth-pass mymaster 12345678???? //連接主節點時的密碼
三個配置除端口外,其它一樣
配完此腳本,哨兵機制可正常啟動運行。
哨兵其它配置
sentinel monitor mymaster 192.168.1.111 6379 2? //監控主節點的IP地址端口
sentinel auth-pass mymaster 12345678? //sentinel連主節點的密碼
sentinel config-epoch mymaster 2????? //執行故障轉移時, 最多可以有多少個從節點同時對新的主節點進行數據同步
sentinel leader-epoch mymaster 2
sentinel failover-timeout mymaster 180000 //故障轉移超時時間180s,???????????????????????????
??? a,如果轉移超時失敗,下次轉移時時間為之前的2倍;
??? b,從節點變主節點時,從節點執行slaveof no one命令一直失敗的話,當時間超過180S時,則故障轉移失敗
??? c,從節點復制新主節點時間超過180S轉移失敗
sentinel down-after-milliseconds mymaster 300000//sentinel節點定期向主節點ping命令
哨兵機制啟動
?????? 啟動sentinel服務:
??????????? ./redis-sentinel conf/sentinel_26379.conf &
??????????? ./redis-sentinel conf/sentinel_26380.conf &
??????????? ./redis-sentinel conf/sentinel_26381.conf &
RedisSentinel節點測試
測試:kill -9 6379? 殺掉6379的redis服務
看日志是分配6380 還是6381做為主節點,當6379服務再啟動時,已變成從節點
假設6380升級為主節點:
進入6380>info replication????
???????? role:master
打開sentinel_26379.conf等三個配置,
???? sentinel monitor mymaster 192.168.1.111 6380 2 //有2個sentinel認為master下線
打開redis6379.conf等三個配置, slaveof 127.0.0.1 6380,也變成了6380
注意:生產環境建議讓redis Sentinel部署到不同的物理機上。
坑點:sentinel monitor mymaster 192.168.1.111 6379 2
???? //切記將IP不要寫成127.0.0.1
不然使用JedisSentinelPool取jedis連接的時候會變成取127.0.0.1 6379的錯誤地址
RedisSentinel如何監控2個redis主節點呢?
我們以3個Sentinel節點、2個從節點、1個主節點為例進行安裝部署
原配置加上一句:sentinel monitor mymasterB 192.168.1.112 6379 2
?部署建議
a,sentinel節點應部署在多臺物理機(線上環境)
b,至少三個且奇數個sentinel節點
c,3個sentinel可同時監控一個主節點或多個主節點,當監聽N個主節點較多時,如果sentinel出現異常,會對多個主節點有影響,同時還會造成sentinel節點產生過多的網絡連接,一般線上建議還是, 3個sentinel監聽一個主節點
哨兵的API
?命令:redis-cli -p 26379? //進入哨兵的命令模式,使用redis-cli進入
? 26379> sentinel masters或sentinel master mymaster
? 26379> sentinel slaves mymaster
? 26379> sentinel sentinels mymaster //查sentinel節點集合(不包括當前26379)
? 26379> sentinel failover mymaster //對主節點強制故障轉移,沒和其它節點協商
./redis-cli -p 26380 shutdown //關閉
?客戶端連接(redis-sentinel例子工程)
遠程客戶端連接時,要打開protected-mode no
?????
在使用工程redis-sentinel,調用jedis查詢的流程如下:
???? 1,將三個sentinel的IP和端口 加入JedisSentinelPool
???? 2,根據IP和地址創建JedisSentinelPool池對象
???? 3,在這個對象創建完后,此時該對象已把redis的主節點
第八章?redis集群高可用
RedisCluster是Redis的分布式解決方案,在3.0版本后推出的方案,有效地解決了Redis分布式的需求,當遇到單機內存、并發等瓶頸時,可使用此方案來解決這些問題
8.1?分區規則
常見的分區:
- 規則哈希分區
- 順序分區,
redis集群使用了哈希分區,順序分區暫用不到,不做具體說明;
?RedisCluster采用了哈希分區的“虛擬槽分區”方式(哈希分區分節點取余、一致性哈希分區和虛擬槽分區)。
根據hash值取模,對應到虛擬槽上(范圍)。比如:server1:[1-n1],server2:[n1-n2],server3:[n2-n3]
插槽:集群內部會將所有的key映射到16384個插槽中,集群中的每個數據庫實例負責其中部分的插槽的讀寫。
鍵與插槽的關系:
Redis會將key的有效部分,使用CRC16算法計算出散列值,然后對16384取余數,從
而把key分配到插槽中。鍵名的有效部分規則是:
1:如果鍵名包含{},那么有效部分就是{}中的值
2:否則就是取整個鍵名
移動已分配的插槽:
獲取插槽對應的節點:
9.總結
Redis要達到高性能需要做到:
1.Value盡可能的小。當value太大時,比如json數,幾十kb,或者上mb的數據。大的數據量在帶寬的限制下直接的效果就是QPS驟降,跌到幾百都毫不意外。而且,redis單線程這一點,導致某個大的value訪問會阻塞所有其他的操作。
2.使用Pipeline或Lua Script。Redis一般被用做網絡服務。所有的請求都是跨網絡進行的。所以TCP Round Trip的長短對Redis的性能表現很重要。盡量減少Round Trip可以有效的提高吞吐。所以,通常的優化方法是使用Pipeline,使得客戶端可以一次性把一組Redis命令發給Redis Server;或者預先在Redis Server中定義Lua Script,使用時直接調用。但無論是Pipeline還是Lua Script,都會受到業務需求的制約——不是所有業務都適合用Pipeline/Lua Script的。
3.網路一定要快。redis性能一般情況都會被網絡影響。
4.不開啟RDB或者AOF。開啟任何一種持久化方案都會影響Redis的性能表現。因為開啟持久化會有同步磁盤的操作。
注意:
1.redis適合一些不太在乎數據丟失的場景,如果針對的數據是絕對不能有失的,那么還是要使用ACID的數據庫。
2.隊列在redis5.0版本之前最好不要使用,因為還不夠完善,即使是5.0版本的Stream Date Type。實現了類似于Kafka的append only數據結構和API。不過很可能要到5.2才能在生產中使用(2019年年底),而且目前redis的隊列還沒有經過市場的檢驗。
3.在?事務這一塊,redis沒有ACID事務,他的原子性只針對于服務端,對于使用redis的客戶端則需要其他的處理來保證線程安全,redis持久化沒有提供相互節點同步數據的方式。
所以redis適合的場景:
- 共享Cache ,不怕丟數據,丟了可以從DB中reload;
- 共享Session ,不怕丟數據,丟了可以重新登錄;
- batch job的中間結果。不怕丟數據,丟了重新跑job就可以了;
- 一些簡單數據的存儲,低頻改動,但是會被頻繁讀取。比如首頁推薦的產品列表。但此時必須增加HA的防護,sentinel、cluster或者自定義的機制都可以;
- 一些更加復雜存儲的building block,比如分布式鎖,此時需要多節點來實現一個簡單的quorum
10. 布隆過濾器
? 描述:不怎么精確的set,特點是高效地插入和查詢,可以用來告訴你 “某樣東西一定不存在或者可能存在”。相比于傳統的 List、Set、Map 等數據結構,它更高效、占用空間更少,但是缺點是其返回的結果是概率性的,而不是確切的,可能誤判。
? 用處:進行精度控制。比如緩存穿透,使用布隆過濾器。
? 原理:?本質是一個bit數組,通過多次hash產生復數個下標,然后把bit數組中的對應下標的值從0變為1。
提高精度方法:1.擴容bit數組的長度,2.增加hash函數,這樣可以有更多的下標。
redisBloom: redis的module插件。
11. 策略?
11.1. 過期策略:?
將一個過期的鍵刪除的三種策略:
- 1.定時刪除(默認會每秒進行十次過期掃描(100ms一次))
????????為每個鍵設置一個定時器,一旦過期時間到了,則將鍵刪除。這種策略對內存很友好,但是對 CPU 不友好,因為每個定時器都會占用一定的 CPU 資源。設置了過期時間的key會放在過期字典中。通過貪心策略來獲取過期字典中的key。
? ? ? ? ?1.從過期字典中隨機 n?個 key;
? ? ? ? ?2.刪除這 n個 key 中已經過期的 key;
? ? ? ? 3.如果過期的 key 比率超過 1/4,那就重復步驟 1;
- ?2.惰性刪除(獲取key時檢查,如果過期就刪除)
??????????不管鍵有沒有過期都不主動刪除,等到每次去獲取鍵時再判斷是否過期,如果過期就刪除該鍵,否則返回鍵對應的值。這種策略對內存不夠友好,可能會浪費很多內存。
- 3.定期掃描:
????????系統每隔一段時間就定期掃描一次,發現過期的鍵就進行刪除。這種策略相對來說是上面兩種策略的折中方案。需要注意的是這個定期的頻率要結合實際情況掌控好,使用這種方案有一個缺陷就是可能會出現已經過期的鍵也被返回。
在 Redis 當中,其選擇的是策略 2 和策略 3 的綜合使用。不過 Redis 的定期掃描只會掃描設置了過期時間的鍵,因為設置了過期時間的鍵 Redis 會單獨存在,所以不會出現掃描所有鍵的情況:
typedef struct redisDb {dict *dict; //所有的鍵值對dict *expires; //設置了過期時間的鍵值對dict *blocking_keys; //被阻塞的 key,如客戶端執行 BLPOP 等阻塞指令時dict *watched_keys; //WATCHED keysint id; //Database ID//... 省略了其他屬性} redisDb;11.2. 內存回收
????????使用redis服務時,很多情況下某些key對只會在特定的時間內有效,為了防止這種類型的數據局一直占用內存,可以給key設置有效期。
redis中可以通過4個獨立的命令來給一個key設置過期時間:
- expire key ttl:將 key 值的過期時間設置為 ttl 秒。
- pexpire key ttl:將 key 值的過期時間設置為 ttl 毫秒。
- expireat key timestamp:將 key 值的過期時間設置為指定的 timestamp 秒數。
- pexpireat key timestamp:將 key 值的過期時間設置為指定的 timestamp 毫秒數。 PS:不管使用哪一個命令,最終 Redis 底層都是使用 pexpireat 命令來實現的。
另外,set 等命令也可以設置 key 的同時加上過期時間,這樣可以保證設值和設過期時間的原子性。
設置了有效期后,可以通過 ttl 和 pttl 兩個命令來查詢剩余過期時間(如果未設置過期時間則下面兩個命令返回 -1,
如果設置了一個非法的過期時間,則都返回 -2):
- ttl key 返回 key 剩余過期秒數。
- pttl key 返回 key 剩余過期的毫秒數。
當 Redis 當中所有的鍵都沒有過期,而且此時內存滿了,那么客戶端繼續執行 set 等命令時,?Redis 當中提供了不同的淘汰策略來處理這種場景。
首先 Redis 提供了一個參數 maxmemory 來配置 Redis 最大使用內存:
maxmemory <bytes>或者也可以通過命令 config set maxmemory 1GB 來動態修改。
如果沒有設置該參數,那么在 32 位的操作系統中 Redis 最多使用 3GB 內存,而在 64 位的操作系統中則不作限制。
Redis 中提供了 8 種淘汰策略,可以通過參數 maxmemory-policy 進行配置:
8種內存淘汰策略:
- noeviction(默認):當內存使用超過配置的時候會返回錯誤,不會驅逐任何鍵,讀操作命令可以正常執行;不做任何處理,直接報錯。
- volatile-lru:對設置了過期時間的記錄,驅逐最久沒有使用的鍵;
- volatile-ttl:對設置了過期時間的記錄,驅逐馬上就要過期的鍵 ;
- volatile-lfu:對設置了過期時間的記錄,驅逐使用頻率最少的鍵;
- volatile-random:對設置了過期時間的記錄,隨機刪除設置了過期時間的key;
- allkeys-lfu:對于所有記錄,驅逐使用頻率最少的鍵;
- allkeys-lru:對于所有記錄,所有key使用LRU算法淘汰,驅逐最久沒有使用的鍵;
- allkeys-random:對于所有記錄,所有key隨機刪除;
根據以上算法刪除設置了過期時間的key,直到騰出可用空間。如果沒有可刪除的key,且內存還是不夠用時,則報錯。
PS:淘汰策略也可以直接使用命令 config set maxmemory-policy <策略> 來
11.3. LRU(最近最少使用)
最長時間未被使用。使用雙向鏈表。
LinkedHashMap模擬LRU算法。
removeEldestEntry(Map.Entry<K,V> eldest)方法,可以刪除最久未訪問的結點。
當accessOrder為false時(默認情況),linkedHashMap只會按插入順序維護雙向鏈表。而開啟了accessOrder之后,linkedHashMap就會把每一次對結點的訪問也作為標準來進行排序。也就是說,在每次插入結點/訪問結點的時候,都會將相應結點移動到雙向鏈表的尾部,從而達到按訪問順序進行排序的目的。
LRU 全稱為:Least Recently Used。即:最近最長時間未被使用。這個主要針對的是使用時間。
Redis 改進后的 LRU 算法 在 Redis 當中,并沒有采用傳統的 LRU 算法,因為傳統的 LRU 算法存在 2 個問題:
- 需要額外的空間進行存儲。
- 可能存在某些 key 值使用很頻繁,但是最近沒被使用,從而被 LRU 算法刪除。
為了避免以上 2 個問題,Redis 當中對傳統的 LRU 算法進行了改造,通過抽樣的方式進行刪除。
配置文件中提供了一個屬性 maxmemory_samples 5,默認值就是 5,表示隨機抽取 5 個 key 值,然后對這 5 個 key 值按照 LRU 算法進行刪除,
所以很明顯,key 值越大,刪除的準確度越高。
對抽樣 LRU 算法和傳統的 LRU 算法,Redis 官網當中有一個對比圖:
- 淺灰色帶是被刪除的對象。
- 灰色帶是未被刪除的對象。
- 綠色是添加的對象。
左上角第一幅圖代表的是傳統 LRU 算法,可以看到,當抽樣數達到 10 個(右上角),已經和傳統的 LRU 算法非常接近了。
Redis 如何管理熱度數據
前面我們講述字符串對象時,提到了 redisObject 對象中存在一個 lru 屬性:
typedef struct redisObject {unsigned type:4;//對象類型(4 位=0.5 字節)unsigned encoding:4;//編碼(4 位=0.5 字節)unsigned lru:LRU_BITS;//記錄對象最后一次被應用程序訪問的時間(24 位=3 字節)int refcount;//引用計數。等于 0 時表示可以被垃圾回收(32 位=4 字節)void *ptr;//指向底層實際的數據存儲結構,如:SDS 等(8 字節) } robj;????????lru 屬性是創建對象的時候寫入,對象被訪問到時也會進行更新。正常人的思路就是最后決定要不要刪除某一個鍵肯定是用當前時間戳減去 lru,差值最大的就優先被刪除。但是 Redis 里面并不是這么做的,Redis 中維護了一個全局屬性 lru_clock,這個屬性是通過一個全局函數 serverCron
每隔 100 毫秒執行一次來更新的,記錄的是當前 unix 時間戳。
最后決定刪除的數據是通過 lru_clock 減去對象的 lru 屬性而得出的。那么為什么 Redis 要這么做呢?直接取全局時間不是更準確嗎?
這是因為這么做可以避免每次更新對象的 lru 屬性的時候可以直接取全局屬性,而不需要去調用系統函數來獲取系統時間,
從而提升效率(Redis 當中有很多這種細節考慮來提升性能,可以說是對性能盡可能的優化到極致)。
不過這里還有一個問題,我們看到,redisObject 對象中的 lru 屬性只有 24 位,24 位只能存儲 194 天的時間戳大小,一旦超過 194 天之后就會重新從 0 開始計算,所以這時候就可能會出現,redisObject 對象中的 lru 屬性大于全局的 lru_clock 屬性的情況。
正因為如此,所以計算的時候也需要分為 2 種情況:
- 當全局 lruclock > lru,則使用 lruclock - lru 得到空閑時間。
- 當全局 lruclock < lru,則使用 lruclock_max(即 194 天) - lru + lruclock 得到空閑時間。
需要注意的是,這種計算方式并不能保證抽樣的數據中一定能刪除空閑時間最長的。這是因為首先超過 194 天還不被使用的情況很少,
再次有 lruclock 第 2 輪繼續超過 lru 屬性時,計算才會出問題。
比如對象 A 記錄的 lru 是 1 天,而 lruclock 第二輪都到 10 天了,這時候就會導致計算結果只有 10-1=9 天,實際上應該是 194+10-1=203 天。
但是這種情況可以說又是更少發生,所以說這種處理方式是可能存在刪除不準確的情況,但是本身這種算法就是一種近似的算法,所以并不會有太大影響。
11.4.?LFU(最近最不常用)
一定時間內,訪問次數最少的。
?LFU 全稱為:Least Frequently Used。即:最近最少頻率使用,這個主要針對的是使用頻率。這個屬性也是記錄在 redisObject 中的 lru 屬性內。
????????當我們采用 LFU 回收策略時,lru 屬性的高 16 位用來記錄訪問時間(last decrement time:ldt,單位為分鐘),低 8 位用來記錄訪問頻率(logistic counter:logc),簡稱 counter。
????????訪問頻次遞增 LFU 計數器每個鍵只有 8 位,它能表示的最大值是 255,所以 Redis 使用的是一種基于概率的對數器來實現 counter 的遞增。
?給定一個舊的訪問頻次,當一個鍵被訪問時,counter 按以下方式遞增:
- 提取 0 和 1 之間的隨機數 R。
- counter - 初始值(默認為 5),得到一個基礎差值,如果這個差值小于 0,則直接取 0,為了方便計算,把這個差值記為 baseval。
- 概率 P 計算公式為:1/(baseval * lfu_log_factor + 1)。
- 如果 R < P 時,頻次進行遞增(counter++)。
公式中的 lfu_log_factor 稱之為對數因子,默認是 10 ,可以通過參數來進行控制:
lfu_log_factor 10 下圖就是對數因子 lfu_log_factor 和頻次 counter 增長的關系圖:
????????可以看到,當對數因子 lfu_log_factor 為 100 時,大概是 10M(1000 萬) 次訪問才會將訪問 counter 增長到 255,而默認的 10 也能支持到 1M(100 萬)次訪問 counter 才能達到 255 上限,這在大部分場景都是足夠滿足需求的。
訪問頻次遞減
如果訪問頻次 counter 只是一直在遞增,那么遲早會全部都到 255,也就是說 counter 一直遞增不能完全反應一個 key 的熱度的,
所以當某一個 key 一段時間不被訪問之后,counter 也需要對應減少。
counter 的減少速度由參數 lfu-decay-time 進行控制,默認是 1,單位是分鐘。默認值 1 表示:N 分鐘內沒有訪問,counter 就要減 N。
lfu-decay-time 1 具體算法如下:
看起來這么復雜,其實計算公式就是一句話:取出當前的時間戳和對象中的 lru 屬性進行對比,計算出當前多久沒有被訪問到,比如計算得到的結果是 100 分鐘沒有被訪問,然后再去除配置參數 lfu_decay_time,如果這個配置默認為 1 也即是 100/1=100,代表 100 分鐘沒訪問,所以 counter 就減少 100。
12.多路復用
cpu 處理相比于 IO 可以忽略
每個客戶端建立連接時,都需要服務端為其 創建 socket 套接字,建立連接
然后該客戶端的每個請求都要經歷以下幾步:
(1)等待請求數據數據從客戶端發送過來
(2)將請求數據從內核復制到用戶進程的緩沖區(buffer)
(3)對請求數據進行處理(對于 redis 而言,一般就是簡單的 get/set)
由于操作簡單+只涉及內存,所以第(3)步的處理很簡單、很快,主要時間耗在(1)步
? ? ?
總結
以上是生活随笔為你收集整理的Redis学习笔记·的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑上个别按键失灵可能原因
- 下一篇: 更改MySQL密码并验证,及使用SQLy