Redis基礎知識大全

 

1.Redis

1.1 初識Redis

1.1.1 Redis特性

1.1.2 Redis使用場景

1.1.3 Redis常用數據類型

1.1.4 單線程

1.2 常用API

1.2.1 通用命令

1.3 數據結構&內部編碼

1.4 持久化

1.4.1 RDB

1.4.2 AOF

1.4.3 RDB VS AOF

1.4.4 持久化存在的問題

2.Redis安裝

2.1 linux安裝

2.2 常用操作

2.3 配置

2.3.1 配置文件

2.3.4 Redis安裝腳本

3. Redis

3.1 慢查詢

3.2 pipeline

3.2.1 pipeline VS m操作

3.3 Hyperloglog

3.1 Redis事務

3.1.1 常用命令:

3.1.2 特性:

3.4 發佈訂閱

3.4.1 訂閱消息

3.4.2 發佈消息

3.3 主從複製

3.3.1 主從配置

3.3.2 情景分析

3.3.3 薪火相傳

3.3.4 反客爲主

3.3.5 全量複製

3.3.6 部分複製

3.3.7 複製運維

3.3.8 優缺點

3.4 哨兵模式

3.4.1 哨兵原理

3.4.2 客戶端

3.4.3 哨兵內部定時任務

3.4.4 主客觀下線

3.4.5 領導者選舉

3.4.6 故障轉移

1.面試題:

1.1 爲什麼要用Redis:

1.2 Redis存在的問題

1.3 Redis的文件事件處理器

1.4 Redis過期策略

1.4.1 惰性刪除 && 定期刪除

1.4.2 內存淘汰機制

1.4.3 LRU算法

1.5 緩存雪崩

1.5.1 緩存雪崩

1.5.2 解決策略

1.6 緩存穿透

1.7 緩存和數據庫雙寫的一致性問題

1.7.1 雙寫的策略

 

1.Redis

1.1 初識Redis

1.1.1 Redis特性

1.1.1.1 快

1.1.1.2 持久化

AOF + RDB

1.1.1.3 多種數據結構

String、Hash、Set、List、SortedSet、BitMap(位圖)、HyperLogLog(超小內存唯一計數)、Geo(地理信息)

1.1.1.4 多種客戶端語言

Java、python、Ruby

1.1.1.5 功能豐富

發佈訂閱、lua腳本、事務、pipeline

1.1.1.6 主從複製

1.1.1.7 高可用、分佈式(集羣)

1.1.2 Redis使用場景

緩存系統、計數器、排行榜、社交網絡、簡易消息隊列、實時系統(布隆過濾器)

 

1.1.3 Redis常用數據類型

  • String:最基本的類型,最大512M,二進制安全(jpg圖片、序列化的對象等)[單key單value]

Redis 的字符串是動態字符串,是可以修改的字符串,內部結構實現上類似於 Java 的ArrayList,採用預分配冗餘空間的方式來減少內存的頻繁分配,內存爲當前字符串實際分配的空間一般要高於實際字符串長度。當字符串長度小於 1M 時,擴容都是加倍現有的空間,如果超過 1M,擴容時一次只會多擴 1M 的空間。需要注意的是字符串最大長度爲 512M。

使用場景:記錄用戶頁面訪問量、緩存基本數據、分佈式Id生成器

------------鍵值對------------- > set key value //設置鍵值對,重複設置會更新key > get key // > exists key (integer) 1 > del key (integer) 1 > strlen key //獲取value的長度 > append key valu //在原先的value後面追加值 ------------批量鍵值對------------- (可以批量對多個字符串進行讀寫,節省網絡耗時開銷。) > mset k1 v1 k2 v2 k3 v3 > mget k1 k2 k3 > msenx k1 v1 k2 v2 //當且僅當key都不存在時,設置成功 ------------過期和 set 命令擴展------------- > set key value > expire key 5 # 5s 後過期 > setex key 5 value # 5s 後過期,等價於 set+expire > setnx key value# 如果 name 不存在就執行 set 創建 (integer) 1 > setnx key value (integer) 0 # 因爲 name 已經存在,所以 set 創建不成功 ---------------------計數------------------- > set age 30 > incr age //自增值 decr key // 自減 (integer) 31 > incrby age 5 //decrby key 值 //-值 (integer) 36 -------------------獲取----------------------- getrange key startIdx endIdx //獲取內容,包括起始位置和終止位置 setrang key startIdx value //從指定位置開始寫

還有一個需要特別注意的地方是如果一個key已經設置了過期時間,然後你調用了set 方法修改了它,它的過期時間會消失。

  • Hash :

