引言
Redis 是一個開源(BSD許可)的,內存中的數據結構存儲系統,它可以用作數據庫、緩存和消息中間件。 它支持多種類型的數據結構,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 與范圍查詢, bitmaps, hyperloglogs 和 地理空間(geospatial) 索引半徑查詢。 Redis 內置了 復制(replication),LUA腳本(Lua scripting), LRU驅動事件(LRU eviction),事務(transactions) 和不同級別的 磁盤持久化(persistence), 並通過 Redis哨兵(Sentinel)和自動 分區(Cluster)提供高可用性(high availability)。
數據結構
Redis是基於內存的數據儲存服務,它支持key-value查詢操作,value存在以下五種數據類型
- 字符串(strings)
- 散列(hashes)
- 表(lists)
- 集合(sets)
- 有序集合(sorted sets)
redis安裝
下載
地址:http://download.redis.io/releases/
解壓
##解壓
tar -xzvf redis-6.0.6.tar.gz
編譯
進入解壓的目錄,運行make命令
##使用gcc編譯,gcc版本需要大於5.0,查看 gcc版本:gcc -v
make
##若需要指定安裝目錄,則加PREFIX選項
make install PREFIX=/usr/local/redis
##如果gcc小於5,需要先升級gcc,依次運行以下命令
yum install centos-release-scl
yum install devtoolset-7-gcc*
scl enable devtoolset-7 bash
常用命令
String
Redis 字符串數據類型的相關命令用於管理 redis 字符串值,常用操作命令如下:
set key value ##設置key的值為value
mset key1 value1 key2 value2 [key value] ##同里設置多個
get key ##獲取key的value值
mget key1 key2 key3 [key] ##同里獲取多個key的value值
del key ##刪除key,跟value類型無關
incr key ##對num型的key進行++1操作,decr減1
incrby key incrememt ##對num型的key加上increment,decrby減n
append key value ##將value的值進行字符串連接操作,加在原value末尾
strlen key ##獲取value字符串長度
setnx key value ##只有key不存在時設置key的值
exists key1 key2 key3 ##判斷某個key是否存在,跟value類型無關,返回存在key的個數
Hash
hash 是一個 string 類型的 field(字段) 和 value(值) 的映射表,hash 特別適合用於存儲對象
hset key field1 value1 field2 value2 [field value] ##添加key,給key設置字段以及相應的value值,可以同里設置多個field,hmset同樣
hget key field ##獲得key的其中一個field字段的value值
hmget key field ##獲得key的一個或多個field字段的value值
hdel key field1 field2 ##刪除key的一個或多個field字段
hexists key field ##判斷某個key中的field字段是否存在
hkeys key ##獲取key的所有存在的field字段
hvals key ##獲取key的所有value
hlen key ##獲取key存在的字段field個數
hsetnx key field value ##當只有key中的field字段不存在的時候,將field值設為value
hincrby key field increment ##將key中的某個num字段filed,自增increment
List
Redis列表是簡單的字符串列表,按照插入順序排序。你可以添加一個元素到列表的頭部(左邊)或者尾部(右邊)
lpush key element1 element2 element3 ##從左邊往右邊推,依次推e1 e2 e3,(lpushx,如果list存在,才插入)
rpush key element1 element2 element3 ##從右邊往左邊推,依次推e1 e2 e3 (rpushx,如果list存在,才插入)
lpop key ##從左邊往外彈(取),取出最左邊的元素,取了該元素就沒了
lpop key ##從右邊往外彈(取),取出最右邊的元素,取了該元素就沒了
lrange start end ##取出list中某幾個值,start和end可以是正反向索引,(正向索引從最左邊0開始,反向索引從最右邊-1開始),start end都是include
lindex key index ##根據索引取出list中的元素
llen key ##list的長度
LSET key index value ##設置設置list某個位置的元素
##根據參數COUNT的值,移除列表中與參數 VALUE 相等的元素。
###count > 0 : 從表頭開始向表尾搜索,移除與 VALUE 相等的元素,數量為 COUNT 。
###count < 0 : 從表尾開始向表頭搜索,移除與 VALUE 相等的元素,數量為 COUNT 的絕對值。
###count = 0 : 移除表中所有與 VALUE 相等的值。
lrem key count value
##將source列表中最右邊的元素取出來,再放入到destination列表中最左邊
RPOPLPUSH source destination
##將值 value 插入到列表 key 當中,位於值 pivot 之前或之后。
LINSERT key BEFORE|AFTER pivot value
BLPOP key timeout ##彈出list中最左邊的值,如果不存在,則阻塞timout秒
BRLPOP key timeout ##彈出list中最右邊的值,如果不存在,則阻塞timout秒
BRPOPLPUSH source destination timeout ##將source列表中最右邊的元素取出來,再放入到destination列表中最左邊,如果source沒有元素,則阻塞timeout秒。
Set
集合Set 是 String 類型的無序集合。集合成員是唯一的,這就意味着集合中不能出現重復的數據。
Redis中集合是通過哈希表實現的,所以添加,刪除,查找的復雜度都是 O(1)
sadd key member1 member2 member3 ##向集合中添加1個或多個成員,自動去重
srem key memeber1 memeber2 ##移除集中中一個或多個元素
scrad key ##獲取集合的成員數
SMEMBERS key ##獲取集中中的所有成員
SISMEMBER key member ##判斷memeber是否是集合key中的成員
sdiff key1 key2 ##差集(返回集合1中有的,而集合2中沒有的數據)
SDIFFSTORE destination key1 [key2] ##將差集存在destination當中
SINTER key1 [key2] ##交集
SINTERSTORE destination key1 [key2] ##將交集存儲在destination
SUNION key1 key2 ##並集
SUNIONSTORE destination key1 [key2] ##將並集儲存在destination
SMOVE source destination member ##將元素memeber從source集合移動到destination中
spop key ##移除並返回集合中的一個隨機元素
##返回集合中一個或多個隨機元素
###如果count>0 且 count<size,則返回count個不重復的元素,如果count>size,則返回集合的全部元素(不重復)
###如果count<0,則返回count絕對值個元素,無論count絕對值是否大於size,都有可能重復。
SRANDMEMBER key [count]
Sorted Set
有序集合和集合一樣也是 string 類型元素的集合,且不允許重復的成員。
不同的是每個元素都會關聯一個 double 類型的分數。redis 正是通過分數來為集合中的成員進行從小到大的排序。
有序集合的成員是唯一的,但分數(score)卻可以重復
ZADD key score1 member1 [score2 member2] ##向有序集合添加一個或多個成員,或者更新已存在成員的分數
zcard key ##返回有序集合中的成員數量
zrange key start end [withscores] ##返回指定索引區間的memeber,withscores可以把sroce也返回來,,ZREVRANGE從高到低
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT] ##返回分數在指定范圍內的成員
scount key min max ##返回分數在min和max區間范圍內的成員,min和max都是Including
ZINCRBY key increment member ##對有序集合中key的member成員分數加上increment
zrank key member ##返回member的索引(它的索引是score排過序后的索引),ZREVRANK,從高到低
zrem key member [member ...] ##移除有序集合中一個或多個成員
zremrangebyrank key start top ##移除有序集合中指定rank區間的成員
zremrangebyscore key min max ##移除有序集中中指定score區間的成員,ZREVRANGEBYSCORE從高到低
zscore key member ##返回有序集合,成員的分數值
HyperLogLog
HyperLogLog 是用來做基數統計的算法,HyperLogLog 的優點是,在輸入元素的數量或者體積非常非常大時,計算基數所需的空間總是固定 的、並且是很小的。
在 Redis 里面,每個 HyperLogLog 鍵只需要花費 12 KB 內存,就可以計算接近 2^64 個不同元素的基 數。這和計算基數時,元素越多耗費內存就越多的集合形成鮮明對比。
但是,因為 HyperLogLog 只會根據輸入元素來計算基數,而不會儲存輸入元素本身,所以 HyperLogLog 不能像集合那樣,返回輸入的各個元素。
比如數據集 {1, 3, 5, 7, 5, 7, 8}, 那么這個數據集的基數集為 {1, 3, 5 ,7, 8}, 基數(不重復元素)為5。 基數估計就是在誤差可接受的范圍內,快速計算基數。
PFADD key element1 element2 element3 ##添加元素
PFCOUNT key ##計算基數
PFMERGE destination key1 key2 ##將多個 HyperLogLog 合並為一個 HyperLogLog
bitmap
bitmap就是通過最小的單位bit來進行0或者1的設置,表示某個元素對應的值或者狀態。
一個bit的值,或者是0,或者是1;也就是說一個bit能存儲的最多信息是2。
setbit key offset value ##將key這個bitmap上的offset位置設為value,value只能是0或1
getbit key offset ##獲取第offset位置上的value值
##以下的區間是的字節為b為單位,即0 0,則第1個8個二進制位
bitcount key [start end] ##統計bit為1的個數,可以指定區間,如果沒有指定,則獲取全部
BITPOS key bit [start end] ##查找bitmap中,首個值為bit(0或1)的位置(索引),可以指定區間
bitOp ##對二進制位進行位運算 and or
redis事務
multi ##標記一個事務塊開始
exec ##執行事務塊中的所有命令
DISCARD ##取消事務
unwatch ##取消watch命令對所有key的監視
watch key1 key2 ... ##監視一個或多個key,如果在事務執行之前這個(或這些) key 被其他命令所改動,那么事務將被打斷。類似Java中的cas操作
其它操作
auth password ##如果有密碼,登陸后,要輸入密碼
select index ##選擇數據庫 0- 15
dbsize ##當前庫中key的數量
keys parttern ##查看當前庫中parttern匹配成功的所有key
flushdb ##刪除當前庫中的所有數據
flushall ##刪除整個redis的數據
expire key seconds ##設置過期時間,單位秒,pexpire毫秒
EXPIREAT timestamp ##設置過期時間,在指定時間點過期,接受unix timestamp,pexpireat毫秒時間戳
ttl key ##返回key的過期時間,pttl毫秒
PERSIST key ##移除key的過期時間
exists key ##檢查key是否存在
move key dbindex ##將當前數據庫中的Key-value移至指定的db中
rename key newname ##為key修改名字,新名字newname
type key ##返回key所存儲值的類型
save ##立即生成內存快照rdb文件
redis配置
基本配置
bind 0.0.0.0 ##綁定IP
port 6379 ##監聽端口
dir "/home/server/redis-16379/data" ##數據存儲目錄 rdb aof log文件的存放目錄
pidfile /var/run/redis_6379.pid ##pid文件位置
logfile "" ##log文件位置,默認沒有,則輸出到 /dev/null
databases 16 ##數據庫個數,默認16,即0-15
daemonize no ##是否后台運行
supervised no ## no upstart systemd auto
requirepass 123456 ##設置auth認證時的密碼123456,默認沒有密碼
rename-command FLUSHDB FSAEWQFREWQFEWQ23 ##禁用flushdb
rename-command FLUSHALL FSAEWQFREWQFEWQ32s ##禁用flushall
maxmemory 200m ##最大可占用內存,一般設為物理內存的80%左右,默認大小等於物理內存
maxclients 10000 ##最大client連接數量
maxmemory-policy noeviction 內存滿了后,數據淘汰策略
redis持久化
RDB持久化方式能夠在指定的時間間隔能對你的數據進行快照存儲.
AOF持久化方式記錄每次對服務器寫的操作,當服務器重啟的時候會重新執行這些命令來恢復原始的數據,AOF命令以redis協議追加保存每次寫的操作到文件末尾.Redis還能對AOF文件進行后台重寫,使得AOF文件的體積不至於過大.
如果你只希望你的數據在服務器運行的時候存在,你也可以不使用任何持久化方式.
你也可以同時開啟兩種持久化方式, 在這種情況下, 當redis重啟的時候會優先載入AOF文件來恢復原始的數據,因為在通常情況下AOF文件保存的數據集要比RDB文件保存的數據集要完整.
RDB內存快照
RDB是redis默認開啟的持久化方式
工作方式
當 Redis 需要保存 dump.rdb 文件時, 服務器執行以下操作:
- Redis 調用forks. 同時擁有父進程和子進程。
- 子進程將數據集寫入到一個臨時 RDB 文件中。
- 當子進程完成對新 RDB 文件的寫入時,Redis 用新 RDB 文件替換原來的 RDB 文件,並刪除舊的 RDB 文件。
這種工作方式使得 Redis 可以從寫時復制(copy-on-write)機制中獲益,與AOF相比,在恢復大的數據集的時候,RDB方式會更快一些.
RDB是一個非常緊湊的文件,它保存了某個時間點得數據集,非常適用於數據集的備份,比如你可以在每個小時報保存一下過去24小時內的數據,同時每天保存過去30天的數據,這樣即使出了問題你也可以根據需求恢復到不同版本的數據集.
在命令行里,可以通過 save
或 bgsave
,命令來手動立刻觸發生成內存快照
RDB配置
dbfilename dump.rdb ##內存快照物理文件地址
save 60 1000 ##該設置會讓 Redis 在滿足“ 60 秒內有至少有 1000 個鍵被改動”這一條件時, 自動保存一次數據集,可以設置多個
##配置文件里面,有以下默認配置
save 900 1
save 300 10
save 60 10000
AOF日志追加
工作方式
AOF 重寫和 RDB 創建快照一樣,都巧妙地利用了寫時復制機制:
- Redis 執行 fork() ,現在同時擁有父進程和子進程。
- 子進程開始將新 AOF 文件的內容寫入到臨時文件。
- 對於所有新執行的寫入命令,父進程一邊將它們累積到一個內存緩存中,一邊將這些改動追加到現有 AOF 文件的末尾,這樣樣即使在重寫的中途發生停機,現有的 AOF 文件也還是安全的。
- 當子進程完成重寫工作時,它給父進程發送一個信號,父進程在接收到信號之后,將內存緩存中的所有數據追加到新 AOF 文件的末尾。
- 搞定!現在 Redis 原子地用新文件替換舊文件,之后所有命令都會直接追加到新 AOF 文件的末尾。
AOF吸取RDB的優點,在第一次使用AOF的時候,
AOF配置
appendfilename "appendonly.aof" ##aof文件存儲位置
appendonly yes ##打開aof功能,默認no
###aof寫入磁盤的機制,關閉aof后,可以將他們都注釋,默認是appendfsync everysec,以下三種方式3選1
# appendfsync always ##每次有新命令追加到 AOF 文件時就執行一次 fsync :非常慢,也非常安全
# appendfsync everysec ##每秒 fsync 一次:足夠快(和使用 RDB 持久化差不多),並且在故障時只會丟失 1 秒鍾的數據。(默認)
# appendfsync no ##從不 fsync :將數據交給操作系統來處理。更快,也更不安全的選擇。
推薦(並且也是默認)的措施為每秒 fsync 一次, 這種 fsync 策略可以兼顧速度和安全性。
aof-use-rdb-preamble yes ##aof結合rdb的功能,默然開啟,建議開啟
在redis的配置文件中,大概1000多行的位置,有一個描述
redis的AOF持久功能,實際是結合了rdb的功能的,在首次進行AOF追加的時候,會將內存現有的數據以RDB快照的形式儲存在aof文件的開始位置,然后如果有追加,就會在AOF文件中將command加入到末尾。
也可以通過命令BGREWRITEAOF
,將redis現在的數據,以RDB快照的形式重寫進aof文件中,並覆蓋aof文件中原有的數據(命令記錄就消失,只有rdb快照,文件變小了))。
存有rdb快照的aof文件內容如下:
redis集群
redis主從模式
在 Redis 復制的基礎上,使用和配置主從復制非常簡單,能使得從 Redis 服務器(下文稱 slave)能精確得復制主 Redis 服務器(下文稱 master)的內容。每次當 slave 和 master 之間的連接斷開時, slave 會自動重連到 master 上,並且無論這期間 master 發生了什么, slave 都將嘗試讓自身成為 master 的精確副本。
這個系統的運行依靠三個主要的機制:
- 當一個 master 實例和一個 slave 實例連接正常時, master 會發送一連串的命令流來保持對 slave 的更新,以便於將自身數據集的改變復制給 slave , :包括客戶端的寫入、key 的過期或被逐出等等。
- 當 master 和 slave 之間的連接斷開之后,因為網絡問題、或者是主從意識到連接超時, slave 重新連接上 master 並會嘗試進行部分重同步:這意味着它會嘗試只獲取在斷開連接期間內丟失的命令流。
- 當無法進行部分重同步時, slave 會請求進行全量重同步。這會涉及到一個更復雜的過程,例如 master 需要創建所有數據的快照,將之發送給 slave ,之后在數據集更改時持續發送命令流到 slave 。
配置文件(slave從機上配置即可)
replicaof 172.17.0.5 6379 ##在從機上指定master主機的IP和端口
masterauth 123456 ##指定主機的認證密碼
replica-read-only yes ##從機使用只讀模式
Sentinel哨兵模式
Redis 的 Sentinel 系統用於管理多個 Redis 服務器(instance), 該系統執行以下三個任務:
- 監控(Monitoring): Sentinel 會不斷地檢查你的主服務器和從服務器是否運作正常。
- 提醒(Notification): 當被監控的某個 Redis 服務器出現問題時, Sentinel 可以通過 API 向管理員或者其他應用程序發送通知。
- 自動故障遷移(Automatic failover): 當一個主服務器不能正常工作時, Sentinel 會開始一次自動故障遷移操作, 它會將失效主服務器的其中一個從服務器升級為新的主服務器, 並讓失效主服務器的其他從服務器改為復制新的主服務器; 當客戶端試圖連接失效的主服務器時, 集群也會向客戶端返回新主服務器的地址, 使得集群可以使用新主服務器代替失效服務器。
Redis Sentinel 是一個分布式系統, 你可以在一個架構中運行多個 Sentinel 進程(progress), 這些進程使用流言協議(gossip protocols)來接收關於主服務器是否下線的信息, 並使用投票協議(agreement protocols)來決定是否執行自動故障遷移, 以及選擇哪個從服務器作為新的主服務器。
雖然 Redis Sentinel 釋出為一個單獨的可執行文件 redis-sentinel , 但實際上它只是一個運行在特殊模式下的 Redis 服務器, 你可以在啟動一個普通 Redis 服務器時通過給定 –sentinel 選項來啟動 Redis Sentinel 。
配置文件(sentinel.conf)
##Sentinel 去監視一個名為 mymaster 的主服務器,
##這個主服務器的 IP 地址為 127.0.0.1 , 端口號為 6379 ,
##而將這個主服務器判斷為失效至少需要 2 個 Sentinel 同意 (只要同意 Sentinel 的數量不達標,自動故障遷移就不會執行)
sentinel monitor mymaster 127.0.0.1 6379 2
##master服務器的認證密碼
sentinel auth-pass mymaster 123456
##指定Sentinel 認為服務器已經斷線所需的毫秒數。
##不過只有一個 Sentinel 將服務器標記為主觀下線並不一定會引起服務器的自動故障遷移:
##只有在足夠數量的 Sentinel 都將一個服務器標記為主觀下線之后,
##服務器才會被標記為客觀下線(objectively down, 簡稱 ODOWN ), 這時自動故障遷移才會執行。
sentinel down-after-milliseconds mymaster 60000
##多個sentinel之間投票表決超時時間
sentinel failover-timeout mymaster 180000
##parallel-syncs 選項指定了在執行故障轉移時,
##最多可以有多少個從服務器同時對新的主服務器進行同步, 這個數字越小, 完成故障轉移所需的時間就越長。
sentinel parallel-syncs mymaster 1
啟動方式
##第一種
redis-server /path/to/sentinel.conf --sentinel
##第二種
redis-sentinel /path/to/sentinel.conf
集群啟動順序
1. 啟動sentinel集群
2. 啟動redis master主機
3. 啟動redis slave從機
下以是當主機掛掉后,在sentinel控制台打印的日志信息
需要注意的是:
sentinel監聽到master主機掉線后,會通過選舉算法,產生新的master主機,並通知其它在線的從機(除剛剛被選為master的那台以外)連接到新的主機上面,所以在master和slave上的認證密碼應該相同,需要在master和slave所有redis機器上面配置 masterauth password
,剛剛斷掉的主機如果重新啟動的話,會被sentinel集群當作從機,並連接到選舉產生的主機上面。同里sentinel會將現在的master監聽信息固化(持久化)到自己的sentinel.conf配置文件里
Cluster模式
為什么要實現redis cluster?
1. 主從復制不能實現高可用
2. 隨着公司發展,用戶數量增多,並發越來越多,業務需要更高的QPS,而主從復制中單機的QPS可能無法滿足業務需求
3. 數據量的考慮,現有服務器內存不能滿足業務數據的需要時,單純向服務器添加內存不能達到要求,此時需要考慮分布式需求,把數據分布到不同服務器上
4. 網絡流量需求:業務的流量已經超過服務器的網卡的上限值,可以考慮使用分布式來進行分流
5. 離線計算,需要中間環節緩沖等別的需求
常見的分區方式
全量數據,單機Redis節點無法滿足要求,按照分區規則把數據分到若干個子集當中
- 節點取余分區:
節點取余方式是非常簡單的一種分區方式
節點取余分區方式有一個問題:即當增加或減少節點時,原來節點中的80%的數據會進行遷移操作,對所有數據重新進行分布
節點取余分區方式建議使用多倍擴容的方式,例如以前用3個節點保存數據,擴容為比以前多一倍的節點即6個節點來保存數據,這樣只需要適移50%的數據。數據遷移之后,第一次無法從緩存中讀取數據,必須先從數據庫中讀取數據,然后回寫到緩存中,然后才能從緩存中讀取遷移之后的數據 - 一致性哈希分區:
對每一個key進行hash運算,被哈希后的結果在哪個token的范圍內,則按順時針去找最近的節點,這個key將會被保存在這個節點上。
在上面的圖中,有4個key被hash之后的值在在n1節點和n2節點之間,按照順時針規則,這4個key都會被保存在n2節點上,
如果在n1節點和n2節點之間添加n5節點,當下次有key被hash之后的值在n1節點和n5節點之間,這些key就會被保存在n5節點上面了
在上面的例子里,添加n5節點之后,數據遷移會在n1節點和n2節點之間進行,n3節點和n4節點不受影響,數據遷移范圍被縮小很多
同理,如果有1000個節點,此時添加一個節點,受影響的節點范圍最多只有千分之2
一致性哈希一般用在節點比較多的時候 - 虛擬槽位分配:
虛擬槽分區是Redis Cluster采用的分區方式
預設虛擬槽,每個槽就相當於一個數字,有一定范圍。每個槽映射一個數據子集,一般比節點數大
- 把16384槽按照節點數量進行平均分配,由節點進行管理
- 對每個key按照CRC16規則進行hash運算
- 把hash結果對16383進行取余
- 把余數發送給Redis節點
- 節點接收到數據,驗證是否在自己管理的槽編號的范圍
如果在自己管理的槽編號范圍內,則把數據保存到數據槽中,然后返回執行結果
如果在自己管理的槽編號范圍外,則會把數據發送給正確的節點,由正確的節點來把數據保存在對應的槽中
需要注意的是:Redis Cluster的節點之間會共享消息,每個節點都會知道是哪個節點負責哪個范圍內的數據槽
redis cluster槽位管理
把16384個槽平均分配給節點進行管理,每個節點只能對自己負責的槽進行讀寫操作
由於每個節點之間都彼此通信,每個節點都知道另外節點負責管理的槽范圍
客戶端訪問任意節點時,對數據key按照CRC16規則進行hash運算,然后對運算結果對16383進行取作,如果余數在當前訪問的節點管理的槽范圍內,則直接返回對應的數據
如果不在當前節點負責管理的槽范圍內,則會告訴客戶端去哪個節點獲取數據,由客戶端去正確的節點獲取數據
1.每個節點通過通信都會共享Redis Cluster中槽和集群中對應節點的關系
2.客戶端向Redis Cluster的任意節點發送命令,接收命令的節點會根據CRC16規則進行hash運算與16383取余,計算自己的槽和對應節點
3.如果保存數據的槽被分配給當前節點,則去槽中執行命令,並把命令執行結果返回給客戶端
4.如果保存數據的槽不在當前節點的管理范圍內,則向客戶端返回moved重定向異常
5.客戶端接收到節點返回的結果,如果是moved異常,則從moved異常中獲取目標節點的信息
6.客戶端向目標節點發送命令,獲取命令執行結果
key過期策略
定期刪除(主動)
redis 會將每個設置了過期時間的 key 放入到一個獨立的字典中,以后會定期遍歷這個字典來刪除到期的 key。
Redis 默認會每秒進行十次過期掃描(100ms一次),過期掃描不會遍歷過期字典中所有的 key,而是采用了一種簡單的貪心策略。
- 從過期字典中隨機 20 個 key;
- 刪除這 20 個 key 中已經過期的 key;
- 如果過期的 key 比率超過 1/4,那就重復步驟 1;
配置文件
hz 10 ##默認情況下,每秒執行10次過期key掃描(100ms/次)
redis默認是每隔 100ms就隨機抽取一些設置了過期時間的key,檢查其是否過期,如果過期就刪除。注意這里是隨機抽取的。為什么要隨機呢?你想一想假如 redis 存了幾十萬個 key ,每隔100ms就遍歷所有的設置過期時間的 key 的話,就會給 CPU 帶來很大的負載。
惰性刪除(被動)
所謂惰性策略就是在客戶端訪問這個key的時候,redis對key的過期時間進行檢查,如果過期了就立即刪除,不會給你返回任何東西。
總結:定期刪除是集中處理,惰性刪除是零散處理。
內存淘汰策略
為什么需要內存淘汰策略
有了以上過期策略的說明后,就很容易理解為什么需要淘汰策略了,因為不管是定期采樣刪除還是惰性刪除都不是一種完全精准的刪除,就還是會存在key沒有被刪除掉的場景,同里redis服務器內存容量有限,所以就需要內存淘汰策略進行補充。
通過閱讀redis的配置文件,所提供的內存淘汰策略一共有8種
淘汰策略
- noeviction:當內存使用超過配置的時候會返回錯誤,不會驅逐任何鍵
- allkeys-lru:加入鍵的時候,如果過限,首先通過LRU算法驅逐最久沒有使用的鍵
- volatile-lru:加入鍵的時候如果過限,首先從設置了過期時間的鍵集合中驅逐最久沒有使用的鍵
- allkeys-random:加入鍵的時候如果過限,從所有key隨機刪除
- volatile-random:加入鍵的時候如果過限,從過期鍵的集合中隨機驅逐
- volatile-ttl:從配置了過期時間的鍵中驅逐馬上就要過期的鍵
- volatile-lfu:從所有配置了過期時間的鍵中驅逐使用頻率最少的鍵
- allkeys-lfu:從所有鍵中驅逐使用頻率最少的鍵
LRU:最久沒有使用
LFU:使用頻率最少
配置文件
maxmemory-policy noeviction ##redis默認采用的是noeviction策略,即容量滿了后,返回錯誤。不會淘汰任何key。
緩存擊穿
概念
緩存擊穿是指緩存中沒有但數據庫中有的數據(一般是緩存時間到期的某條),這時由於並發用戶特別多,同時讀取緩存沒讀到這條數據,又同時去數據庫去取數據,引起數據庫壓力瞬間增大,造成過大壓力
解決方案
- 設置熱點數據永遠不過期。
- 加互斥鎖,互斥鎖參考代碼如下
緩存穿透
概念
緩存穿透是指緩存和數據庫中都沒有的數據,而用戶不斷發起請求,如發起為id為“-1”的數據或id為特別大不存在的數據。這時的用戶很可能是攻擊者,攻擊會導致數據庫壓力過大。
解決方案
- 接口層增加校驗,如用戶鑒權校驗,id做基礎校驗,id<=0的直接攔截(布隆過濾器);
- 從緩存取不到的數據,在數據庫中也沒有取到,這時也可以將key-value對寫為key-null,緩存有效時間可以設置短點,如30秒(設置太長會導致正常情況也沒法使用)。這樣可以防止攻擊用戶反復用同一個id暴力攻擊
緩存雪崩
概念
緩存雪崩是指緩存中數據大批量到過期時間,而查詢數據量巨大,引起數據庫壓力過大甚至down機。和緩存擊穿不同的是,緩存擊穿指並發查同一條數據,緩存雪崩是不同數據都過期了,很多數據都查不到從而查數據庫。
解決方案
- 緩存數據的過期時間設置隨機,防止同一時間大量數據過期現象發生。
- 如果緩存數據庫是分布式部署,將熱點數據均勻分布在不同搞得緩存數據庫中。
- 設置熱點數據永遠不過期。