CentOS7安裝Redis及基本的認識


1.Redis 下載地址:https://redis.io/download

2.上傳到服務器指定文件夾 ,我這邊傳到了根目錄下 /mysoft 這個目錄下

 解壓  tar  -zxvf redis-4.0.8.tar.gz  -C /mysoft/

3.進行編譯安裝

cd redis-4.0.8
make //編譯
cd src
make install PREFIX=/usr/local/redis //安裝到這個目錄下

 編譯可能會出錯,這邊需要安裝  gcc 服務

yum install gcc

我這邊把配置文件移到了 安裝目錄下 /usr/local/redis/etc

mkdir /usr/local/redis/etc
mv redis.conf /usr/local/redis/etc

4.簡單配置  

配置密碼:

//取消該注解並在后面配上密碼
 requirepass newings2018

配置遠程連接

# bind 127.0.0.1 //注釋掉改行
protected-mode yes //yes改為no

配置以后台方式運行

daemonize yes //no改成yes

5.將redis加入到開機啟動

  vi /etc/rc.local //在里面添加內容:/usr/local/redis/bin/redis-server  /usr/local/redis/etc/redis.conf (意思就是開機調用這段開啟redis的命令)

6..最后一定要把redis對應的端口開放  我這里用的阿里雲服務器 需要配置安全組規則

這樣便可以啟動 Redis 並加載配置文件

/usr/local/redis/bin/redis-server /usr/local/redis/etc/redis.conf

關閉服務

pkill redis-server

啟動redis客戶端:進入src目錄  ./redis-cli -p 6399

以密碼登陸 :進入src目錄  ./redis-cli -h 127.0.0.1 -p 6399 -a password    flushall刪除所有數據  

select  index進入指定數據庫  flushdb   刪除該db的數據

 數據類型:

  官網是這么描述的:

  Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache and message broker. It supports data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, geospatial indexes with radius queries and streams. Redis has built-in replication, Lua scripting, LRU eviction, transactions and different levels of on-disk persistence, and provides high availability via Redis Sentinel and automatic partitioning with Redis Cluster.(Redis是開源的(BSD許可)內存數據結構存儲,用作數據庫,緩存和消息代理。它支持數據結構,例如字符串,哈希,列表,集合,帶范圍查詢的排序集合,位圖,超日志,帶有半徑查詢和流的地理空間索引。Redis具有內置的復制,Lua腳本,LRU逐出,事務和不同級別的磁盤持久性,並通過Redis Sentinel和Redis Cluster自動分區提供高可用性。)

  這段文字描述了redis的書記結構以及數據持久化及淘汰,以及高可用相關。我們先來看看相關的數據結構及其基本操作:

1.String 字符串:

  存儲類型:可以用來存儲字符串、整數、浮點數。

//設置多個值(批量操作,原子性)
mset key1 value1 key2 value2 //設置值,如果 key 存在,則不成功 
setnx key1 value1 //基於setnx 可實現分布式鎖。用 del key 釋放鎖。但如果釋放鎖的操作失敗了,導致其他節點永遠獲取不到鎖,怎么辦?加過期時間。單獨用 expire 加過期,也失敗了,無法保證原子性,怎么辦?多參數
set key value [expiration EX seconds|PX milliseconds][NX|XX] //使用參數的方式
set lock1 1 EX 10 NX //(整數)值原子遞增
incr key1 incrby qingshan 100
//(整數)值遞減
decr qingshan decrby qingshan 100
//浮點數增量
set f 2.6 incrbyfloat f 7.3
//獲取多個值
mget key1 key2 //獲取值長度
strlen key1 //字符串追加內容
append key1 good //獲取指定范圍的字符
getrange key 0 8
//位計算
set k1 a setbit k1 6 1 setbit k1 7 0
get k1 //key 分層的方式 student 表 有ID 字段 sno字段 sname字段 company字段
mset student:1:sno 8888 student:1:sname 沐風 student:1:company 騰訊 //獲取值的時候一次獲取多個值:
mget student:1:sno 9999 student:1:sname java student:1:company alibaba

