Redis簡單瞭解

1. 前言

Redis不是簡單的鍵值存儲,它實際上是一個數據結構服務器,支持不同類型的值。在傳統鍵值存儲中,鍵和值都是字符串,而在Redis中,值不僅限於簡單的字符串,還可以容納更復雜的數據結構。

2. Redis的數據類型

Redis中的鍵

Redis的鍵是二進制安全的,這意味着您可以使用任何二進制序列作爲鍵,從“ foo”之類的字符串到JPEG文件的內容。 空字符串也是有效的鍵。

需要注意的是:

  1. 儘量不要使用過長的鍵。例如,一個1024字節的鍵會佔用較大的內存,而且在比較鍵的過程中代價更高。更好的方法是,將該值hash之後再作爲鍵。
  2. 儘量不要使用過短的鍵。儘管較短的鍵佔用更少的內存,但是可讀性更差。例如: “user:1000:followers” 比 “u1000flw” 作爲鍵更合適。
  3. 鍵的模式最好保持一致。 例如,“ object-type:id”是一個好主意,例如“ user:1000”。 點或破折號通常用於多詞字段,例如“ comment: 1234:reply.to”或“ comment: 1234:reply-to”。
  4. 鍵的大小不能超過512 MB

2.1 String

Redis 中最簡單的數據類型就是 String 了,而它也是 Memcached 中唯一的數據類型。
由於Redis 中的鍵都是 String 類型的,當值也是 String 時,其實就是將一個 String 映射到另一個 String
值的最大值不能超過512MB
String類型是二進制安全的,表示redisstring 可以包含任何數據。如數字,字符串,jpg圖片或者序列化的對象。

# 如果原來的key已經存在了,那麼再使用set會給key設置一個新的值。
set 鍵 值
get 鍵
# 該鍵的值加1(原子性遞增),將原鍵的值解析爲一個整數,然後加1,將加1後的結果SET爲該鍵的新值
incr 鍵
# 在原來鍵的值得基礎上,追加數據
append 鍵 "append value"
# 獲取字符串的值得分片, [start, end]
getrange 鍵 start end
# 設置字符串的值的分片,從索引爲start開始 用新值開始替換
setrange 鍵 start 新值

INCR, DECR, INCRBY原子性操作,也就是說不會類似多線程的現象。例如,多線程中,A和B同時讓共享變量x(值爲10)加1,結果可能會是11,而不是12,而兩個 redis-cli 同時使用incr對某鍵加1,最終的結果一定是12。

1條命令 SETGET 多個鍵的值。

mset key1 value1 key2 value2 key3 value3

mget key1 key2 key3

# EXISTS返回1或者0作爲信號,表示該鍵是否存在
exists 鍵
# DEL刪除指定鍵和其值
del

mget的返回值是一個數組。

2.2 Lists

Redis 中的 lists 是通過雙端鏈表實現的。這意味着不管 list 中的元素有多少,在列頭和列尾添加一個新元素都能在常數時間,即O(1),內完成。lists 中的元素都是字符串。

1個 Redis list 的最大長度限制爲 232 - 1

之所以使用鏈表實現,就是因爲常常需要在較短時間內從很長很長的 list 中添加或刪除元素(頭、尾都行)。
還有一個優點是,Redis 中的 Lists 在常數時間,即O(1),內獲取固定長度的list

但是缺點是獲取元素比數組實現的 list 要慢,尤其是獲取中間的元素,這是一個O(N)操作。

# 添加一個新元素或多個元素到列頭
lpush list名 值
lpush list名 值123

# 添加一個新元素或多個元素到列尾
rpush list名 值
rpush list名 值123

# 從列表中獲取一定範圍的數據,[start, end]
lrange list名 start end
# 返回list中的所有元素
lrange list0 -1

# 彈出(獲取+刪除)list中的元素
# 從列頭刪除1個元素
lpop list# 從列尾刪除1個元素
rpop list

需要注意的是LRANGE命令中,[start, end]區間兩邊都是閉合的。
使用LPOP或者RPOP刪除list中元素,若list已經爲空,則返回值是NULL。

