主頁 > 後端開發 > Redis常用資料結構及應用場景

Redis常用資料結構及應用場景

2021-12-28 06:12:52 後端開發

1. 概述

Redis 一個開源的基于鍵值對(Key-Value)NoSQL 資料庫,使用 ANSIC 語言撰寫、支持網路、基于記憶體但支持持久化,性能優秀,并提供多種語言的 API,

我們要首先理解一點,我們把 Redis 稱為 KV 資料庫,鍵值對資料庫,那就可以把 Redis 內部的存盤視為存在著一個巨大的 Map,對 Map 的操作無非就是get 和 put,然后通過 key 操作這個 key 所對應的 value,而這個 value 的型別可以多種多樣,也就是 Redis 為我們提供的那些資料結構,比如字串(String)、哈希(Hash)等等,

Redis 會將所有資料都存放在記憶體中,所以它的讀寫性能非常驚人,不僅如此,Redis 還可以將記憶體的資料利用快照和日志的形式保存到硬碟上,這樣在發生類似斷電或者機器故障的時候,記憶體中的資料不會丟失,

除了上述功能以外,Redis 還提供了鍵過期、發布訂閱、事務、流水線、Lua 腳本等附加功能,

1.1 應用場景

快取

快取機制幾乎在所有的大型網站都有使用,合理地使用快取不僅可以加快資料的訪問速度,而且能夠有效地降低后端資料源的壓力,Redis 提供了鍵值過期時間設定,并且也提供了靈活控制最大記憶體和記憶體溢位后的淘汰策略,可以這么說,一個合理的快取設計能夠為一個網站的穩定保駕護航,

排行榜系統

排行榜系統幾乎存在于所有的網站,例如按照熱度排名的排行榜,按照發布時間的排行榜,按照各種復雜維度計算出的排行榜,Redis 提供了串列和有序集合資料結構,合理地使用這些資料結構可以很方便地構建各種排行榜系統,

計數器應用

計數器在網站中的作用至關重要,例如視頻網站有播放數、電商網站有瀏覽數,為了保證資料的實時性,每一次播放和瀏覽都要做+1 的操作,如果并發量很大對于傳統關系型資料的性能是一種挑戰,Redis 天然支持計數功能而且計數的性能也非常好,可以說是計數器系統的重要選擇,

社交網路

贊/踩、粉絲、共同好友/喜好、推送、下拉重繪等是社交網站的必備功能,由于社交網站訪問量通常比較大,而且傳統的關系型資料不太適合保存這種型別的資料,Redis 提供的資料結構可以相對比較容易地實作這些功能,

訊息佇列系統

訊息佇列系統可以說是一個大型網站的必備基礎組件,因為其具有業務解耦、 非實時業務削峰等特性,Redis 提供了發布訂閱功能和阻塞佇列的功能,雖然和專業的訊息佇列比還不夠足夠強大,但是對于一般的訊息佇列功能基本可以滿足,(我上家公司就用過 Redis 做訊息佇列,雖然后面換了其他 MQ),

1.2 特性

速度快

正常情況下,Redis 執行命令的速度非常快,官方給出的數字是讀寫性能可以達到 10 萬/秒,

基于鍵值對的資料結構服務器

幾乎所有的編程語言都提供了類似字典的功能,例如 Java 里的 map,類似于這種組織資料的方式叫作基于鍵值的方式,與很多鍵值對資料庫不同的是,Redis 中的值不僅可以是字串,而且還可以是具體的資料結構,這樣不僅能便于在許多應用場景的開發,同時也能夠提高開發效率,

Redis 的全稱是 Remote Dictionary Server,它主要提供了 5 種資料結構:字串、哈希、串列、集合、有序集合,同時在字串的基礎之上演變出了位圖(Bitmaps)和 HyperLogLog 兩種資料結構,并且隨著 LBS (Location BasedService,基于位置服務)的不斷發展,Redis 中加入有關 GEO(地理資訊定位)的功能,

豐富的功能

除了 5 種資料結構,Redis 還提供了許多額外的功能:提供了鍵過期功能,可以用來實作快取,

提供了發布訂閱功能,可以用來實作訊息系統,支持 Lua 腳本功能,可以利用 Lua 創造出新的 Redis 命令,提供了簡單的事務功能,能在一定程度上保證事務特性,提供了流水線(Pipeline)功能,這樣客戶端能將一批命令一次性傳到 Redis,減少了網路的開銷,

簡單穩定

Redis 的簡單主要表現在三個方面,