key:fileld : value ;MapMap結構

Redis 的Hash相當於 Java 語言裏面的 HashMap,它是無序字典。內部通過數組 + 鏈表實現。第一維 hash 的數組位置碰撞時,就會將碰撞的元素使用鏈表串接起來。不同的是,Redis 的字典的值只能是字符串,另外它們擴容 方式不一樣,因爲Java 的HashMap 在字典很大時,擴容是個耗時的操作,需要一次性全部 擴容。Redis爲了高性能,不能堵塞服務,所以採用了漸進式擴容策略。漸進式 擴容會在 擴容的同時,保留新舊兩個hash 結構,查詢時會同時查詢兩個hash 結構,然後在後續的定時任務中以及 hash 的子指令中,循序漸進地將舊 hash 的內容一點點遷移到新的 hash 結構中。當 hash 移除了最後一個元素之後,該數據結構自動被刪除,內存被回收。

hset sunpeipei age 22 //設置修改年齡 hget sunpei age //獲取年齡 hmset sunpei name "sunpei" age 23 phone "153..."//批量設置 hmget sunpei name age hgetall sunpei //獲取全部key和v hlen sunpei //獲取value有多少鍵值對 hexists sunpei name //判斷key存在否 hkeys hash名 //獲取所有的key hvals 哈希名 //獲取所有的value hincrby hash名 k 數 //將指定的值+數 【整數】 hsetnx sunpei age 22 //當且僅當元素不存在時設置成功

 

  • List : 列表,按照插入順序排序[先進後出][單值多value]

Redis 的列表相當於 Java 語言裏面的 LinkedList,注意它是鏈表而不是數組。這意味着list 的插入和刪除操作非常快,時間複雜度爲 O(1),但是索引定位很慢,時間複雜度爲O(n)。當列表彈出了最後一個元素之後,該數據結構自動被刪除,內存被回收。

Redis 的列表結構常用來做異步隊列使用。將需要延後處理的任務結構體序列化成字符

串塞進 Redis 的列表,另一個線程從這個列表中輪詢數據進行處理。

Redis 底層存儲的不是一個簡單的 linkedlist,而是稱之爲快速鏈表 quicklist 的一個結構。

首先在列表元素較少的情況下會使用一塊連續的內存存儲,這個結構是 ziplist,也即是壓縮列表。它將所有的元素緊挨着一起存儲,分配的是一塊連續的內存。當數據量比較多的時候纔會改成 quicklist。因爲普通的鏈表需要的附加指針空間太大,會比較浪費空間,而且會加重內存的碎片化。所以 Redis 將鏈表和 ziplist 結合起來組成了 quicklist。也就是將多個ziplist 使用雙向指針串起來使用。這樣既滿足了快速的插入刪除性能,又不會出現太大的空間冗餘。

Tips:

LPUSH+LPOP =棧

LPUSH+RPOP =隊列

LPUSH+ LTRIM = 固定數量的列表

LPUSH +BRPOP =消息隊列

--------------右邊進左邊出:隊列------------- > rpush books python java golang // lpush 列表名 值 (integer) 3 > lpop books //正序彈出一個元素 "python" > lpop books "java" > lpop books "golang" -----------右邊進右邊出:棧--------------- > rpush books python java golang (integer) 3 > rpop books //倒序彈出一個元素 "golang" > rpop books "java" > rpop books "python" -----------慢操作(慎用)--------------- > lindex 列表名 索引 //獲取指定索引的元素 > lrange 列表名 startIdx endIdx //獲取指定數據【包括起始索引和結束索引】 > ltrim 列表名 starIdx endIdx //截取指定範圍的值再賦值給key 【包括起止索引】 > lset 列表名 index value // > linsert 列表名 before/after v1 v2 //在v1前/後面插入v2 -----------其他--------------- > llen 列表名 //獲取列表的長度 > lrem 列表名 個數 值 //刪除n個值 > rpoplpush 源列表 目的列表 //將原列表表的最後一個元素添加到目的列表的第一位

 

  • Set :無序集合,不予許重複

sadd friends lilei //成功返回 1 sadd friends lilei //重複返回 0 不可重複設置 smembers set名 //查看元素,返回結果無序 sismember set名 value //查看該集合中是否包含該元素 1-包含 0-不包含 scard set名 //獲取集合元素個數 srem set名 值 //移除某元素 srandmember set名 個數 //隨機獲取指定個數值 spop set名 //隨機出棧 smove set1名 set2名 v //將集合1中的某值移動到集合2中 sdiff set1 set2 //在集合1中且不再集合2中的元素 sinter set1 set2 //集合1和集合2的交集部分 sunion set1 set2 //集合1和集合2的並集 //用戶關注人 、粉絲 可以求交集、差集、並集操作【共同操作、共同愛好】

