Redis 緩存相關

 

一 Redis可緩存的數據類型

String,List,Hash,Set,ZSet

二 Redis常見的命令

2.1 String類型的命令

  • GET -獲取key值,存在返回value,不存在返回特殊字符:nil
  • INCR -將 key 中儲存的數字值增一。如果 key 不存在,那麼 key 的值會先被初始化爲 0 ,然後再執行 INCR 操作。如果值包含錯誤的類型,或字符串類型的值不能表示爲數字,那麼返回一個錯誤。

這是一個針對字符串的操作,因爲 Redis 沒有專用的整數類型,所以 key 內儲存的字符串被解釋爲十進制 64 位有符號整數來執行 INCR 操作。

  • INCRBY  將 key 所儲存的值加上增量 increment 。如果 key 不存在,那麼 key 的值會先被初始化爲 0 ,然後再執行 INCRBY 命令。DECR 和DESCBY 相反,減量。
//INCRBY key increment
redis> SET rank 50
OK
redis> INCRBY rank 20
(integer) 70
  • MSET  mset key value [key value ...] 同時設置一個或多個 key-value 對,是一個原子性(atomic)操作,所有給定 key 都會在同一時間內被設置,某些給定 key 被更新而另一些給定 key 沒有改變的情況,不可能發生。總是返回 OK (因爲 MSET 不可能失敗)。
    jedis.mset(key,value,key1,value1,key2,value2);

    同理MSETNX設置的 key都不存在時,才添加

    jedis.msetnx(key,value,key,value,key,value);
    
  • MGET 獲取值MGET時候就比較簡單,獲取多個key的z
    jedis.mget(key,key,key);
  • SET
  • SETEX 設置鍵的過期時間爲 second 秒。 SET key value EX second 效果等同於 SETEX key second value 。成功返回OK;
jedis.set("key", "value", "EX", seconds); 等同於 jedis.setex("key",seconds,"value");

類似於

 jedis.set(key,value);

 jedis.expire(key,1000);//設置生存時間1000s

不同之處是, SETEX 是一個原子性(atomic)操作,關聯值和設置生存時間兩個動作會在同一時間內完成,該命令在 Redis 用作緩存時。

  • SETPX 設置鍵的過期時間爲 millisecond 毫秒,SET key value PX millisecond 效果等同於 PSETEX key millisecond value ,成功返回OK;
jedis.set("key", "value", "PX", milliseconds); 等同於 jedis.psetex("key",milliseconds,"value");
  • SETNX 『SET if Not eXists』只在鍵不存在時,纔對鍵進行設置操作。 成功返回1,失敗0 。

    XX :只在鍵已經存在時,纔對鍵進行設置操作,成功返回1,失敗0。

jedis.set("key", "value", "NX");等同於 jedis.setnx("key", "value");

jedis.set("key", "value", "XX");

 EX 、PX和 NX、XX組合使用

jedis.set(key,value,"NX","EX",1000);//不存在時才添加,成功返回1,不成功返回0,過期時間 1000s
jedis.set(key,value,"XX","PX",10000);//存在時才添加,成功返回1,不成功返回0,過期時間 10000ms

2.2 Hash(hash表)常見命令 

  • HDEL HDEL key field [field ...] 刪除哈希表 key 中的一個或多個指定域,不存在的域將被忽略。
# 刪除單個域
redis> HDEL abbr a
(integer) 1

# 刪除不存在的域
redis> HDEL abbr not-exists-field
(integer) 0

# 刪除多個域
redis> HDEL abbr b c
(integer) 2
  • HEXISTS  HEXISTS key field 查看哈希表 key 中,給定域 field 是否存在。
redis> HEXISTS phone myphone
(integer) 0

redis> HSET phone myphone nokia-1110
(integer) 1

redis> HEXISTS phone myphone
(integer) 1
  • HGET  返回哈希表 key 中,一個或多個給定域的值。

HMGET key field [field ...]

redis> HMSET pet dog "doudou" cat "nounou"    # 一次設置多個域
OK