應用場景:

  •   緩存String 類型,例如:熱點數據緩存,對象緩存,全頁緩存。可以提升熱點數據的訪問速度。
  •   數據共享:因為 Redis 是分布式的獨立服務,可以在多個應用之間共享.例如:分布式 Session
  •   分布式鎖:setnx 方法,只有不存在時才能添加成功,返回 true。
  •   全局 ID:INCRBY,利用原子性.例如:文章的閱讀量,微博點贊數,允許一定的延遲,先寫入 Redis 再定時同步到數據庫。(限流)以訪問者的 IP 和其他信息作為 key,訪問一次增加一次計數,超過次數則返回 false。

  缺點:key 太長,占用的空間太多。

2.Hash 哈希


  存儲類型:包含鍵值對的無序散列表。value 只能是字符串,不能嵌套其他類型。

  同樣是存儲字符串,Hash 與 String 的主要區別?

  1. 把所有相關的值聚集到一個 key 中,節省內存空間
  2. 只使用一個 key,減少 key 沖突
  3. 當需要批量獲取值的時候,只需要使用一個命令,減少內存/IO/CPU 的消耗

  Hash 不適合的場景:

  1. Field 不能單獨設置過期時間
  2. 沒有 bit 操作
  3. 需要考慮數據量分布的問題(value 值非常大的時候,無法分布到多個節點)
//設置值
hset wuzz name wuzz //設置值
hset wuzz age 18
//設置多個值
hmset wuzz sex m height 180
//獲取值
hget wuzz name //批量獲取
hmget wuzz name age sex height //獲取keys
hkeys wuzz //獲取vals
hvals wuzz //獲取所有
hgetall wuzz //是否存在
hget exists wuzz //刪除
hdel wuzz age hgetall wuzz //原子遞增
hincrby wuzz age 1

應用場景:

  hash 類型十分適合存儲對象類數據,相對於在 string 中介紹的把對象轉化為 json 字符串存儲,hash 的結構可以任意添加或刪除‘字段名’,更加高效靈活。String 可以做的事情,Hash 基本都可以做(位運算除外)。

3.List 列表

  存儲類型:存儲有序的字符串(從左到右),元素可以重復。可以充當隊列和棧的角色。

//設置值 此時隊列是 a
lpush queue a //設置值 此時隊列是 c b a
lpush queue b c //設置值 此時隊列是 c b a d e
rpush queue d e //從左邊取出值 則是 c 采用blpop會沒有任何元素可以彈出的時候,連接會被阻塞
lpop queue //取出所有值
lrange queue 0 -1
//獲取指定位置的值
lindex queue 0
//取出右邊第一個
rpop queue

應用場景:

  因為 List 是有序的,可以用來做用戶時間線(排序相關)

  消息隊列:List 提供了兩個阻塞的彈出操作:BLPOP/BRPOP,可以設置超時時間。BLPOP:BLPOP key1 timeout 移出並獲取列表的第一個元素, 如果列表沒有元素會阻塞列表直到等待超時或發現可彈出元素為止。BRPOP:BRPOP key1 timeout 移出並獲取列表的最后一個元素, 如果列表沒有元素會阻塞列表直到等待超時或發現可彈出元素為止。隊列:先進先出:rpush blpop,左頭右尾,右邊進入隊列,左邊出隊列。棧:先進后出:rpush brpop

4.Set 集合

  存儲類型:String 類型的無序集合,最大存儲數量 2^32-1(40 億左右)。

//添加一個或者多個元素
sadd myset a b c d e f g //獲取所有元素
smembers myset //統計元素個數
scard myset //隨機獲取一個元素
srandmember key //隨機彈出一個元素
spop myset //移除一個或者多個元素
srem myset d e f //查看元素是否存在
sismember myset a //獲取差集
sdiff set1 set2 //獲取交集( intersection )
sinter set1 set2 //獲取並集
sunion set1 set2