首先,Redis 的原始碼很少,早期版本的代碼只有 2 萬行左右,3.0 版本以后由于添加了集群特性,代碼增至 5 萬行左右,

其次,Redis 使用單執行緒模型,這樣不僅使得 Redis 服務端處理模型變得簡單,而且也使得客戶端開發變得簡單,

最后,Redis 不需要依賴于作業系統中的類別庫,

Redis 雖然很簡單,但是不代表它不穩定,實際的運行中很少出現因為 Redis 自身 bug 而宕掉的情況,

客戶端語言多

Redis 提供了簡單的 TCP 通信協議,很多編程語言可以很方便地接人到 Redis,

持久化

通常看,將資料放在記憶體中是不安全的,一旦發生斷電或者機器故障,重要的資料可能就會丟失,因此Redis提供了兩種持久化方式:RDB 和 AOF,即可以用兩種策略將記憶體的資料保存到硬碟中,這樣就保證了資料的可持久性,

主從復制

Redis 提供了復制功能,實作了多個相同資料的 Redis 副本,復制功能是分布式Redis 的基礎,

高可用和分布式

Redis Sentinel,它能夠保證 Redis 節點的故障發現和故障自動轉移,Redis 從 3.0 版本正式提供了分布式實作 Redis Cluster,它是 Redis 真正的分布式實作,提供了高可用、讀寫和容量的擴展性,

2. 下載安裝

目前演示的安裝操作是基于 Centos7 下講解,

官方下載地址:https://redis.io/download

# 先新建一個目錄
mkdir /usr/local/redis
cd /usr/local/redis
# 下載
wget https://download.redis.io/releases/redis-6.2.6.tar.gz
# 解壓
tar -xzvf redis-6.2.6.tar.gz
# 編譯
cd redis-6.2.6
make

2.1 啟動

Redis 有三種方法啟動 Redis:默認配置、帶引數啟動、組態檔啟動,

1?? 默認配置

進入安裝好的 Redis 的 src目錄下執行以下命令:

./redis-server 

image-20211215203151805

可以看到直接使用 redis-server 啟動 Redis 后,會列印出一些日志,通過日志 可以看到一些資訊:

當前的 Redis 版本的是 64 位的 6.2.6,默認埠是 6379,Redis 建議使用組態檔來啟動,所以這種方式是不會在生產環境中使用的,

2?? 引數啟動

redis-server 加上要修改配置名和值(可以是多對),沒有設定的配置將使用默認配置,例如:如果要用 6380 作為埠啟動 Redis,那么可以執行:

./redis-server --port 6380 

image-20211215203128782

不過這種方式一般也用得比較少,

3?? 組態檔啟動

將配置寫到指定檔案里,并啟動,主要修改的是安裝目錄下的 redis.conf檔案,

./redis-server ../redis.conf

image-20211215203701640

2.2 操作

Redis 服務啟動完成后,就可以使用 redis-cli 連接和操作 Redis 服務,

image-20211215203942143

2.3 停止

Redis 提供了 shutdown 命令來停止 Redis 服務,例如我們目前已經啟動的 Redis 服務,可以執行:

./redis-cli -p 6379 shutdown 

Redis 服務端將會顯示:

2853:M 15 Dec 2021 20:41:26.593 # User requested shutdown...
2853:M 15 Dec 2021 20:41:26.593 * Saving the final RDB snapshot before exiting.
2853:M 15 Dec 2021 20:41:26.594 * DB saved on disk
2853:M 15 Dec 2021 20:41:26.594 * Removing the pid file.
2853:M 15 Dec 2021 20:41:26.594 # Redis is now ready to exit, bye bye...

除了可以通過 shutdown 命令關閉 Redis 服務以外,還可以通過 kill 行程號的方式關閉掉 Redis,但是強烈不建議使用 kill -9 強制殺死 Redis 服務,不但不會做持久化操作,還會造成緩沖區等資源不能被優雅關閉,極端情況會造成 AOF 和復制丟失資料的情況,

shutdown 還有一個引數,代表是否在關閉 Redis 前,生成持久化檔案:

./redis-cli -p 6379 shutdown nosave/save

默認是 save,生成持久化檔案,如果是 nosave 則不生成持久化檔案,

3. 全域命令

在了解 Redis 的資料結構之前,先了解 Redis 的一些全域命令,