Tips:

SADD :標籤

SPOP + SRANDMEMBER : 隨機數

SADD +SINTER :社交

  • Sorted Set : 有序結合,不允許重複 【在set的基礎上加了一個分數】

zset 可能是 Redis 提供的最爲特色的數據結構,它類似於 Java 的 SortedSet 和 HashMap 的結合體,一方面它是一個 set,保證了內部value 的唯一性,另一方面它可以給每個 value 賦予一個 score,代表這個 value 的排序權重。它的內部實現用的是一種叫着「跳躍列表」的數據結構。

zset 要支持隨機的插入和刪除,所以它不好使用數組來表示。我們先看一個普通的

鏈表結構。

我們需要這個鏈表按照 score 值進行排序。這意味着當有新元素需要插入時,要定位到

特定位置的插入點,這樣纔可以繼續保證鏈表是有序的。通常我們會通過二分查找來找到插入點,但是二分查找的對象必須是數組,只有數組纔可以支持快速位置定位,鏈表做不到。

「跳躍列表」之所以「跳躍」,是因爲內部的元素可能「身兼數職」,比如上圖中間的

這個元素,同時處於 L0、L1 和 L2 層,可以快速在不同層次之間進行「跳躍」。

定位插入點時,先在頂層進行定位,然後下潛到下一級定位,一直下潛到最底層找到合

適的位置,將新元素插進去。你也許會問,那新插入的元素如何纔有機會「身兼數職」呢?跳躍列表採取一個隨機策略來決定新元素可以兼職到第幾層。

首先 L0 層肯定是 100% 了,L1 層只有 50% 的概率,L2 層只有 25% 的概率,L3

層只有 12.5% 的概率,一直隨機到最頂層 L31 層。絕大多數元素都過不了幾層,只有極少數元素可以深入到頂層。

zadd 集合名 分數1 值1 分數2 值2 //添加數據 zrange key startIdx endIdx 【withscores】 //遍歷集合 【帶分數】 zrangebyscore 集合名 【(】開始socre 【(】 結束score 【limit 開始idx 個數】//獲取指定範圍的元素 zrem 集合名 元素 // 刪除指定元素 zcard 集合名 //統計集合中有多少元素 zcount 集合名 開始score 結束score //統計得分在指定範圍的元素有多少個,包括邊界 zrank 集合名 k1 //獲取元素的下標 zcore 集合名 元素 //獲取元素的分數 zrevrank 集合名 元素 //逆序獲取元素的下標 zrevrange 集合名 開始idx 結束idx //逆序獲取元素 zrevrangebyscore 集合名 結束分數 開始分數 //獲取指定分數範圍的集合

  • HyperLogLog: 用於計數

 

  • Geo:存儲地理位置信息

 

1.1.4 單線程

單線程的理解:

Redis一次只會執行一個命令,因此要避免長命令(keys、flush、flushdb、slow lua script、multi/exec、operate、big value)

單線程爲什麼這麼快?

  • 基於內存
  • 非阻塞IO(IO多路複用)
  • 避免線程切換

 

1.2 常用API

1.2.1 通用命令

keys //keys * keys h[h-l]* keys jav? config get * //獲取配置

 

命令

時間複雜度

keys

O(n)

dbsize

O(1)

exists、hexists、hlen

O(1)

del 、hdel

O(1)

expire、ttl、persist

O(1)

type

O(1)

get、set、del、hget、hset、hdel

O(1)

incr、decr、incrby、decrby、incrbyflocat、hincrby、hincrbyflocat

O(1)

setnx(add)、set xx(update)、hsetnx

O(1)

mget、mset、hmget、hmset、

O(n)[1次網絡時間+n次命令時間]

getset(設置新值、返回舊值)、append、strlen(中文)

O(1)

getrange、setrange

O(1)

hgetall、hvals、hkeys

O(n)

rpush、lpush

O(1-n)

linsert

O(n)

lpop、rpop、

O(1)

lrem、ltrim

O(n)

lindex、lrange(包含end)、

O(n)

llen

O(1)

lset、

O(n)

blpop、brpop

O(1)

sadd、srem

O(1)

scard、sismember、srandmember、spop

O(1)

smembers

O(n)

sdiff、sinter、sunion、

集合間操作

zadd

O(lgn)

zrem、zscore、zincrby、zcard

O(1)

zrange、zrangebyscore、zcount、zremrangebyrank、zremrangebyscore