redis> HMGET pet dog cat fake_pet             # 返回值的順序和傳入參數的順序一樣
1) "doudou"
2) "nounou"
3) (nil)                                      # 不存在的域返回nil值
  • HKEYS 返回哈希表 key 中的所有域
redis> HMSET website google www.google.com yahoo www.yahoo.com
OK

redis> HKEYS website
1) "google"
2) "yahoo"

# 空哈希表/key不存在

redis> EXISTS fake_key
(integer) 0

redis> HKEYS fake_key
(empty list or set)
  • HMGET HMGET key field [field ...]返回哈希表 key 中,一個或多個給定域的值。如果給定的域不存在於哈希表,那麼返回一個 nil 值。
redis> HMSET pet dog "doudou" cat "nounou"    # 一次設置多個域
OK

redis> HMGET pet dog cat fake_pet             # 返回值的順序和傳入參數的順序一樣
1) "doudou"
2) "nounou"
3) (nil)                                      # 不存在的域返回nil值
  • HMSET 同時將多個 field-value (域-值)對設置到哈希表 key 中。此命令會覆蓋哈希表中已存在的域。

    如果 key 不存在,一個空哈希表被創建並執行 HMSET 操作。

# HMSET key field value [field value ...]
redis> HMSET website google www.google.com yahoo www.yahoo.com
OK

redis> HGET website google
"www.google.com"

redis> HGET website yahoo
"www.yahoo.com"
  • HSET  將哈希表 key 中的域 field 的值設爲 value 。如果 key 不存在,一個新的哈希表被創建並進行 HSET 操作。如果域 field 已經存在於哈希表中,舊值將被覆蓋。
redis> HSET website google "www.g.cn"       # 設置一個新域
(integer) 1 #返回值

redis> HSET website google "www.google.com" # 覆蓋一箇舊域
(integer) 0 #返回值
  • HSETNX  將哈希表 key 中的域 field 的值設置爲 value ,當且僅當域 field 不存在。若域 field 已經存在,該操作無效。如果 key 不存在,一個新哈希表被創建並執行 HSETNX 命令。
redis> HSETNX nosql key-value-store redis
(integer) 1 #返回值

redis> HSETNX nosql key-value-store redis       # 操作無效,域 key-value-store 已存在
(integer) 0 #返回值

2.3 List(列表)常用命令 

  • LLEN LLEN key返回列表 key 的長度。如果 key 不存在,則 key 被解釋爲一個空列表,返回 0 .如果 key 不是列表類型,返回一個錯誤。
# 空列表
redis> LLEN job
(integer) 0

# 非空列表
redis> LPUSH job "cook food"
(integer) 1

redis> LPUSH job "have lunch"
(integer) 2

redis> LLEN job
(integer) 2
  • LPOP LPOP key 移除並返回列表 key 的頭元素。
redis> LLEN course
(integer) 0

redis> RPUSH course algorithm001
(integer) 1

redis> RPUSH course c++101
(integer) 2

redis> LPOP course  # 移除頭元素
"algorithm001"
  • LPUSH  LPUSH key value [value ...] 將一個或多個值 value 插入到列表 key 的表頭,如果有多個 value 值,那麼各個 value 值按從左到右的順序依次插入到表頭: 比如說,對空列表 mylist 執行命令 LPUSH mylist a b c ,列表的值將是 c b a ,這等同於原子性地執行 LPUSH mylist a 、 LPUSH mylist b 和 LPUSH mylist c 三個命令。

    如果 key 不存在,一個空列表會被創建並執行 LPUSH 操作。當key 存在但不是列表類型時,返回一個錯誤。

# 加入單個元素

redis> LPUSH languages python
(integer) 1 #返回執行 LPUSH 命令後,列表的長度。


# 加入重複元素

redis> LPUSH languages python
(integer) 2 #返回執行 LPUSH 命令後,列表的長度。

redis> LRANGE languages 0 -1     # 列表允許重複元素
1) "python"
2) "python"


# 加入多個元素