命令 說明
keys * 查看所有鍵,同時也支持通配符,如 keys n*
dbsize 回傳當前資料庫中鍵的總數
exists 檢查鍵是否存在,存在回傳 1,不存在回傳 0,如 exists name
del 洗掉鍵,無論值是什么資料結構型別,del 命令都可以將其洗掉,回傳洗掉鍵個數,洗掉不存在鍵回傳 0,同時 del 命令可以支持洗掉多個鍵,如 del name age
expire Redis 支持對鍵添加過期時間,當超過過期時間后,會自動洗掉鍵,時間單位秒,如 expire name 10
ttl ttl 命令會回傳鍵的剩余過期時間,若回傳 -1 則表示鍵沒設定過期時間,-2 鍵不存在
type 回傳鍵的資料結構型別
randomkey 隨機回傳一個鍵
rename 鍵重命名,為了防止被強行 rename,Redis 提供了 renamenx 命令,確保只有 newKey 不存在時候才被覆寫,由于重命名鍵期間會執行 del 命令洗掉舊的鍵,如果鍵對應的值比較大,會存在阻塞 Redis 的可能性

注:

  1. dbsize 命令在計算鍵總數時不會遍歷所有鍵,而是直接獲取 Redis 內置的鍵總數變數,所以 dbsize 命令的時間復雜度是 O(1),而 keys 命令會遍歷所有鍵,所以它的時間復雜度是 o(n),當 Redis 保存了大量鍵時線上環境禁止使用 keys 命令;
  2. 除了 expire、ttl 命令以外,Redis 還提供了 expireat、pexpire,pexpireat、pttl、persist 等一系列命令,可自行查驗,

4. 基本資料結構

Redis 提供了一些資料結構供我們往 Redis 中存取資料,最常用的的有 5 種,字串(String)、哈希(Hash)、串列(list)、集合(set)、有序集合(ZSET),

4.1 String

字串型別是 Redis 最基礎的資料結構,首先鍵都是字串型別,而且其他幾種資料結構都是在字串型別基礎上構建的,所以字串型別能為其他四種資料結構的學習奠定基礎,字串型別的值實際可以是字串(簡單的字串、復 雜的字串(例如 JSON、XML))、數字(整數、浮點數),甚至是二進制(圖片、音頻、視頻),但是值最大不能超過 512 MB,

1?? 常用命令

設定值 set

set key value [ex seconds] [px milliseconds] [nxlxx]
  • ex seconds:為鍵設定秒級過期時間,
  • px milliseconds:為鍵設定毫秒級過期時間,
  • nx:鍵必須不存在,才可以設定成功,用于添加,
  • xx:與 nx 相反,鍵必須存在,才可以設定成功,用于更新,

其中,ex 引數和 expire 命令基本一樣,還有一個需要特別注意的地方是如果一個字串已經設定了過期時間,然后你呼叫了 set 方法修改了它,它的過期時間會消失,

除了 set 選項,Redis 還提供了 setex 和 setnx 兩個命令:

  • setex key seconds value
  • setnx key value

setex 和 setnx 的作用和 ex 和 nx 選項是一樣的,也就是,setex 為鍵設定秒級過期時間,setnx 設定時鍵必須不存在,才可以設定成功,

有什么應用場景嗎?

以 setnx 命令為例子,由于 Redis 的單執行緒命令處理機制,如果有多個客戶端同時執行 setnx key value,根據 setnx 的特性只有一個客戶端能設定成功,setnx 可以作為分布式鎖的一種實作方案,

獲取值 get

get key

如果要獲取的鍵不存在,則回傳 nil

另外,除了單個設定和獲取鍵值,Redis 還支持批量操作,

批量設定值 mset

mset name ayue age 20 sex 男

批量獲取值 mget

mget name age sex

image-20211216163431238

如果有些鍵不存在,那么它的值為 nil,結果是按照傳入鍵的順序回傳,

批量操作命令可以有效提高效率,假如沒有 mget 這樣的命令,要執行 n 次 get 命令具體耗時如下:

n 次 get 時間 = n 次網路時間 + n 次命令時間

使用 mget 命令后,要執行 n 次 get 命令操作具體耗時如下:

n 次 get 時間 = 1 次網路時間 + n 次命令時間

Redis 可以支撐每秒數萬的讀寫操作,但是這指的是 Redis 服務端的處理能力,對于客戶端來說,一次命令除了命令時間還是有網路時間,假設網路時間為 1 毫秒,命令時間為 0.1 毫秒(按照每秒處理 1 萬條命令算),那么執行 1000 次 get 命令需要 1.1 秒(1000*1+1000*0.1=1100ms),1 次 mget 命令的需要 0.101 秒 (1*1+1000*0.1=101ms),

數字運算 incr