O(log(n)+m)

zinterstore、zunionstore

 

 

1.3 數據結構&內部編碼

 

這樣設計的目的: 空間換時間? 時間換空間

Redis內置對象結構:

 

1.4 持久化

1.4.1 RDB

觸發機制:save、bgsave、自動觸發

文件策略:新文件覆蓋舊文件

1.4.1.1 save命令

1.4.1.2 bgsave命令

1.4.1.3 save VS bgsave

1.4.1.4 自動生成

自動觸發:達到配置、shutdown、全量複製

20217uploading.4e448015.gif轉存失敗重新上傳取消

 

推薦配置:關閉自動save、開啓文件壓縮、開啓文件校驗

1.4.1.5 總結

  • save會阻塞Redis,bgsave不會長時間阻塞(fork時阻塞)
  • bgsave會fork出子進程

1.4.2 AOF

1.4.2.1 策略

 

 

推薦使用第二種:最多丟失1s數據

1.4.2.2 AOF重寫

重寫的作用:減少硬盤佔用量、加快恢復速度

1.4.2.3 AOF重寫實現方式

注意:重寫是基於內存重寫,不是基於舊的AOF文件;

 

 

 

min-size:第一次重寫最小內存

percentage:下一次重寫的條件

重寫條件:(同時滿足)

1.當期內存 > 最小內存

2.(當期尺寸-上次尺寸)/上次尺寸 > 增長率

no-appendfsync-on-rewrite :yes 在往緩存區寫重寫內容時,是否停止寫aof;性能 VS 數據(如果重寫失敗,會導致aof丟失部分數據),推薦使用yes,提高性能;

 

1.4.3 RDB VS AOF

 

RDB建議:主節點關閉RDB、從節點打開RDB,便於備份數據;

 

1.4.4 持久化存在的問題

1.4.4.1 fork

latest_fork_usec :上次fork花費的時間

1.4.4.2 子進程的開銷

Linux在2.6版本後,優化了內存頁(內存頁更大),然而這對Redis來說並不是一個好消息;echo:nerver > /sys/kernel/mm/transparent_hugepage/enable

1.4.4.3 AOF追加阻塞

 

 

2.Redis安裝

2.1 linux安裝

下載壓縮包:http://download.redis.io/releases/

// 下載 wget http://download.redis.io/releases/+版本 //解壓 tar -zxvf 文件名 // 安裝gcc[c編譯器]: yum install gcc-c++ // 安裝Redis 【redis目錄下】 make // 出錯用 :  make MALLOC=libc  make install // 開啓守護線程 daemonize yes // 運行Redis redis-server 配置文件 // 查詢線程信息 ps -ef|grep "redis" // 連接客戶端: redis-cli -h host-p 端口號

 

2.2 常用操作

性能測試:

redis-benchmark

 

切換庫:

select 編號 【從0開始,默認16個庫】

 

查看當前數據庫key的數量:

DBSIZE

 

查看當前庫所有的key

keys * // keys k? 精確查找

 

清空當前庫

flushdb

 

清空全庫

flushall

 

判斷某個key是否存在

exists key //0-不存在 1-存在

 

剪切key到另一個庫

move key db //當前庫就不存在了

 

給key設置過期時間

expire key 10 //過期時間

 

查看key還有多久過期

ttl key //-1 永久有效,-2 已過期

 

查看key的類型

type key

 

2.3 配置

2.3.1 配置文件