常見用例

  • 記錄用戶發佈到社交網絡上的最近更新。
  • 生產者-消費者模式。生產者生產消息並寫入 list 中,消費者消費消息。

舉個例子,假設你的微博主頁顯示了你發佈的一些最新照片,並且你希望主頁的訪問速度要儘可能的快。利用 Redis 中的 list 來實現:

  • 每次用戶發佈新照片時,我們都會使用LPUSH將其照片id添加到list中。
  • 當用戶訪問主頁時,我們使用LRANGE 0 9來獲取最新發布的10個照片。

Capped lists

在許多情況,我們只是想使用Redis中的list來存儲最新的數據,不管這些數據是社交網絡更新數據、日誌數據還是其它什麼數據。

只記錄最新的N個數據,使用LTRIM丟棄所有舊的數據。LTRIM命令指定一個範圍,這個範圍之外的元素都會被刪掉。

# 向某list中寫入5個元素
rpush list1 2 3 4 5
# 設置最新值的範圍是[0, 2],兩邊都是閉合的,所以有3個元素
ltrim list0 2
# 展示該list中的所有元素
lrange list0 -1

# Redis中的Sorted sets
在Redis中獲取集合中間某部分的元素是很常見的,這就需要使用Sorted sets。

list的阻塞操作

Redis中的list 實現隊列非常合適,這是因爲list有一個特性:阻塞操作,並且通常用作進程間通信系統的構建塊。

  • 生產者調用LPUSH命令推送一些數據到list中
  • 消費者調用RPOP從該list中抽取/處理這些數據

但是,有時list可能爲空(沒有任何要處理的數據),因此RPOP會返回NULL。 在這種情況下,消費者被迫等待一段時間,然後使用RPOP重試。這被稱爲輪詢,在這種情形有幾個缺點:

  • Redisclients會處理這些讀命令,但這並有什麼卵用,因爲壓根就沒有數據了。
  • 由於添加了數據處理的延遲,當1個消費者獲取到一個NULL後,會縮短延遲的時間,這樣會導致更多無用命令的調用。

Redis使用BRPOPBLPOP可以解決上述問題。BRPOPBLPOPRPOPLPOP的阻塞版本,當list是空的之後,會阻塞獲取數據。僅當將新元素添加到list中或達到用戶指定的超時時間了,它們纔會返回結果給調用方。

可以同時等待多個list

# 意思是等待該list中的元素,但如果5秒鐘後沒有可用元素,則返回
brpop list5
# 等待時間設置爲0s,表示永遠等待
brpop list0

-clients 按照順序享受服務。當該list中有可用元素之後,會優先服務第一個阻塞的客戶端。

  • 返回值與RPOP相比有所不同。BRPOP的返回值是一個數組,由鍵名和值,因爲BRPOPBLPOP能夠同時等待多個list,所以需要指明list名。
  • 如果超時了,則會返回NULL

自動創建和刪除鍵

我們不必去創建空list,只需要向該list中添加元素即可。我們也不用移除空list,這些都是Redis自己的工作。Streams, Sets, Sorted SetsHashes 也是這樣。

  1. 當我們添加元素到一個聚合數據類型StreamsSetsSorted SetsHashes),如果該鍵並不存在,則Redis會先建一個空的該聚合數據類型作爲鍵,然後將元素添加進去。
    在這裏插入圖片描述
  2. 當我們從一個聚合數據類型中刪除了最後一個元素之後,Redis會自動刪除該鍵。Stream數據類型是個例外。
    在這裏插入圖片描述
  3. 對一個空的 list 調用只讀命令(如LLEN,返回list的長度)或者寫命令移除元素時,效果如下。
    在這裏插入圖片描述

2.3 Hashes

Redis 哈希是字符串字段和字符串值之間的映射,1個 hash 常用來表示1個對象(可以有許多 field 和 對應的 value)下面的例子中,鍵是"user:1000",剩下是一對一對的字段和值。
在這裏插入圖片描述
每一個hash 可以存儲 232 -1 個 field/value 對。