incr 命令用于對值做自增操作,回傳結果分為三種情況:

  1. 值不是整數,回傳錯誤;
  2. 值是整數,回傳自增后的結果;
  3. 鍵不存在,按照值為 0 自增,回傳結果為 1,
incr key

image-20211216170800153

除了 incr 命令,Redis 提供了 decr(自減)、 incrby(自增指定數字)、decrby(自減指定數字)、incrbyfloat(自增浮點數),

追加指令 append

append 可以向字串尾部追加值,

append key value

image-20211216171635960

strlen

回傳字串長度,

strlen key

截取字串 getrange

getrange 截取字串中的一部分,形成一個子串,需要指明開始和結束的偏移量,截取的范圍是個閉區間,

image-20211216171944312

命令 說明 時間復雜度
get key 獲取值 O(1)
del key [key ...] 洗掉key O(N)(N是鍵的個數)
mset key [key value ...] 批量設定值 O(N)(N是鍵的個數)
mget key [key ...] 批量獲取值 O(N)(N是鍵的個數)
incr key 將 key 中儲存的數字值增一 O(1)
decr key 將 key 中儲存的數字值減一 O(1)
incrby key increment 將 key 所儲存的值加上給定的增量值(increment) O(1)
decrby key increment key 所儲存的值減去給定的減量值(decrement) O(1)
incrbyfloat key increment 將 key 所儲存的值加上給定的浮點增量值(increment) O(1)
append key value 如果 key 已經存在并且是一個字串, APPEND 命令將指定的 value 追加到該 key 原來值(value)的末尾 O(1)
strlen key 回傳 key 所儲存的字串值的長度, O(1)
setrange key offset value 用 value 引數覆寫給定 key 所儲存的字串值,從偏移量 offset 開始 O(1)
getrange key start end 回傳 key 中字串值的子字符 O(N)(N是字串的長度)

2?? 命令的時間復雜度

字串這些命令中,除了 del 、mset、 mget 支持多個鍵的批量操作,時間復雜度和鍵的個數相關,為 O(n),getrange 和字串長度相關,也是 O(n),其余的命令基本上都是 O(1)的時間復雜度,在速度上是非常快的,

3?? 使用場景

字串型別的使用場景很廣泛,如下:

1、快取功能

Redis 作為快取層,MySQL 作為存盤層,絕大部分請求的資料都是從 Redis 中獲取,由于 Redis 具有支撐高并發的特性,所以快取通常能起到加速讀寫和降低 后端壓力的作用,

2、計數

使用 Redis 作為計數的基礎工具,它可以實作快速計數、查詢快取的功能,同時資料可以異步落地到其他資料源,

3、共享 Session

一個分布式 Web 服務將用戶的 Session 資訊(例如用戶登錄資訊)保存在各 自服務器中,這樣會造成一個問題,出于負載均衡的考慮,分布式服務會將用戶的訪問均衡到不同服務器上,用戶重繪一次訪問可能會發現需要重新登錄,這個問題是用戶無法容忍的,

為了解決這個問題, 可以使用 Redis 將用戶的 Session 進行集中管理,在這種模式下只要保證 Redis 是高可用和擴展性的,每次用戶更新或者查詢登錄資訊都直接從 Redis 中集中獲取,

4、限時

很多應用出于安全的考慮,會在每次進行登錄時,讓用戶輸入手機驗證碼,從而確定是否是用戶本人,但是為了短信介面不被頻繁訪問,會限制用戶每分鐘獲取驗證碼的頻率,例如一分鐘不能超過 5 次,一些網站限制一個 IP 地址不能在一秒鐘之內訪問超過 n 次,或者同一 IP 在短時間內多次瀏覽謀篇文章瀏覽次數不會一直增加,點贊次數在短時間內不能重復點贊,

4.2 Hash

Redis hash 是一個 string 型別的 field(欄位) 和 value(值) 的映射表,hash 特別適合用于存盤物件,

Redis 中每個 hash 可以存盤 232 - 1 鍵值對(40多億),

1?? 常用命令

基本上,哈希的操作命令和字串的操作命令很類似,很多命令在字串型別的命令前面加上了 h 字母,代表是操作哈希型別,同時還要指明要操作的 field 的值,

hset

hset key field value

如果設定成功會回傳 1,反之會回傳 0,此外 Redis 提供了 hsetnx 命令,它們的關系就像 set 和 setnx 命令一樣,只不過作用域由鍵變為 field,

127.0.0.1:6379> hset hash:test name ayue
(integer) 1
127.0.0.1:6379> 

hget

hget key field

獲取值