redis.conf 配置項說明如下: 1.daemonize no Redis默認不是以守護進程的方式運行,可以通過該配置項修改, 使用yes啓用守護進程 2. pidfile /var/run/redis.pid 當Redis以守護進程方式運行時,Redis默認會把pid 寫入/var/run/redis.pid文件,可以通過pidfile指定   3. port 6379 指定Redis監聽端口,默認端口爲6379,   4. bind 127.0.0.1 綁定的主機地址   5. timeout 300 當客戶端閒置多長時間後關閉連接,如果指定爲0,表示關閉該功能 6. loglevel verbose 指定日誌記錄級別,Redis總共支持四個級別:debug、verbose、notice、 warning,默認爲verbose   7. logfile stdout 日誌記錄方式,默認爲標準輸出,如果配置Redis爲守護進程方式運行, 而這裏又配置爲日誌記錄方式爲標準輸出,則日誌將會發送給/dev/null   8. databases 16 Redis支持多個數據庫,並且每個數據庫的數據是隔離的不能共享, 並且基於單機纔有,如果是集羣就沒有數據庫的概念 。 設置數據庫的數量,默認數據庫爲0,可以使用SELECT <dbid>命令 在連接上指定數據庫id   9. save <seconds> <changes> 指定在多長時間內,有多少次更新操作,就將數據同步到數據文件, 可以多個條件配合 save "" //關閉RDB   Redis默認配置文件中提供了三個條件:   save 900 1   save 300 10   save 60 10000   分別表示900秒(15分鐘)內有1個更改,300秒(5分鐘)內有10個更改以及 60秒內有10000個更改。   10. rdbcompression yes 指定存儲至本地數據庫時是否壓縮數據,默認爲yes, Redis採用LZF壓縮,如果爲了節省CPU時間,可以關閉該選項, 但會導致數據庫文件變的巨大   11. dbfilename dump.rdb 指定本地數據庫文件名,默認值爲dump.rdb   12. dir ./ 指定本地數據庫存放目錄   13. slaveof <masterip> <masterport> 設置當本機爲slave服務時,設置master服務的IP地址及端口, 在Redis啓動時,它會自動從master進行數據同步   14. masterauth <master-password> 當master服務設置了密碼保護時,slav服務連接master的密碼   15. requirepass foobared 設置Redis連接密碼,如果配置了連接密碼, 客戶端在連接Redis時需要通過AUTH <password>命令提供密碼,默認關閉   16. maxclients 12 設置同一時間最大客戶端連接數,默認無限制, Redis可以同時打開的客戶端連接數爲Redis進程可以打開的最大文件描述符 數,如果設置 maxclients 0,表示不作限制。當客戶端連接數到達限制時, Redis會關閉新的連接並向客戶端返回max number of clients reached 錯誤信息 17. maxmemory <bytes> 指定Redis最大內存限制,Redis在啓動時會把數據加載到內存中, 達到最大內存後,Redis會先嚐試清除已到期或即將到期的Key, 當此方法處理 後,仍然到達最大內存設置,將無法再進行寫入操作, 但仍然可以進行讀取操作。Redis新的vm機制,會把Key存放內存, Value會存放在swap區   18. appendonly no 指定是否在每次更新操作後進行日誌記錄,Redis在默認情況下是異步的 把數據寫入磁盤,如果不開啓,可能會在斷電時導致一段時間內的數據丟失。 因爲 redis本身同步數據文件是按上面save條件來同步的,所以有的數據會 在一段時間內只存在於內存中。默認爲no   19. appendfilename appendonly.aof 指定更新日誌文件名,默認爲appendonly.aof    20. appendfsync everysec 指定更新日誌條件,共有3個可選值:  no:表示等操作系統進行數據緩存同步到磁盤(快)    always:表示每次更新操作後手動調用fsync()將數據寫到磁盤(慢,安全)    everysec:表示每秒同步一次(折衷,默認值)   21. vm-enabled no 指定是否啓用虛擬內存機制,默認值爲no,簡單的介紹一下, VM機制將數據分頁存放,由Redis將訪問量較少的頁即冷數據 swap到磁盤上,訪問多的頁面由磁盤自動換出到內存中    22. 虛擬內存文件路徑,默認值爲/tmp/redis.swap,不可多個Redis實例共享    vm-swap-file /tmp/redis.swap 23. 將所有大於vm-max-memory的數據存入虛擬內存,無論vm-max-memory設置多小,所有索引數據都是內存存儲的(Redis的索引數據 就是keys),也就是說,當vm-max-memory設置爲0的時候,其實是所有value都存在於磁盤。默認值爲0    vm-max-memory 0 24. Redis swap文件分成了很多的page,一個對象可以保存在多個page上面,但一個page上不能被多個對象共享,vm-page-size是要根據存儲的 數據大小來設定的,作者建議如果存儲很多小對象,page大小最好設置爲32或者64bytes;如果存儲很大大對象,則可以使用更大的page,如果不 確定,就使用默認值    vm-page-size 32 25. 設置swap文件中的page數量,由於頁表(一種表示頁面空閒或使用的bitmap)是在放在內存中的,,在磁盤上每8個pages將消耗1byte的內存。    vm-pages 134217728 26. 設置訪問swap文件的線程數,最好不要超過機器的核數,如果設置爲0,那麼所有對swap文件的操作都是串行的,可能會造成比較長時間的延遲。默認值爲4    vm-max-threads 4 27. 設置在向客戶端應答時,是否把較小的包合併爲一個包發送,默認爲開啓   glueoutputbuf yes 28. 指定在超過一定的數量或者最大的元素超過某一臨界值時,採用一種特殊的哈希算法   hash-max-zipmap-entries 64   hash-max-zipmap-value 512 29. 指定是否激活重置哈希,默認爲開啓(後面在介紹Redis的哈希算法時具體介紹)   activerehashing yes 30. 指定包含其它的配置文件,可以在同一主機上多個Redis實例之間使用同一份配置文件,而同時各個實例又擁有自己的特定配置文件   include /path/to/local.conf

 