# 爲某hash鍵設置字段的值
hmset 鍵 field1 value1 field2 value2
# 獲取該hash鍵的某一個字段的值
hget 鍵 field1
hget 鍵 field2
# 獲取該hash鍵的多個字段的值,返回的是一個數組
hmget 鍵 field1 field3 field2
# 獲取該hash鍵的所有字段和其值
hgetall 鍵
# 有些命令可以單獨在hash鍵的某字段上進行操作,如HINCRBY(理解爲h-incr-by)
hincrby 鍵 field1 10 # 讓該hash鍵的field1字段的值加10

需要注意的是,比較值比較小的hashes使用了特殊的編碼方式,使得其在內存中的存儲效率更高。

2.4 Sets

集合類型也是用來保存多個字符串的元素,可以理解成python中的set。具有以下特性:

  1. 沒有重複的元素
  2. 集合中的元素是無序的,不能通過索引下標獲取元素
  3. 支持集合間的操作,可以取多個集合取交集、並集、差集

Redis 中的 Sets 是字符串的無序集合。它支持集合間的操作(交集、並集、差集)和成員檢查等操作。

1個 Redis set 的最大長度限制爲 232 - 1

常見用例

  • 跟蹤指定博客的所有IP地址,會自動去重。
  • Redis sets 可以很好的表示某些關係。用 set 來表示每一個tag
  • 可以從 set 中獲取隨機元素。
# 向set中添加一個或多個新元素
sadd set名 value1 value2 value3
# 查看某set鍵中的所有元素
smembers set# 判斷某元素是否在該set中
sismember set名 value
# 求交集
sinter set1 set2 set3
# 求並集
sunion set1 set2 set3
sunionstore 新setset1 set2 # 將幾個set的並集存到一個新的set
# 求差集
# 彈出集合中隨機一個元素
spop set# 獲取集合中隨機一個元素但是不刪除
srandmember set# 集合的基數,即集合中元素數量
scard set

例如,打標籤。

# 表示給id爲1000的新聞文章打了標籤號爲1,2,5,77的標籤。
sadd news:1000:tags 1 2 5 77
# 也可以反過來,記錄該標籤標記了哪些新聞文章
sadd tag:1:news 1000
sadd tag:2:news 1000
sadd tag:5:news 1000
sadd tag:77news 1000

2.5 Sorted Sets

Sorted Sets 即有序的set。這個有序是通過給每個元素關聯一個名爲score的浮點數來實現的,score 是可以相同的,也就是score可以重複。

排序規則如下:

  • A和B是兩個不同score的元素,如果A.score > B.score,則A > B
  • 如果A和B的score相同,比較A和B的字符串大小,若A的字符串大於B,則A > B

Sorted set的特點可以簡單總結爲:添加、刪除、更新快,元素有序,成員檢查快,獲取中間元素快。