127.0.0.1:6379> hget hash:test name
"ayue"
127.0.0.1:6379> 

其他命令:

命令 說明 時間復雜度
HDEL key field [field] 洗掉一個或多個Hash的field O(N) N是被洗掉的欄位數量
HEXISTS key field 判斷field是否存在于Hash中 O(1)
HGET key field 獲取Hash中field的值 O(1)
HGETALL key 從Hash中讀取全部的域和值 O(N) N是Hash的長度
HINCRBY key field increment 將Hash中指定域的值增加給定的數字 O(1)
HINCRBYFLOAT key field increment 將Hash中指定域的值增加給定的浮點數 O(1)
HKEYS key 獲取Hash的所有欄位 O(N) N是Hash的長度
HLEN key 獲取Hash里所有欄位的數量 O(1)
HMGET key field field 獲取Hash里面指定欄位的值 O(N) N是請求的欄位數
HMSET key field value [field value ...] 批量設定Hash欄位值 O(N) N是設定的欄位數
HSET key field value 設定Hash里面一個欄位的值 O(1)
HSETNX key field value 設定Hash的一個欄位,只有當這個欄位不存在時有效 O(1)
HSTRLEN key field 獲取Hash里面指定field的長度 O(1)
HVALS key 獲得 Hash 的所有值 O(N) N是Hash的長度
HSCAN key cursor [MATCH pattern] [COUNT count] 迭代 Hash 里面的元素

2?? 命令的時間復雜度

哈希型別的操作命令中,hdel,hmget,hmset 的時間復雜度和命令所帶的 field 的個數相關 O(k),hkeys,hgetall,hvals 和存盤的 field 的總數相關,O(N),其余的命令時間復雜度都是 O(1),

3?? 使用場景

1、存盤物件

Redis哈希物件常常用來快取一些物件資訊,如用戶資訊、商品資訊、配置資訊等,

我們以用戶資訊為例,它在關系型資料庫中的結構是這樣的:

id name age
1 Tom 15
2 Jerry 13

而使用Redis Hash存盤其結構如下圖:

image-20211220192747612

hmset user:1 name Tom age 15
hmset user:2 name Jerry age 13

相比較于使用Redis字串存盤,其有以下幾個優缺點:

  1. 原生字串每個屬性一個鍵,

    set user:1:name Tom
    set user:1:age 15
    

    優點:簡單直觀,每個屬性都支持更新操作,
    缺點:占用過多的鍵,記憶體占用量較大,同時用戶資訊內聚性比較差,所以此種方案一般不會在生產環境使用,

  2. 序列化字串后,將用戶資訊序列化后用一個鍵保存

    set user:1 serialize(userInfo)
    

    優點:簡化編程,如果合理的使用序列化可以提高記憶體的使用效率,
    缺點:序列化和反序列化有一定的開銷,同時每次更新屬性都需要把全部資料取出進行反序列化,更新后再序列化到Redis中,

  3. 序列化字串后,將用戶資訊序列化后用一個鍵保存

    hmset user:1 name Tom age 15 
    

    優點:簡單直觀,如果使用合理可以減少記憶體空間的使用,
    缺點:要控制哈希在ziplist和hashtable兩種內部編碼的轉換,hashtable會消耗更多記憶體,

2、購物車

購物車主要功能是臨時存放欲購買的商品,然后在結算或下訂單時,把購物里面的資料全部移除,其資料結構主要包含的欄位有:用戶ID、商品ID、商品數量等等,通常我們需要實作以下幾個功能:

  1. 全選功能,獲取所有該用戶的所有購物車商品;
  2. 商品數量,購物車圖示上要顯示購物車里商品的總數;
  3. 洗掉,要能移除購物車里某個商品;
  4. 增加或減少某個商品的數量,

image-20211221154409694

在之前很多電商網站通過 cookie 實作購物車功能,也就是將整個購物車都存盤到 cookie里面,

  • 優點:無須對資料庫進行寫入就可以實作購物車功能,這種方式大大提高了購物車的性能,
  • 缺點:程式需要重新決議和驗證( validate) cookie,確保 cookie 的格式正確,并且包含的商品都是真正可購買的商品,另外,因為瀏覽器每次發送請求都會連 cookie 一起發送,所以如果購物車 cookie 的體積比較大,那么請求發送和處理的速度可能會有所降低,

而通過 Redis 定義購物車非常簡單:當前登錄用戶 ID 號做為key,商品 ID 號為 field,加入購物車數量為 value,如下:

hmset cart:001 prod:01  1 prod:02  1
          |        |    |
          |        |    |
          |        |    |
         key    field value

而對于上述功能,可以通過 Hash 的相關命令來操作,

4.3 List

串列( list)型別是用來存盤多個有序的字串,a、b、c、d、e 五個元素從左到右組成了一個有序的串列,串列中的每個字串稱為元素(element),一個串列最多可以存盤 2-1 個元素,

在 Redis 中,可以對串列兩端插入( push)和彈出(pop),還可以獲取指定范圍的元素串列、獲取指定索引下標的元素等,

串列是一種比較靈活的資料結構,它可以充當堆疊和佇列的角色,在實際開發上有很多應用場景,

image-20211221105439714

串列型別有兩個特點:

  1. 串列中的元素是有序的,這就意味著可以通過索引下標獲取某個元素或者某個范圍內的元素串列,
  2. 串列中的元素可以是重復的,

1?? 常用命令

Redis串列物件常用命令如下表(點擊命令可查看命令詳細說明):

命令 說明 時間復雜度
BLPOP key [key ...] timeout 洗掉,并獲得該串列中的第一元素,或阻塞,直到有一個可用 O(1)
BRPOP key [key ...] timeout 洗掉,并獲得該串列中的最后一個元素,或阻塞,直到有一個可用 O(1)
BRPOPLPUSH source destination timeout 彈出一個串列的值,將它推到另一個串列,并回傳它;或阻塞,直到有一個可用 O(1)
LINDEX key index 獲取一個元素,通過其索引串列 O(N)
LINSERT key BEFORE AFTER pivot value在串列中的另一個元素之前或之后插入一個元素 O(N)
LLEN key 獲得佇列(List)的長度 O(1)
LPOP key 從佇列的左邊出隊一個元素 O(1)
LPUSH key value [value ...] 從佇列的左邊入隊一個或多個元素 O(1)
LPUSHX key value 當佇列存在時,從隊到左邊入隊一個元素 O(1)
LRANGE key start stop 從串列中獲取指定回傳的元素 O(S+N)
LREM key count value 從串列中洗掉元素 O(N)
LSET key index value 設定佇列里面一個元素的值 O(N)
LTRIM key start stop 修剪到指定范圍內的清單 O(N)
RPOP key 從佇列的右邊出隊一個元 O(1)
RPOPLPUSH source destination 洗掉串列中的最后一個元素,將其追加到另一個串列 O(1)
RPUSH key value [value ...] 從佇列的右邊入隊一個元素 O(1)
RPUSHX key value 從佇列的右邊入隊一個元素,僅佇列存在時有效 O(1)

2?? 命令的時間復雜度

串列型別的操作命令中,llen,lpop,rpop,blpop 和 brpop 命令時間復雜度都是 O(1),其余的命令的時間復雜度都是 O(n),只不過 n 的值根據命令不同而不同,比如 lset,lindex 時間復雜度和命令后的索引值大小相關,rpush 和 lpush 和插入元素的個數相關等等,

3?? 使用場景

1、訊息佇列

但使用 Redis 做訊息佇列存在很多問題,如訊息確認 ACK,訊息丟失等,所以一般來說還是用比較專業的 MQ 中間件,

2、文章串列

如下面這樣的文章串列,當用戶和文章都越來越多時,為了加快程式的回應速度,我們可以把用戶自己的文章存入到 List 中,因為 List 是有序的結構,所以這樣又可以完美的實作分頁功能,從而加速了程式的回應速度,

image-20211221154339179

上圖可表示為:

# 深圳衛健委發布一條訊息,訊息ID為 99
lpush mes:001 99
# 武漢本地寶發布一條訊息,訊息ID為 100
lpush mes:001 100
# 獲取訊息串列‘
lrange mes:001 0 5

4.4 Set

集合( set)型別也是用來保存多個的字串元素,但和串列型別不一樣的是,集合中不允許有重復元素,并且集合中的元素是無序的,不能通過索引下標獲取元素,

一個集合最多可以存盤 232 - 1 個元素,Redis 除了支持集合內的增刪改查,同時還支持多個集合取交集、并集、差集,合理地使用好集合型別,能在實際開發中解決很多實際問題,

1?? 常用命令

Redis Set 物件常用命令如下表(點擊命令可查看命令詳細說明):