應用場景:

  • 抽獎:隨機獲取元素。
  • 點贊 、簽到、打卡:比如有條微博的 ID 是 t1001,用戶 ID 是 u3001。用 like:t1001 來維護 t1001 這條微博的所有點贊用戶。點贊了這條微博:sadd like:t1001 u3001取消點贊:srem like:t1001 u3001是否點贊:sismember like:t1001 u3001點贊的所有用戶:smembers like:t1001點贊數:scard like:t1001
  • 商品標簽:維護商品所有的標簽。某個商品有的一些標簽都可以維護到set里。

5.ZSet 有序集合:

  sorted set,有序的 set,每個元素有個 score。score 相同時,按照 key 的 ASCII 碼排序。數據結構對比:

//添加元素
zadd myzset 10 java 20 php 30 ruby 40 cpp 50 python //獲取全部元素
zrange myzset 0 -1 withscores zrevrange myzset 0 -1 withscores //根據分值區間獲取元素
zrangebyscore myzset 20 30
//移除元素 也可以根據 score rank 刪除
zrem myzset php cpp //統計元素個數
zcard myzset //分值遞增
zincrby myzset 5 python //根據分值統計個數
zcount myzset 20 60
//獲取元素 rank
zrank myzset java //獲取元素 score
zsocre myzset java //也有倒序的 rev 操作(reverse)

應用場景:

  排行榜:id 為 6001 的新聞點擊數加 1:zincrby hotNews:20190926 1 n6001.獲取今天點擊最多的 15 條:zrevrange hotNews:20190926 0 15 withscores.

6.其他數據類型介紹:

  在 2.8.9 版本添加了 HyperLogLog 結構。Redis HyperLogLog 是用來做基數統計的算法,HyperLogLog 的優點是,例如統計一個集合里面不同元數的個數,在輸入元素的數量或者體積非常非常大時,計算基數所需的空間總是固定 的、並且是很小的。

  這里還需要說明的是另外一種數據類型 geospatial indexes  。這個東東是可以用來做地理位置信息存儲及簡單計算的,比如要獲取某個經緯度的點方圓多少公里內的點的經緯度,兩個點之間的距離等等。

//設置某個點的經緯度
geoadd location 120.1 29.2 hangzhou //獲取
geopos location hangzhou

  對於其他的一些操作,就Redis的java客戶端來說,Jedis也已經提供了這個地理空間功能的基本操作Api

發布訂閱模式:

  通過隊列的 rpush 和 lpop 可以實現消息隊列(隊尾進隊頭出),但是消費者需要不停地調用 lpop 查看 List 中是否有等待處理的消息(比如寫一個 while 循環)。為了減少通信的消耗,可以 sleep()一段時間再消費,但是會有兩個問題:

  • 如果生產者生產消息的速度遠大於消費者消費消息的速度,List 會占用大量的內存。
  • 消息的實時性降低。list 還提供了一個阻塞的命令:blpop,沒有任何元素可以彈出的時候,連接會被阻塞。

  除了通過 list 實現消息隊列之外,Redis 還提供了一組命令實現發布/訂閱模式。這種方式,發送者和接收者沒有直接關聯(實現了解耦),接收者也不需要持續嘗試獲取消息。

  首先,我們有很多的頻道(channel),我們也可以把這個頻道理解成 queue。訂閱者可以訂閱一個或者多個頻道。消息的發布者(生產者)可以給指定的頻道發布消息。只要有消息到達了頻道,所有訂閱了這個頻道的訂閱者都會收到這條消息。需要注意的注意是,發出去的消息不會被持久化,因為它已經從隊列里面移除了,所以消費者只能收到它開始訂閱這個頻道之后發布的消息。下面我們來看一下發布訂閱命令的使用方法。訂閱者訂閱頻道:可以一次訂閱多個,比如這個客戶端訂閱了 3 個頻道。

subscribe channel-1 channel-2 channel-3

  發布者可以向指定頻道發布消息(並不支持一次向多個頻道發送消息):