redis> LPUSH mylist a b c
(integer) 3

redis> LRANGE mylist 0 -1
1) "c"
2) "b"
3) "a"
  • LREM LREM key count value根據參數 count 的值,移除列表中與參數 value 相等的元素。REM 命令總是返回 0 。

count 的值可以是以下幾種:

count > 0 : 從表頭開始向表尾搜索,移除與 value 相等的元素,數量爲 count 。

count < 0 : 從表尾開始向表頭搜索,移除與 value 相等的元素,數量爲 count 的絕對值。

count = 0 : 移除表中所有與 value 相等的值。

# 先創建一個表,內容排列是
# morning hello morning helllo morning

redis> LPUSH greet "morning"
(integer) 1
redis> LPUSH greet "hello"
(integer) 2
redis> LPUSH greet "morning"
(integer) 3
redis> LPUSH greet "hello"
(integer) 4
redis> LPUSH greet "morning"
(integer) 5

redis> LRANGE greet 0 4         # 查看所有元素
1) "morning"
2) "hello"
3) "morning"
4) "hello"
5) "morning"

redis> LREM greet 2 morning     # 移除從表頭到表尾,最先發現的兩個 morning
(integer) 2                     # 兩個元素被移除

redis> LLEN greet               # 還剩 3 個元素
(integer) 3

redis> LRANGE greet 0 2
1) "hello"
2) "hello"
3) "morning"

redis> LREM greet -1 morning    # 移除從表尾到表頭,第一個 morning
(integer) 1

redis> LLEN greet               # 剩下兩個元素
(integer) 2

redis> LRANGE greet 0 1
1) "hello"
2) "hello"

redis> LREM greet 0 hello      # 移除表中所有 hello
(integer) 2                    # 兩個 hello 被移除

redis> LLEN greet
(integer) 0
  • LSET   LSET key index value 將列表 key 下標爲 index 的元素的值設置爲 value 。當 index 參數超出範圍,或對一個空表( key 不存在)進行 LSET 時,返回一個錯誤。操作成功返回 ok ,否則返回錯誤信息。
# 對空列表(key 不存在)進行 LSET
redis> EXISTS list
(integer) 0

redis> LSET list 0 item
(error) ERR no such key

# 對非空列表進行 LSET
redis> LPUSH job "cook food"
(integer) 1

redis> LRANGE job 0 0  #查看列表key指定區間(開始索引-結束索引)間的元素
1) "cook food"

redis> LSET job 0 "play game"
OK

redis> LRANGE job  0 0
1) "play game"

# index 超出範圍
redis> LLEN list                    # 列表長度爲 1
(integer) 1

redis> LSET list 3 'out of range'
(error) ERR index out of range
  • LTRIM LRANGE key start stop 返回列表 key 中指定區間內的元素,區間以偏移量 start 和 stop 指定。下標(index)參數 start 和 stop 都以 0 爲底,也就是說,以 0 表示列表的第一個元素,以 1 表示列表的第二個元素,以此類推。你也可以使用負數下標,以 -1 表示列表的最後一個元素, -2 表示列表的倒數第二個元素,以此類推。
redis> RPUSH fp-language lisp
(integer) 1

redis> LRANGE fp-language 0 0
1) "lisp"

redis> RPUSH fp-language scheme
(integer) 2

redis> LRANGE fp-language 0 1
1) "lisp"
2) "scheme"
  • RPOP RPOP key 移除並返回列表 key 的尾元素。 當 key 不存在時,返回 nil 。
redis> RPUSH mylist "one"
(integer) 1

redis> RPUSH mylist "two"
(integer) 2

redis> RPUSH mylist "three"
(integer) 3

redis> RPOP mylist           # 返回被彈出的元素
"three"