命令 說明 時間復雜度
SADD key member [member ...] 添加一個或者多個元素到集合(set)里 O(N)
SCARD key 獲取集合里面的元素數量 O(1)
SDIFF key [key ...] 獲得佇列不存在的元素 O(N)
SDIFFSTORE destination key [key ...] 獲得佇列不存在的元素,并存盤在一個關鍵的結果集 O(N)
SINTER key [key ...] 獲得兩個集合的交集 O(N*M)
SINTERSTORE destination key [key ...] 獲得兩個集合的交集,并存盤在一個關鍵的結果集 O(N*M)
SISMEMBER key member 確定一個給定的值是一個集合的成員 O(1)
SMEMBERS key 獲取集合里面的所有元素 O(N)
SMOVE source destination member 移動集合里面的一個元素到另一個集合 O(1)
SPOP key [count] 洗掉并獲取一個集合里面的元素 O(1)
SRANDMEMBER key [count] 從集合里面隨機獲取一個元素
SREM key member [member ...] 從集合里洗掉一個或多個元素 O(N)
SUNION key [key ...] 添加多個set元素 O(N)
SUNIONSTORE destination key [key ...] 合并set元素,并將結果存入新的set里面 O(N)
[SSCAN key cursor MATCH pattern] [COUNT count] 迭代set里面的元素 O(1)

2?? 命令的時間復雜度

scard,sismember 時間復雜度為 O(1),其余的命令時間復雜度為 O(n),其中 sadd,srem 和命令后所帶的元素個數相關,spop,srandmember 和命令后所帶 count 值相關,交集運算 O(m*k),k 是多個集合中元素最少的個數,m 是鍵個數,并集、差集和所有集合的元素個數和相關,

3?? 使用場景

1、抽獎活動

常見的抽獎活動,比如基于 Redis 實作抽獎功能,

SPOP(隨機移除并回傳集合中一個或多個元素) 和 SRANDMEMBER(隨機回傳集合中一個或多個元素) 命令可以幫助我們實作一個抽獎系統,如果允許重復中獎,可以使用SRANDMEMBER 命令,

活動 ID 為 001,則

# Tom userID:01 參加活動
sadd action:001 01
# Jerry userID:02 參加活動
sadd action:001 02
# 開始抽獎1名中獎者
srandmember action:001 1 或 spop action:001 1
# 查看有多少用戶參加了本次抽獎
smembers action:001

2、點贊功能

比如設計一個微信點贊功能,

# 張三用戶ID 為userId:01
# 張三對訊息 ID008點贊啦
sadd zan:008 userId:01

# 張三取消了對訊息008的點贊
srem zan:008 userId:01

# 檢查用戶是否點過贊
sismember zan:008 userId:01

# 獲取訊息ID008所有的點贊用戶串列
smembers zan:008

# 訊息ID008的點贊數計算
scard zan:008

3、關系設計

如我們要設計一個微博的共同關注,或者可能認識的人,設計如下:

① A 關注的人

sadd A:cares B C D E

② B 關注的人

sadd B:cares A C D F

③ C 關注的人

sadd C:cares A F

按照以上條件:

④ A 和 B 共同關注的人

# D,C
sinter A:cares B:cares

⑤ 我關注的人也關注他

# A 關注的 B 也關注了 F,回傳 1 否則回傳 0
sismember B:cares F

⑥ 可能認識的人

# C 可能認識的人 C,D
sdiff B:cares C:cares

4、集合操作

setA={A,B,C}     setB={B, C}

① 集合與集合之間的交集

sinter setA setB-->得到集合{B,C}

② 集合與集合之間的并集

sunion setA setB -->得到集合{A,B,C}

③ 集合與集合之間的差集

sdiff  setA setB-->得到集合{A}
127.0.0.1:6379> SADD setA A B C
(integer) 3
127.0.0.1:6379> SADD setB B C
(integer) 2
127.0.0.1:6379> SINTER setA setB
1) "C"
2) "B"
127.0.0.1:6379> SUNION setA setB
1) "A"
2) "B"
3) "C"
127.0.0.1:6379> SDIFF setA setB
1) "A"
127.0.0.1:6379> 

4.5 ZSet

ZSet,有序集合,相對于哈希、串列、集合來說會有一點點陌生,但既然叫有序集合,那么它和集合必然有著聯系,它保留了集合不能有重復成員的特性,但不同的是,有序集合中的元素可以排序,但是它和串列使用索引下標作為排序依據不同的是,它給每個元素設定一個分數( score)作為排序的依據,

有序集合中的元素不能重復,但是 score 可以重復,就和一個班里的同學學號不能重復,但是考試成績可以相同,

有序集合提供了獲取指定分數和元素范圍查詢、計算成員排名等功能,合理的利用有序集合,能幫助我們在實際開發中解決很多問題,