publish channel-1 hello

  取消訂閱(不能在訂閱狀態下使用):

unsubscribe channel-1

按規則(P P attern ) 訂閱頻道。支持?和*占位符。?代表一個字符,*代表 0 個或者多個字符。

  • 消費端 1,關注運動信息:psubscribe *sport
  • 消費端 2,關注所有新聞:psubscribe news*
  • 消費端 3,關注天氣新聞:psubscribe news-weather
  • 生產者,發布 3 條信息:publish news-sport yaoming,publish news-music jaychou,publish news-weather rain。

Redis 事務:

  為什么要用事務:

  我們知道 Redis 的單個命令是原子性的(比如 get set mget mset),如果涉及到多個命令的時候,需要把多個命令作為一個不可分割的處理序列,就需要用到事務。例如我們之前說的用 setnx 實現分布式鎖,我們先 set,然后設置對 key 設置 expire,防止 del 發生異常的時候鎖不會被釋放,業務處理完了以后再 del,這三個動作我們就希望它們作為一組命令執行。Redis 的事務有兩個特點:

  1. 按進入隊列的順序執行。
  2. 不會受到其他客戶端的請求的影響。

  Redis 的事務涉及到四個命令:multi(開啟事務),exec(執行事務),discard(取消事務),watch(監視)

  案例場景:user1 和 user2 各有 1000 元,user1 需要向 user2 轉賬 100 元。user1 的賬戶余額減少 100 元,user2 的賬戶余額增加 100 元。

//初始化數據
127.0.0.1:6379> set user1 1000 OK 127.0.0.1:6379>  set user2 1000 OK //開啟事務
127.0.0.1:6379> multi OK //執行命令入隊
127.0.0.1:6379>  decrby user1 100 QUEUED 127.0.0.1:6379>  incrby user2 100 QUEUED //執行事務
127.0.0.1:6379> exec 1) (integer) 900
2) (integer) 1100
127.0.0.1:6379> get user1
"900" 127.0.0.1:6379> get user2
"1100" 127.0.0.1:6379>

  通過 multi 的命令開啟事務。事務不能嵌套,多個 multi 命令效果一樣。multi 執行后,客戶端可以繼續向服務器發送任意多條命令, 這些命令不會立即被執行, 而是被放到一個隊列中, 當 exec 命令被調用時, 所有隊列中的命令才會被執行。通過 exec 的命令執行事務。如果沒有執行 exec,所有的命令都不會被執行。如果中途不想執行事務了,怎么辦?可以調用 discard 可以清空事務隊列,放棄執行。

whatch 命令:

  在 Redis 中還提供了一個 watch 命令。它可以為 Redis 事務提供 CAS 樂觀鎖行為(Check and Set / Compare andSwap),也就是多個線程更新變量的時候,會跟原值做比較,只有它沒有被其他線程修改的情況下,才更新成新的值。我們可以用 watch 監視一個或者多個 key,如果開啟事務之后,至少有一個被監視key 鍵在 exec 執行之前被修改了, 那么整個事務都會被取消(key 提前過期除外)。可以用 unwatch 取消。例子如下:

127.0.0.1:6379> set balance 1000 OK 127.0.0.1:6379> watch balance OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> incrby balance 100 QUEUED //此時此刻 客戶端2 對balance進行了修改 //127.0.0.1:6379> decrby balance 100 //(integer) 900
127.0.0.1:6379> exec (nil) 127.0.0.1:6379> get balance "900"