redis> LRANGE mylist 0 -1    # 列表剩下的元素
1) "one"
2) "two"
  • RPUSH  RPUSH key value [value ...]將一個或多個值 value 插入到列表 key 的表尾(最右邊)。如果有多個 value 值,那麼各個 value 值按從左到右的順序依次插入到表尾:比如對一個空列表 mylist 執行 RPUSH mylist a b c ,得出的結果列表爲 a b c ,等同於執行命令 RPUSH mylist a 、 RPUSH mylist b 、 RPUSH mylist c 。如果 key 不存在,一個空列表會被創建並執行 RPUSH 操作。當 key 存在但不是列表類型時,返回一個錯誤。執行 RPUSH 操作後,表的長度。
# 添加單個元素
redis> RPUSH languages c
(integer) 1

# 添加重複元素
redis> RPUSH languages c
(integer) 2

redis> LRANGE languages 0 -1 # 列表允許重複元素
1) "c"
2) "c"

# 添加多個元素
redis> RPUSH mylist a b c
(integer) 3

redis> LRANGE mylist 0 -1
1) "a"
2) "b"
3) "c

 2.4 Set(集合)常用命令

格式: sadd  name  value

Redis的Set是string類型的無序集合。

jedis.sadd(key,value,value,value);

2.5 ZSet(有序集合)命令

zset(sorted set:有序集合) 格式: zadd  name score value

Redis zset 和 set 一樣也是string類型元素的集合,且不允許重複的成員。

不同的是每個元素都會關聯一個double類型的分數。redis正是通過分數來爲集合中的成員進行從小到大的排序。

zset的成員是唯一的,但分數(score)卻可以重複。

2.6 Key常用命令

  • DEL DEL key [key ...]刪除給定的一個或多個 key 。不存在的 key 會被忽略。
#  刪除單個 key
redis> SET name huangz
OK

redis> DEL name
(integer) 1

# 刪除一個不存在的 key
redis> EXISTS phone
(integer) 0

redis> DEL phone # 失敗,沒有 key 被刪除
(integer) 0

# 同時刪除多個 key
redis> SET name "redis"
OK

redis> SET type "key-value store"
OK

redis> SET website "redis.com"
OK

redis> DEL name type website
(integer) 3
  • EXISTS EXISTS key 檢查給定 key 是否存在。
redis> SET db "redis"
OK

redis> EXISTS db
(integer) 1

redis> DEL db
(integer) 1

redis> EXISTS db
(integer) 0
  • EXPIRE EXPIRE key seconds 爲給定 key 設置生存時間,當 key 過期時(生存時間爲 0 ),它會被自動刪除。
redis> SET cache_page "www.google.com"
OK

redis> EXPIRE cache_page 30  # 設置過期時間爲 30 秒
(integer) 1

redis> TTL cache_page    # 查看剩餘生存時間
(integer) 23

redis> EXPIRE cache_page 30000   # 更新過期時間
(integer) 1

redis> TTL cache_page
(integer) 29996
  • KEYS  KEYS pattern 查找所有符合給定模式 pattern 的 key 。

               KEYS * 匹配數據庫中所有 key 。

    KEYS h?llo 匹配 hello , hallo 和 hxllo 等。

    KEYS h*llo 匹配 hllo 和 heeeeello 等。

    KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo 。

    特殊符號用 \ 隔開

    KEYS 的速度非常快,但在一個大的數據庫中使用它仍然可能造成性能問題,如果你需要從一個數據集中查找特定的 key ,你最好還是用 Redis 的集合結構(set)來代替。

 

redis> MSET one 1 two 2 three 3 four 4  # 一次設置 4 個 key
OK

redis> KEYS *o*
1) "four"
2) "two"
3) "one"

redis> KEYS t??
1) "two"

redis> KEYS t[w]*
1) "two"

redis> KEYS *  # 匹配數據庫內所有 key
1) "four"
2) "three"
3) "two"
4) "one"
  • SORT 最簡單的 SORT 使用方法是 SORT key 和 SORT key DESC :

SORT key 返回鍵值從小到大排序的結果。

SORT key DESC 返回鍵值從大到小排序的結果。

假設 today_cost 列表保存了今日的開銷金額, 那麼可以用 SORT 命令對它進行排序:

# 開銷金額列表
redis> LPUSH today_cost 30 1.5 10 8
(integer) 4

# 排序
redis> SORT today_cost
1) "1.5"
2) "8"
3) "10"
4) "30"

# 逆序排序
redis 127.0.0.1:6379> SORT today_cost DESC
1) "30"
2) "10"
3) "8"
4) "1.5"
  • TTTTL key 以秒爲單位,返回給定 key 的剩餘生存時間(TTL, time to live)。
# key 存在,但沒有設置剩餘生存時間
redis> SET key value
OK

redis> TTL key
(integer) -1

# 有剩餘生存時間的 key
redis> EXPIRE key 10086
(integer) 1

redis> TTL key
(integer) 10084

三 Redis常見問題

  3.1  什麼是Redis持久化?Redis有哪幾種持久化方式?優缺點是什麼?

Redis持久化就是將內存中的數據以文件的方式寫入到磁盤中去。Redis有兩種持久化方式:RDB(默認) AOF

RDB:RDB持久化是指在指定的時間間隔內將內存中的數據集快照寫入磁盤。

RDB是 Redis DataBase的縮寫,功能核心函數有兩個:

  1. rdbSave:將內存中的數據以RDB文件的方式寫入到磁盤中。
  2. rdbLoad:將磁盤中的RDB文件加載到內存中。

另一點需要注意的是,每次快照持久化都是將內存數據完整寫入到磁盤一次,並不 是增量的只同步髒數據。如果數據量大的話,而且寫操作比較多,必然會引起大量的磁盤io操作,可能會嚴重影響性能。

AOF

AOF是Append-only file的縮寫

每當執行服務器(定時)任務或者函數時flushAppendOnlyFile 函數都會被調用, 這個函數執行以下兩個工作

aof 寫入保存:

  1. WRITE:根據條件,將 aof_buf 中的緩存寫入到 AOF 文件
  2. SAVE:根據條件,調用 fsync 或 fdatasync 函數,將 AOF 文件保存到磁盤中。

存儲結構:

  內容是redis通訊協議(RESP )格式的命令文本存儲。

比較

aof文件、更新頻率高,比rdb更安全也更大、但性能沒有rdb好,如果兩個都配了優先加載AOF

什麼是RESP?有什麼特點?

RESP 是redis客戶端和服務端之前使用的一種通訊協議;

RESP 的特點:實現簡單、快速解析、可讀性好

3.2 Redis有哪些架構模式?各自的特點又是什麼?

特點:比較簡單,缺點:內存有限,處理能力有限,無法高可用

Redis 的複製(replication)功能允許用戶根據一個 Redis 服務器來創建任意多個該服務器的複製品,其中被複制的服務器爲主服務器(master),而通過複製創建出來的服務器複製品則爲從服務器(slave)。 只要主從服務器之間的網絡連接正常,主從服務器兩者會具有相同的數據,主服務器就會一直將發生在自己身上的數據更新同步 給從服務器,從而一直保證主從服務器的數據相同。

特點:有主從角色,主和從數據一樣,降低了master的讀取壓力

問題:寫操作是在master上進行的,由master同步給slave,master寫壓力較大

Redis sentinel (發音:sentɪnl-森忒no)是一個分佈式系統中監控 redis 主從服務器,並在主服務器下線時自動進行故障轉移。其中三個特性:

  1. 監控(Monitoring):    Sentinel  會不斷地檢查你的主服務器和從服務器是否運作正常。
  2. 提醒(Notification): 當被監控的某個 Redis 服務器出現問題時, Sentinel 可以通過 API 向管理員或者其他應用程序發送通知。
  3. 自動故障遷移(Automatic failover): 當一個主服務器不能正常工作時, Sentinel 會開始一次自動故障遷移操作。

特點:監控各個節點 ,保證高可用,一旦有服務器出現問題,會自動故障遷移

缺點:主從模式,切換需要時間丟數據,沒有解決 master 寫的壓力

Twemproxy 是一個 Twitter 開源的一個 redis 和 memcache 快速/輕量級代理服務器; Twemproxy 是一個快速的單線程代理程序,支持 Memcached ASCII 協議和 redis 協議。

特點:1、多種 hash 算法:MD5、CRC16、CRC32、CRC32a、hsieh、murmur、Jenkins 

2、支持失敗節點自動刪除

3、後端 Sharding 分片邏輯對業務透明,業務方的讀寫方式和操作單個 Redis 一致

缺點:增加了新的 proxy,需要維護其高可用。

failover 邏輯需要自己實現,其本身不能支持故障的自動轉移可擴展性差,進行擴縮容都需要手動干預

從redis 3.0之後版本支持redis-cluster集羣,Redis-Cluster採用無中心結構,每個節點保存數據和整個集羣狀態,每個節點都和其他所有節點連接。

特點:

1、無中心架構(不存在哪個節點影響性能瓶頸),少了 proxy 層。

2、數據按照 slot 存儲分佈在多個節點,節點間數據共享,可動態調整數據分佈。

3、可擴展性,可線性擴展到 1000 個節點,節點可動態添加或刪除。

4、高可用性,部分節點不可用時,集羣仍可用。通過增加 Slave 做備份數據副本

5、實現故障自動 failover,節點之間通過 gossip 協議交換狀態信息,用投票機制完成 Slave到 Master 的角色提升。

缺點:

1、資源隔離性較差,容易出現相互影響的情況。

2、數據通過異步複製,不保證數據的強一致性

3.3 什麼是一致性哈希算法?什麼是哈希槽?

這兩個問題篇幅過長 網上找了兩個解鎖的不錯的文章

https://www.cnblogs.com/lpfuture/p/5796398.html

https://blog.csdn.net/z15732621582/article/details/79121213

3.4 使用過Redis分佈式鎖麼,它是怎麼實現的?

先拿setnx來爭搶鎖,搶到之後,再用expire給鎖加一個過期時間防止鎖忘記了釋放

如果在setnx之後執行expire之前進程意外crash或者要重啓維護了,那會怎麼樣?

set指令有非常複雜的參數,這個應該是可以同時把setnx和expire合成一條指令來用的!

set(key,value,"NX","EX",1000);

3.5 使用過Redis做異步隊列麼,你是怎麼用的?有什麼缺點?

一般使用list結構作爲隊列,rpush生產消息,lpop消費消息。當lpop沒有消息的時候,要適當sleep一會再重試。

缺點:在消費者下線的情況下,生產的消息會丟失,得使用專業的消息隊列如rabbitmq等。

能不能生產一次消費多次呢?

使用pub/sub主題訂閱者模式,可以實現1:N的消息隊列。

3.6 什麼是緩存穿透?如何避免?什麼是緩存雪崩?何如避免?

緩存穿透

一般的緩存系統,都是按照key去緩存查詢,如果不存在對應的value,就應該去後端系統查找(比如DB)。一些惡意的請求會故意查詢不存在的key,請求量很大,就會對後端系統造成很大的壓力。這就叫做緩存穿透。

如何避免?

 1:對一定不存在的key進行過濾。可以把所有的可能存在的key放到一個大的Bitmap中,查詢時通過該bitmap過濾。

 2:對查詢結果爲空的情況也進行緩存,緩存時間設置短一點,或者該key對應的數據insert了之後清理緩存。

緩存雪崩

當緩存服務器重啓或者大量緩存集中在某一個時間段失效,這樣在失效的時候,會給後端系統帶來很大壓力。導致系統崩潰。

如何避免?

1:在緩存失效後,控制讀數據庫和寫緩存的線程個數,比如某個key,只允許一個線程查詢數據和寫緩存,其他線程等待。

2:做二級緩存,A1爲原始緩存,A2爲拷貝緩存,A1失效時,可以訪問A2,A1緩存失效時間設置爲短期,A2設置爲長期

3:不同的key,設置不同的過期時間,讓緩存失效的時間點儘量均勻。

 

參考鏈接:https://www.cnblogs.com/jasontec/p/9699242.html

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