1?? 常用命令

zadd

向有序集合 top:20211221 添加話題和點擊量,

zadd hot:20211220 10 薇婭逃稅

zadd 命令還有四個選項 nx、xx、ch、incr 四個選項:

  • nx,member 必須不存在,才可以設定成功,用于添加;
  • xx,member 必須存在,才可以設定成功,用于更新;
  • ch,回傳此次操作后,有序集合元素和分數發生變化的個數;
  • incr,對 score 做增加,相當于 zincrby ,

Redis串列物件常用命令如下表:

命令 說明 時間復雜度
BZPOPMAX key [key ...] timeout 從一個或多個排序集中洗掉并回傳得分最高的成員,或阻塞,直到其中一個可用為止 O(log(N))
BZPOPMIN key [key ...] timeout 從一個或多個排序集中洗掉并回傳得分最低的成員,或阻塞,直到其中一個可用為止 O(log(N))
[ZADD key NXXX] [CH] [INCR] score member [score member ...] 添加到有序set的一個或多個成員,或更新的分數,如果它已經存在 O(log(N))
ZCARD key 獲取一個排序的集合中的成員數量 O(1)
ZCOUNT key min max 回傳分數范圍內的成員數量 O(log(N))
ZINCRBY key increment member 增量的一名成員在排序設定的評分 O(log(N))
ZINTERSTORE 相交多個排序集,導致排序的設定存盤在一個新的關鍵 O(NK)+O(Mlog(M))
ZLEXCOUNT key min max 回傳成員之間的成員數量 O(log(N))
ZPOPMAX key [count] 洗掉并回傳排序集中得分最高的成員 O(log(N)*M)
ZPOPMIN key [count] 洗掉并回傳排序集中得分最低的成員 O(log(N)*M)
ZRANGE key start stop [WITHSCORES] 根據指定的index回傳,回傳sorted set的成員串列 O(log(N)+M)
ZRANGEBYLEX key min max [LIMIT offset count] 回傳指定成員區間內的成員,按字典正序排列, 分數必須相同, O(log(N)+M)
ZREVRANGEBYLEX key max min [LIMIT offset count] 回傳指定成員區間內的成員,按字典倒序排列,分數必須相同 O(log(N)+M)
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] 回傳有序集合中指定分數區間內的成員,分數由低到高排序, O(log(N)+M)
ZRANK key member 確定在排序集合成員的索引 O(log(N))
ZREM key member [member ...] 從排序的集合中洗掉一個或多個成員 O(M*log(N))
ZREMRANGEBYLEX key min max 洗掉名稱按字典由低到高排序成員之間所有成員, O(log(N)+M)
ZREMRANGEBYRANK key start stop 在排序設定的所有成員在給定的索引中洗掉 O(log(N)+M)
ZREMRANGEBYSCORE key min max 洗掉一個排序的設定在給定的分數所有成員 O(log(N)+M)
ZREVRANGE key start stop [WITHSCORES] 在排序的設定回傳的成員范圍,通過索引,下令從分數高到低 O(log(N)+M)
ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count] 回傳有序集合中指定分數區間內的成員,分數由高到低排序, O(log(N)+M)
ZREVRANK key member 確定指數在排序集的成員,下令從分數高到低 O(log(N))
ZSCORE key member 獲取成員在排序設定相關的比分 O(1)
ZUNIONSTORE 添加多個排序集和導致排序的設定存盤在一個新的鍵 O(N)+O(M log(M))
ZSCAN key cursor [MATCH pattern] [COUNT count] 迭代sorted sets里面的元素 O(1)

2?? 命令的時間復雜度

參考上表,

3?? 使用場景

有序集合比較典型的使用場景就是排行榜系統,例如視頻網站需要對用戶上傳的視頻做排行榜,榜單的維度可能是多個方面的:按照時間、按照播放數量、按斬訓得的贊數,

image-20211221212056683

如上熱搜榜,以日期為 key :

① 點擊熱搜,每次加 1

zincrby hot:20211220 1 薇婭逃稅

② 右側排行實作,展示今日前 50 排名

# zrange 是從低到高回傳,zrevrange 反之
zrevrange  hot:20211221 0 49 withscores

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/394922.html

標籤:Java

上一篇:火的一塌糊涂的Spring Cloud到底是什么?這篇文章帶你全面了解微服務

下一篇:zabbix 線路質量監控自定義python模塊(socket+deque版),集成ICMP/TCP/UDP探測,批量監控線路質量并自定義閾值聯動mtr保存線路故障日志

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more