事務可能遇到的問題

  我們把事務執行遇到的問題分成兩種,一種是在執行 exec 之前發生錯誤,一種是在執行 exec 之后發生錯誤。

  • 在執行 exec 之前 發生錯誤,比如:入隊的命令存在語法錯誤,包括參數數量,參數名等等(編譯器錯誤)。在這種情況下事務會被拒絕執行,也就是隊列中所有的命令都不會得到執行。
  • 在執行 exec 之后 發生錯誤,比如,類型錯誤,比如對 String 使用了 Hash 的命令,這是一種運行時錯誤。在這種發生了運行時異常的情況下,只有錯誤的命令沒有被執行,但是其他命令沒有受到影響。這個顯然不符合我們對原子性的定義,也就是我們沒辦法用 Redis 的這種事務機制來實現原子性,保證數據的一致。

  為什么在一個事務中存在錯誤,redis不會回滾呢?只有當被調用的Redis命令有語法錯誤時,這條命令才會執行失敗(在將這個命令放入事務隊列期間,Redis能夠發現此類問題),或者對某個鍵執行不符合其數據類型的操作:實際上,這就意味着只有程序錯誤才會導致Redis命令執行失敗,這種錯誤很有可能在程序開發期間發現,一般很少在生產環境發現。Redis已經在系統內部進行功能簡化,這樣可以確保更快的運行速度,因為Redis不需要事務回滾的能力。對於Redis事務的這種行為,有一個普遍的反對觀點,那就是程序有可能會有缺陷(bug)。但是,你應當注意到:事務回滾並不能解決任何程序錯誤。例如,如果某個查詢會將一個鍵的值遞增2,而不是1,或者遞增錯誤的鍵,那么事務回滾機制是沒有辦法解決這些程序問題的。請注意,沒有人能解決程序員自己的錯誤,這種錯誤可能會導致Redis命令執行失敗。正因為這些程序錯誤不大可能會進入生產環境,所以我們在開發Redis時選用更加簡單和快速的方法,沒有實現錯誤回滾的功能。

Lua 腳本:

  Lua/ˈluə/是一種輕量級腳本語言,它是用 C 語言編寫的,跟數據的存儲過程有點類似。 使用 Lua 腳本來執行 Redis 命令的好處:

  1. 一次發送多個命令,減少網絡開銷。
  2. Redis 會將整個腳本作為一個整體執行,不會被其他請求打斷,保持原子性。
  3. 對於復雜的組合命令,我們可以放在文件中,可以實現程序之間的命令集復用。

在Redis Cli 中 調用 Lua 腳本:

  使用 eval /ɪ'væl/ 方法,語法格式:

127.0.0.1:6379> eval lua-script key-num [key1 key2 key3 ....] [value1 value2 value3 ....]
eval 代表執行 Lua 語言的命令。 lua
-script 代表 Lua 語言腳本內容。 key-num 表示參數中有多少個 key,需要注意的是 Redis 中 key 是從 1 開始的,如果沒有 key 的參數,那么寫 0。 [key1 key2 key3…]是 key 作為參數傳遞給 Lua 語言,也可以不填,但是需要和 key-num 的個數對應起來。 [value1 value2 value3 ….]這些參數傳遞給 Lua 語言,它們是可填可不填的。

  例如:

在 Lua 腳本 中調用 Redis 命令:

  使用 redis.call(command, key [param1, param2…])進行操作。語法格式:

redis> eval "redis.call('set',KEYS[1],ARGV[1])" 1 lua-key lua-value
command 是命令,包括 set、get、del 等。
key 是被操作的鍵。
param1,param2…代表給 key 的參數。

  注意跟 Java 不一樣,定義只有形參,調用只有實參。Lua 是在調用時用 key 表示形參,argv 表示參數值(實參)。在 Redis 中調用 Lua 腳本執行 Redis 命令

  以上命令等價於 set name wuzz。

在 Redis 中調用 Lua 腳本 文件中的命令 ,

  操作 Redis創建 Lua 腳本文件:

//創建文件
cd /mysoft/redis-4.0.8/src vim wuzz.lua //Lua 腳本內容,先設置,再取值:
redis.call('set','wuzz',hello) return redis.call('get',wuzz) //在 Redis 客戶端中調用 Lua 腳本 0 位參數個數
cd /mysoft/redis-4.0.8/src redis-cli --eval wuzz.lua 0

  案例 :對 IP 進行限流

  需求:在 X 秒內只能訪問 Y 次。

  設計思路:用 key 記錄 IP,用 value 記錄訪問次數。拿到 IP 以后,對 IP+1。如果是第一次訪問,對 key 設置過期時間(參數 1)。否則判斷次數,超過限定的次數(參數 2),返回 0。如果沒有超過次數則返回 1。超過時間,key 過期之后,可以再次訪問。KEY[1]是 IP, ARGV[1]是過期時間 X,ARGV[2]是限制訪問的次數 Y。

-- ip_limit.lua -- IP 限流,對某個 IP 頻率進行限制 ,5 秒鍾訪問 2 次 local num=redis.call('incr',KEYS[1]) if tonumber(num)==1 then redis.call('expire',KEYS[1],ARGV[1]) return 1 elseif tonumber(num)>tonumber(ARGV[2]) then return 0
else
    return 1 end

  5 秒鍾內限制訪問 2 次,調用測試(連續調用 2 次):

./redis-cli  -h 127.0.0.1 -p 6379 -a wuzhenzhao   --eval "ip_limit.lua" app:ip:limit:192.168.8.111 , 5 2

Jedis 調用 lua 腳本相關操作:

public static void main(String[] args) { Jedis jedis = getJedisUtil(); for(int i=0; i<5; i++){ limit(); } } /** * 5秒內限制訪問2次 */
public static void limit(){ Jedis jedis = getJedisUtil(); // 只在第一次對key設置過期時間
        String lua = "local num = redis.call('incr', KEYS[1])\n" +
                "if tonumber(num) == 1 then\n" +
                "\tredis.call('expire', KEYS[1], ARGV[1])\n" +
                "\treturn 1\n" +
                "elseif tonumber(num) > tonumber(ARGV[2]) then\n" +
                "\treturn 0\n" +
                "else \n" +
                "\treturn 1\n" +
                "end\n"; Object result = jedis.evalsha(jedis.scriptLoad(lua), Arrays.asList("localhost"), Arrays.asList("5", "2")); System.out.println(result); } private static Jedis getJedisUtil() { String ip = ResourceUtil.getKey("redis.host"); int port = Integer.valueOf(ResourceUtil.getKey("redis.port")); String password = ResourceUtil.getKey("redis.password"); JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); JedisPool pool = new JedisPool(jedisPoolConfig, ip, port, 10000, password); return pool.getResource(); }

緩存 Lua 腳本:

  為什么要緩存?在腳本比較長的情況下,如果每次調用腳本都需要把整個腳本傳給 Redis 服務端,會產生比較大的網絡開銷。為了解決這個問題,Redis 提供了 EVALSHA 命令,允許開發者通過腳本內容的 SHA1 摘要來執行腳本。

  Redis 在執行 script load 命令時會計算腳本的 SHA1 摘要並記錄在腳本緩存中,執行 EVALSHA 命令時 Redis 會根據提供的摘要從腳本緩存中查找對應的腳本內容,如果找到了則執行腳本,否則會返回錯誤:"NOSCRIPT No matching script. Please useEVAL."

  腳本超時:

  Redis 的指令執行本身是單線程的,這個線程還要執行客戶端的 Lua 腳本,如果 Lua腳本執行超時或者陷入了死循環,是不是沒有辦法為客戶端提供服務了呢?

  使用  eval 'while(true) do end' 0 來模擬死循環,為了防止某個腳本執行時間過長導致 Redis 無法提供服務,Redis 提供了 lua-time-limit 參數限制腳本的最長運行時間,默認為 5 秒鍾。

  lua-time-limit 5000(redis.conf 配置文件中) 當腳本運行時間超過這一限制后,Redis 將開始接受其他命令但不會執行(以確保腳本的原子性,因為此時腳本並沒有被終止),而是會返回“BUSY”錯誤。Redis 提供了一個 script kill 的命令來中止腳本的執行。新開一個客戶端:script kill

  如果當前執行的 Lua 腳本對 Redis 的數據進行了修改(SET、DEL 等),那么通過 script kill 命令是不能終止腳本運行的。因為要保證腳本運行的原子性,如果腳本執行了一部分終止,那就違背了腳本原子性的要求。最終要保證腳本要么都執行,要么都不執行。遇到這種情況,只能通過 shutdown nosave 命令來強行終止 redis。shutdown nosave 和 shutdown 的區別在於 shutdown nosave 不會進行持久化操作,意味着發生在上一次快照后的數據庫修改都會丟失。

  如果我們有一些特殊的需求,可以用 Lua 來實現。