2.3.4 Redis安裝腳本

#!/bin/bash #檢查用戶是否已經存在 ############################## ## 描述:創建用戶,安裝Redis ## 作者:孫培 ## 時間:2019-09-17 ############################## set -o nounset set -o errexit readonly redisDir="/opt/cachecloud/redis" readonly redisTarGz="redis-3.0.7.tar.gz" checkExist(){ #查看passwd裏是否有用戶記錄 local num=`cat /etc/passwd | grep -w ${1}|wc -l` if [ ${num} -ge 1 ] then echo "==>>當前用戶已經存在,是否覆蓋已有數據?:[y/n]" read replace if [ ${replace} == "y" ] then echo "==>>刪除已存在用戶: ${1}" userdel -r "${1}" createUser "${1}" init "${1}" return 0 fi else createUser "${1}" init "${1}" fi return 0 } #創建用戶 createUser() { # 添加用戶 useradd -m -d /home/${1} -s /bin/bash ${1} # 添加密碼 echo "==>> 請輸入用戶密碼" passwd ${1} # 密碼保存的有效天數 chage -M 9999 ${1} echo "==>> 用戶${1}創建成功!" } # 初始化 init() { # 創建目錄 mkdir -p /opt/cachecloud/data mkdir -p /opt/cachecloud/conf mkdir -p /opt/cachecloud/logs mkdir -p /opt/cachecloud/redis mkdir -p /tmp/cachecloud echo "==>>初始化文件目錄" # 爲用戶授權 chown -R ${1}:${1} /opt/cachecloud chown -R ${1}:${1} /tmp/cachecloud echo "==>>爲${1}用戶授權" } # 安裝redis installRedis() { # 提示語: echo "===>>開始安裝Redis," # 安裝GCC yum install -y gcc # 創建目錄 mkdir -p ${redisDir} && cd ${redisDir} # 下載解壓目錄 wget http://download.redis.io/releases/${redisTarGz} && mv ${redisTarGz} redis.tar.gz && tar zxvf redis.tar.gz --strip-component=1 # 安裝Redis make && make install if [ $? == 0 ] then echo "==>> Redis安裝成功,Redis默認安裝目錄:${redisDir},Redis默認版本:${redisTarGz},如有需要,請修改腳本" #授權 chown -R $1:$1 ${redisDir} #配置環境變量 export PATH=$PATH:${redisDir}/src return 0 fi echo "==>>Redis安裝失敗,原因:Redis已經存在" } username=$1 checkExist "${username}" installRedis "${username}"

3. Redis

3.1 慢查詢

Redis會將慢查詢命令保存在內存隊列中,慢查詢數據不會持久化;

slowlog get [n] //獲取慢查詢列表 slowlog len //獲取慢查詢隊列長度 slowlog reset //清理慢查詢隊列 //動態修改 config set slowlog-max-len 1000 //慢查詢隊列長度 config set slowlog-log-slower-than 1000 //單位微妙:即1毫秒

tip:

  • slowlog-log-slower-than 默認10ms,通常設置1ms
  • slowlog-max-len 不要太小,通常設置1000左右
  • 查詢聲明週期:1.發送請求 2.請求排隊 3.執行請求(慢查詢) 4,返回請求
  • 定期持久化慢查詢

3.2 pipeline

1次請求時間 = 1次網絡請求時間(發送請求+接收請求) + 命令排隊時間+執行時間

注意:Redis執行命令的時間是微妙級別、所以Redis的瓶頸是在網絡時間上;北京到上海的一次網 絡請求大概需要花費13毫秒。

pipeline就是爲了節省網絡開銷,一次執行多條命令;

3.2.1 pipeline VS m操作

 

原生的M操作是原子性的,pipeline是非原子性的;

 

3.3 Hyperloglog

pfadd key element //添加元素 pfcount key :計算總數 pfmerge destkey sourcekey [sourcekey..] //合併多個key

Tips:

  • 是否容忍錯誤 (錯誤率:0.81)
  • 是否需要單條數據

 

3.1 Redis事務

3.1.1 常用命令:

①標記開始一個事務:MULTI

②取消事務:DISCARD

③執行所有的事務:EXEC

④監視一個(或多個)key,如果在事務執行前這個key被改動,那麼事務被打斷:WATCH key [key...]

⑤取消WATCH命令多所有key的監視: UNWATCH

redis 127.0.0.1:6379> MULTI OK redis 127.0.0.1:6379> set k1 v1 QUEUED redis 127.0.0.1:6379> DISCARD OK redis 127.0.0.1:6379> get k1 (nil) redis 127.0.0.1:6379> EXEC 1) OK redis 127.0.0.1:6379> get k1 "v1"

 

3.1.2 特性:

①冤頭債主:

部分支持事務,成功的操作不會受錯誤操作的影響;

redis 127.0.0.1:6379> set k1 v1 QUEUED redis 127.0.0.1:6379> getset k2 (error) ERR wrong number of arguments for 'getset' command redis 127.0.0.1:6379> set k3 v3 QUEUED redis 127.0.0.1:6379> exec 1) OK 3) OK redis 127.0.0.1:6379> get k3 "v3"

 

②WATCH鎖

WATCH鎖,底層由樂觀鎖實現;

redis 127.0.0.1:6379> get k1 "800" //設置監視鎖 redis 127.0.0.1:6379> watch k1 OK //開啓事務 redis 127.0.0.1:6379> multi OK // 修改k1爲29 redis 127.0.0.1:6379> set k1 29 QUEUED /----------- 別的線程---------------- redis 127.0.0.1:6379> get k1 "800" redis 127.0.0.1:6379> set k1 200 OK /----------- 別的線程---------------- //執行失敗 redis 127.0.0.1:6379> exec (nil) //修改值失敗 redis 127.0.0.1:6379> get k1 "200"

 

3.4 發佈訂閱

Redis消息隊列特點:1. 無法獲取歷史消息 2.消息是非搶佔式的(都可以收到)

3.4.1 訂閱消息

//訂閱多個消息 redis 127.0.0.1:6379> subscribe cctv-1 cctv-2 //訂閱多個消息 psubscribe cctv*

3.4.2 發佈消息

publish cctv-1 xinweblianbo

 

3.3 主從複製

作用:數據副本(高可用)、擴展讀性能(讀寫分離)

3.3.1 主從配置

3.3.1.1 命令

查看當前節點信息:

info replication

 

作爲從節點:【重啓無效】

slaveof 主機IP 主機端口號 slaveof no one // 恢復爲主節點,舊數據不會清除,跟隨新主後會清空數據

3.3.1.2 配置

slaveof ip port slave-read-only yes //從節點只讀

3.3.2 情景分析

情景一:先寫入數據,後進行主從備份,之前的數據會不會被同步?

主從備份前的數據也會被同步

情景二:從機是否可以寫數據?

從機只支持讀取數據,不支持寫數據

情景三:主機宕機後,從機是否會選舉爲主機?主機恢復後,從機是否可以正常工作?

主機宕機後,從機不會進行選舉;

主機恢復後,從機正常工作;

情景四:從機宕機重啓後是否可以正常工作?

命令式:重啓會從機不會識別主機,需要重新執行命令;

配置型:重啓後從機會識別主機

 

3.3.3 薪火相傳

①:中間的節點還是屬於從節點,不支持寫操作;

②:中間節點宕機,最後的從節點也無法正常工作;中間節點恢復後需要重新執行slaveof命令

 

3.3.4 反客爲主

原主機宕機 -->從機執行 slaveof no one -->選出新的主機【剩餘的從機不會更新主機(忠心不二);誰執行這條命令誰就會變爲主機(謀權篡位)】

原主機恢復---> 新主機不會禪讓(反客爲主) ---> 叢機會跟隨原主機(堅貞不渝)

 

3.3.5 全量複製

全量複製開銷:

1.bgsave時間

2.RDB文件網絡傳輸時間

3.從節點清空數據時間

4.從節點加載RDB文件時間

5.從節點AOF重寫時間(如果從節點開啓了AOF功能)

 

3.3.6 部分複製

3.3.7 複製運維

  1. 讀寫分離:主從數據複製延遲、讀到過期數據(3.2已經解決)
  2. 配置不一樣:例如:maxmemory配置不一致,數據結構優化參數不一致(hash-max-ziplist-entries)
  3. 規避全量複製:第一次全量複製(不可避免)、runId不一致、複製緩衝區不足(默認1M,可增大該值)
  4. 複製風暴:假如Master掛了很多Slave,Master重啓後,會進行大量全量複製、單機器故障,該機器上有大批主節點(主機分散、從機晉升)

3.3.8 優缺點

優點:1.數據備份 2、讀寫分離,減輕主機壓力

缺點:1.手動故障轉移 2.寫能力、存儲能力有限

3.4 哨兵模式

3.4.1 哨兵原理

 

 

 

新建配置文件:sentinel.conf

添加配置:

sentinel monitor mastser-name(區分不同的組) 主機IP 主機端口 投票數(達到該票時就會選舉爲主機) daemonized yesse port 端口號 logfile "${port}.log" ###########以下配置會重寫################## sentinel down-after-milliseconds 組名 30000(30s,超過該時間,就認爲該節點下線) sentinel parallel-syncs 組名 1(新主機成立後,一次可以有幾個從機複製主機) sentinel failover-timeout 組名 180000(故障轉移時間)

啓動哨兵: /Redis/src目錄下

redis-sentinel sentinel.conf【配置文件位置】

 

命令查看:

redis-cli -p 26379 //哨兵是一個特殊的 info //查看哨兵信息

 

3.4.2 客戶端

3.4.2.1 客戶端如何連接Master

step1: 客戶端需要提供哨兵集合和組名,遍歷哨兵集合,找到一個可用的哨兵

step2:通過組名獲取master地址

step3: 驗證節點的信息

step4:如果發生故障轉移,哨兵通過發佈訂閱模式通知客戶端

3.4.3 哨兵內部定時任務

3.4.4 主客觀下線

從節點:因爲不涉及故障轉移,固只需要主觀下線就行;

 

3.4.5 領導者選舉

 

 

3.4.6 故障轉移

 

 

1.面試題:

1.1 爲什麼要用Redis:

高性能:Redis讀取速率高於DB查找

高併發:緩解數據庫壓力

1.2 Redis存在的問題

1.3 Redis的文件事件處理器

1.4 Redis過期策略

1.4.1 惰性刪除 && 定期刪除

定期刪除:每隔100ms,就會隨機抽取一些設置了過期時間的key,j檢查其是否過期,如果過期,就刪除;

惰性刪除:在獲取key時,Redis會檢查這個key是否設置過期時間以及是否過期,如果已經過期則刪除;

其餘:走內存淘汰機制

 

1.4.2 內存淘汰機制

如果Redis內存佔用過多,就會進行內存淘汰,對應的策略:

策略一:內存不足時,拒絕寫入;(noeviction)

策略二:內存不足時,刪除最近最少使用的key[全量key];推薦使用(allkeys-lru)

策略三:內存不足時,隨機刪除;(allkeys-random)

策略四:內存不足時,刪除最近最少使用的key【設置了過期時間的key】;(volatile-lru)

策略五:內存不足時,隨機刪除;【設置了過期時間的key】 (volatile-random)

策略六:內存不足時,優先刪除過期時間早的key; (volatile-ttl)

 

1.4.3 LRU算法

public class MyLRU<K,V> extends LinkedHashMap<K,V>{ /** * 緩存的容量 */ private final int CACHE_SIZE; /** * LinkedHashMap的一個構造函數,當參數accessOrder爲true時,即會按照訪問順序排序,最近訪問的放在最前,最早訪問的放在後面 * public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { * super(initialCapacity, loadFactor); * this.accessOrder = accessOrder; * } * @param cacheSize */ public MyLRU(int cacheSize) { super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true); CACHE_SIZE = cacheSize; } /** * 過期策略: * LinkedHashMap自帶的判斷是否刪除最老的元素方法,默認返回false,即不刪除老數據 * 我們要做的就是重寫這個方法,當滿足一定條件時刪除老數據 * @param eldest * @return */ @Override protected boolean removeEldestEntry(Map.Entry eldest) { return size() > CACHE_SIZE; } }

 

1.5 緩存雪崩

1.5.1 緩存雪崩

緩存崩潰 -->數據庫崩潰 -->系統不可用

 

1.5.2 解決策略

事前:保證Redis的高可用 : Redis集羣

事中:本地ehcache+限流組件

事後:根據Redis持久化文件,恢復Redis集羣

1.6 緩存穿透

穿透:Redis->DB ->穿透

根本原因:數據庫中不存在的數據不會回寫到Redis中,進而每次都請求數據庫

 

解決方案:查詢不到數據時,就寫空值到緩存;

 

1.7 緩存和數據庫雙寫的一致性問題

1.7.1 雙寫的策略

讀數據:讀取數據時,先從Redis獲取,獲取不到時,再查數據庫,並將結果回寫數據庫。

更新數據:更新數據時,先刪除緩存,再更新數據庫。

更新數據時刪除緩存原因:懶加載機制,頻繁更新不等於頻繁查詢,只有查詢的時候才放入緩存;

1.7.2

 

 

3

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章