更新分數只需要zadd爲該元素設置新的score即可,但是在更新score之後,Sorted set需要O(log(N)的時間複雜度進行重新排序。因此,當有大量更新時,使用sorted set是一個合適的選擇。

# 給某Sorted set添加一個或多個元素
zadd zset名 score1 value1 score2 value2 score3 value3
# 查看某Sorted set中的所有元素,分數從小到大
zrange zset名 0 -1
#  查看某Sorted set中的所有元素,分數從大到小
zrevrange zset名 0 -1
# 使用withscores返回元素的同時也返回其分數
zrange zset名 0 -1 withscores

############# 範圍操作 #############################

# 返回score在[負無窮,100] 左右區間都包括,這個範圍的元素
zrangebyscore zset名 -inf 100
zrangebyscore zset名 100 +inf # [100, +inf]

# 移除某範圍[start, end] 左右區間都包括,的元素,並返回
zremrangebyscore zset名 start end

# 獲得值爲value的元素在該sorted set中的排名
zrank zset名 value
zrevrank zset名 value # 逆序的排名

############# 按字典順序(字符串大小順序)獲取範圍 #############################
# 當分數都相同時,按照字符串的大小順序有序。

zrangebylex zset名 min max
zrevrangebylex zset名 min max
zremrangebylex zset名 min max
zlexcount zset名 min max

常見用例

  • 大型在線遊戲的排行榜。使用ZADD可以很容易地更新用戶的score,還可以用ZRANGE獲取排名前幾的用戶,用ZRANK獲取某用戶的排名。
  • 索引數據。比如,我有很多代表用戶的hash,那麼可以將用戶的ID作爲Sorted set 的值,用戶的年齡作爲 Sorted setscore 。然後使用 ZRANGEBYSCORE 就可以快速獲取指定年齡範圍的用戶了。

2.6 Bitmaps

位圖。並不是一種實際的數據類型,而是在String類型上定義的一組面向位的操作。

位操作分爲兩類:常數時間的單個位操作(如將一個位設置爲1或0或獲取其值),以及對一組位的操作,例如,計算給定位範圍內位爲1或者0的數量 (例如,人口計數)。

位圖的最大優點之一是,它們在存儲信息時通常可以節省大量空間。 例如,在以增量用戶ID表示不同用戶的系統中,僅使用512 MB內存就可以記住40億用戶的一位信息(例如,知道用戶是否要接收新聞通訊)。

如果設置或者獲取的位數超過了當前String的長度,那麼Redis會自動擴大該String的長度。

########### 單個位上的操作 #######################
setbit 某key 第幾位 值 # 例如: setbit mykey 10 1. 意思第10位設爲1
# 這是該key第10位的值爲1
setbit 某key 10 1
# 獲取第10位的值
getbit 某key 10
# 因爲第11位沒有設置,所以是0
getbit 某key 11

########### 多個位上的操作 #######################

# 在不同String上,實現按位運算,如AND OR XOR NOT操作
# 對一個或多個保存二進制位的字符串key進行位元操作,並將結果保存到destkey上。
bitop 操作 destkey key1 key2 key3
# 統計爲1的位的數量
bitcount 某key
# 查找第一爲0或者爲1的位
bitpos 某key 0或者1

2.7 HyperLogLogs

HLL是一種概率數據結構,旨在對唯一事物進行計數。

RedisHLL被編碼爲Redis字符串,所以我們可以用GET命令來序列化HLL,可以用SET命令來反序列化成HLL

從概念上講,HLL API就像使用Set來執行相同的任務。 你可以將元素添加到集合中,並使用SCARD檢查集合中的元素數量,這是唯一的,因爲SADD不會重新添加現有元素。

# 將新元素添加到該hll鍵中
pfadd hll鍵 value1 value2 value3
# 該hll鍵的值的數量
pfcount hll鍵

常見用例

  • 統計每天用戶在搜索表單中執行的唯一查詢。

2.8 Streams

StreamRedis5.0之後引入的新數據類型。提供抽象日誌數據的append-only 數據結構。從概念上講,因爲Redis是流式傳輸在內存中表示的抽象數據類型,所以它們實現了更強大的操作,以克服日誌文件本身的限制。

consumer group: 允許一組客戶端合作使用同一消息流的不同部分。

3. Redis過期

Redis中的鍵都有存活時間。這與Redis中的數據結構無關。我們可以爲鍵設置一個過期時間,當超過這個過期時間會自動刪除這個鍵和其值,就像使用DEL刪除了這個鍵一樣。

默認永不過期

  • 過期時間的精度可以是s或者ms。
  • 只要超過過期時間1ms則認爲過期。
  • 過期信息是複製並持久化在磁盤中的

設置過期時間,過期時間的默認單位是S

# 設置過期時間
set 鍵 值
expire 鍵 過期時間的s值
pexpire 鍵 過期時間的ms值
或者使用SET命令的ex選項
set 鍵 值 ex 過期時間的s值

# 查看某鍵還有多少時間過期
ttl 鍵 # s
pttl 鍵 # ms

# 讓某鍵用不過期
persist 鍵

參考文獻

[1] Redis 官方文檔

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