redis過期策略和淘汰機制

  redis刪除清理key的一般有如下兩種情況:(1)對設置過期時間redis的進行刪除(2)通過內存淘汰機制刪除部分key;下面我們對這兩種情況的原理分別進行探討。

設置過期時間redis的清理機制:

  redis中對某一個key設置過期時間相信我們再熟悉不過了,因為我們只要在set key的時候,就可以給一個expire time,就是過期時間,指定這個key多久后過期,到達過期時間后緩存就會失效。那么具體redis是怎么刪除我們這些已經過期的key的呢?答案是:定期刪除+惰性刪除

  所謂定期刪除,指的是redis默認是每隔100ms就隨機抽取一些設置了過期時間的key,檢查其是否過期,如果過期就刪除。注意,這里並不是每隔100ms就遍歷所有的設置過期時間的key,因為如果是遍歷檢測所有設置過期時間的key的話那你的redis基本就掛了。簡單做個比喻說明:假設redis里存放了100萬個key,都設置了過期時間,你每隔幾百毫秒,就檢查這100萬個key,那么很不幸你的redis基本上就死了,因為cpu的負載全消耗在你檢查過期key上了。實際上redis是每隔100ms隨機抽取一些key來檢查和刪除的。但是隨機抽取一些key的話又會造成另一個問題: 可能會出現很多過期key到了時間並沒有被刪除掉,因為隨機抽取的時候並沒有抽取到,那redis又是怎么解決這個問題的呢?答案就是惰性刪除。惰性刪除的意思就是,在你獲取某個key的時候,redis會檢查一下 ,這個key是否設置了過期時間,如果設置了過期時間那么是否過期了?如果過期了此時就會刪除該key,並不會給你返回任何東西。因此要注意並不是key到時間就會被刪除掉,而是你查詢這個key的時候,redis再懶惰的檢查一下。redis就是通過上述兩種手段結合起來,以確保過期的key一定會被刪除掉。簡單來說就是你設置的過期key,如果通過定期刪除沒有被刪除掉,就仍然會停留在內存里,占用着你的內存,這個時候只有你的系統去主動查一下那個key,才會被redis給刪除掉。但是實際上經過以上的處理機制后,如果定期刪除漏掉了很多過期key的話,然后你也沒及時去查,也就沒走惰性刪除,就會造成大量過期key堆積在內存里,最終仍然可能會導致redis內存耗盡,因此這個問題又該怎么解決呢?

  答案是:走內存淘汰策略

內存淘汰策略:

  當redis的內存占用過多或者快達到redis可用內存上限一定比例的時候,此時會觸發redis內存淘汰策略,具體有如下一些策略:

  1. noeviction:當內存不足以容納新寫入數據時,新寫入操作會報錯,這個一般沒人用
  2. allkeys-lru當內存不足以容納新寫入數據時,在鍵空間中,移除最近最少使用的key(這個是最常用的)
  3. allkeys-random:當內存不足以容納新寫入數據時,在鍵空間中,隨機移除某個key,這個一般也沒人用
  4. volatile-lru:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,移除最近最少使用的key(這個一般不太合適)
  5. volatile-random:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,隨機移除某個key
  6. volatile-ttl:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,有更早過期時間的key優先移除

  注:一般都是采用第二種策略allkeys-lru,因為刪除最近最少使用key基本符合我們的業務要求


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM