引言
Redis 除了我們最常用的 String、Hash、List、Set、ZSet(Sorted Set) 這五種基本數據結構外,還提供了很多高級或特殊用途的數據結構/類型 ,它們可以滿足更復雜的業務需求。
? Redis 的“五大基本數據結構”回顧
類型 | 特點 |
---|---|
String | 字符串,可以是文本、數字、二進制等 |
Hash | 鍵值對集合,適合存儲對象 |
List | 有序的字符串列表(底層為鏈表) |
Set | 無序且不重復的字符串集合 |
ZSet (Sorted Set) | 帶有分數排序的集合 |
一、Stream(流)
Redis 5.0 引入,是一個日志型數據結構 ,支持發布訂閱、持久化、消費者組 等功能。
可以把 Redis Stream 想象成一個:持久化的、可查詢的、支持消費者組的消息隊列系統。 基于 Radix Tree + Listpacks(或稱 ziplist) 實現的,具有高效寫入和讀取的能力。
1. Stream 的核心概念
概念 | 說明 |
---|---|
Stream | 存儲事件記錄的有序隊列,每個記錄都有唯一 ID |
Entry / Message | 一條消息,包含多個字段(field-value 對) |
Consumer Group | 消費者組,類似 Kafka 的 consumer group,用于多消費者協作消費 |
Consumer | 消費者,屬于某個消費者組 |
Pending Entries List (PEL) | 記錄已發給消費者但尚未確認的消息 |
Claim | 把未確認的消息重新分配給其他消費者 |
比如:我們可以用 Stream 實現一個事件,如下:消防員 + 著火事件
事件 | 操作 |
---|---|
著火事件 | Producer 使用XADD 寫入一條事件消息 |
消防員 | Consumer 屬于某個消費者組,監聽這個 Stream |
發現火情 | Consumer 通過XREADGROUP 接收到事件 |
使用干粉滅火器 | Consumer 執行業務邏輯(如寫數據庫、發通知) |
確認火已滅 | Consumer 調用XACK 確認事件處理完成 |
沒火時等待 | 使用BLOCK 參數阻塞等待新事件到來 |
2. Stream 支持的功能📦
功能 | 描述 |
---|---|
📥 消息寫入 | 使用XADD 命令添加新消息 |
📤 消息讀取 | 使用XRANGE ,XREAD ,XREADGROUP 等命令讀取消息 |
🔄 消費者組 | 支持多個消費者組并行消費,避免重復消費 |
🧾 自動偏移量管理 | 消費者組會自動維護消費進度(offset) |
?? 阻塞讀取 | 類似 BLPOP,使用XREAD 或XREADGROUP COUNT ... BLOCK |
🔁 消息重試機制 | 可通過XCLAIM 將失敗的消息重新分發給其他消費者 |
🗑? 消息過期 | 可設置最大條數限制(MAXLEN )實現自動清理 |
🔐 持久化支持 | 消息寫入后會持久化到 AOF 和 RDB 中(如果啟用) |
3. 常用命令一覽🛠?
命令 | 用途 |
---|---|
XADD key [MAXLEN ~ count] * field value [field value ...] | 添加消息 |
XRANGE key start end [COUNT count] | 查詢指定范圍內的消息 |
XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id [id ...] | 從指定位置讀取消息 |
XREADGROUP GROUP group consumer [COUNT count] [BLOCK ms] STREAMS key [key ...] IDLE [min-idle-time] | 以消費者組方式讀取消息 |
XACK key group id [id ...] | 確認消息已被成功處理 |
XCLAIM key group consumer min-idle-time id [id ...] | 搶占未被確認的消息 |
XDEL key id [id ...] | 刪除指定消息 |
XTRIM key MAXLEN ~ count | 控制 stream 的長度(保留最近的 count 條消息) |
XGROUP CREATE key group-name $ | 創建消費者組 |
XGROUP SETID key group new-id | 設置消費者組的起始讀取位置 |
XGROUP DELGROUP key group | 刪除消費者組 |
XINFO STREAM key | 查看 stream 的詳細信息 |
XINFO GROUPS key | 查看所有消費者組 |
XINFO CONSUMERS key group | 查看某組下的所有消費者 |
4. 示例操作🔁
1?? 添加消息
XADD mystream * name Alice age 30
# 輸出
"1717986912345-0"
- 每條消息都會有一個自動生成的 ID:
<時間戳>-<序列號>
2?? 查詢所有消息
XRANGE mystream - +
# 輸出
1) 1) "1717986912345-0"2) 1) "name"2) "Alice"3) "age"4) "30"
-
表示最小 ID,+
表示最大 ID
3?? 創建消費者組
XGROUP CREATE mystream mygroup $
$
表示從最后一條消息之后開始消費
4?? 以消費者組方式消費消息
XREADGROUP GROUP mygroup consumer1 COUNT 1 STREAMS mystream >
>
表示只讀取未被該組消費過的消息
5?? 確認消息已處理
XACK mystream mygroup 1717986912345-0
6?? 查看未確認的消息
XPENDING mystream mygroup
5. 使用場景
場景 | 描述 |
---|---|
📬 消息隊列 | 替代 RabbitMQ、Kafka,適用于輕量級消息隊列系統 |
📝 日志收集 | 收集分布式系統的日志、事件、監控數據 |
📲 事件溯源(Event Sourcing) | 所有狀態變更都作為事件流存儲 |
🎮 游戲排行榜更新 | 記錄玩家得分變化事件 |
📊 實時數據分析 | 接收實時數據流,進行聚合、分析 |
🚦 分布式任務調度 | 多個 worker 協作處理任務流 |
二、Geospatial(地理位置)
Redis 3.2 引入,用于存儲地理位置信息,并支持 距離計算、范圍查詢 等操作。
Geospatial(地理空間) 是指與地球表面位置相關的信息,通常包括:地理位置(經緯度)、地理對象(點、線、面)、空間關系(距離、包含、交集等)、時間維度(時空變化)
Geospatial 數據可以用于描述任何具有地理屬性的事物,比如:城市的位置、道路網絡、氣象數據、移動設備軌跡、商店分布、用戶當前位置
1. Geospatial 技術的核心組成部分🧭
組成部分 | 描述 |
---|---|
GIS(Geographic Information System) | 地理信息系統,用于采集、存儲、分析和展示地理空間數據 |
GPS(Global Positioning System) | 全球定位系統,用于獲取精確的地理位置坐標 |
RS(Remote Sensing) | 遙感技術,通過衛星或無人機獲取地表信息 |
GEOJSON / KML / Shapefile | 常見的地理空間數據格式 |
地圖服務(如 Google Maps、高德地圖) | 提供可視化、導航、搜索等功能 |
2. Geospatial 應用場景
應用領域 | 示例 |
---|---|
物流配送 | 實時追蹤包裹位置、規劃最優路徑 |
共享出行 | 顯示附近車輛、計算距離、預估到達時間 |
智慧城市 | 監控交通流量、管理公共設施、應急調度 |
零售行業 | 分析門店周邊人流、優化選址策略 |
農業 | 精準施肥、病蟲害監測、產量預測 |
環境監測 | 追蹤污染源、分析氣候變化趨勢 |
社交媒體 | 發布帶地理位置的內容、查找附近好友 |
游戲開發 | 構建基于真實世界的 AR 游戲(如 Pokémon GO) |
3. 在數據庫中支持 Geospatial 的方式🔧
很多數據庫都提供了對地理空間數據的支持,下面是一些常見的數據庫及其功能:
3.1 Redis GEO
Redis 從 3.2 版本開始支持 GEO 功能 ,可以用來存儲地理位置,并進行距離計算和范圍查詢。
示例:
# 添加位置
GEOADD cities 116.4074 39.9042 "北京"
GEOADD cities 121.4737 31.2304 "上海"# 獲取某城市的經緯度
GEOPOS cities 北京# 計算兩地之間的距離
GEODIST cities 北京 上海 km# 查找附近的城市(500km 內)
GEORADIUS cities 116.4074 39.9042 500 km
- Redis GEO 是基于 Sorted Set + GeoHash 實現的,適合輕量級 LBS(基于位置的服務)應用。
3.2 MySQL Spatial Data Types
MySQL 支持 POINT
, LINESTRING
, POLYGON
等空間類型,以及空間索引。
示例:
CREATE TABLE locations (id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(100),coord POINT SRID 4326
);INSERT INTO locations (name, coord)
VALUES ('北京', POINT(116.4074, 39.9042));-- 查詢距離北京 500 公里以內的城市
SELECT name, ST_Distance(coord, POINT(116.4074, 39.9042)) AS distance
FROM locations
HAVING distance <= 500;
Geospatial(地理空間)是指與地球位置相關的數據和技術,廣泛應用于地圖服務、LBS、智慧交通、環境監測等多個領域。主流數據庫(如 Redis、MySQL、PostgreSQL、MongoDB)都提供了對地理空間數據的支持,幫助開發者輕松實現位置查詢、距離計算、區域劃分等功能
三、HyperLogLog
HyperLogLog 是 Redis 提供的一種 概率數據結構(Probabilistic Data Structure) ,用于 高效估算一個集合中不重復元素的數量(基數,Cardinality) 。
? 它是用來做什么的? ==> 場景:你有一個巨大的數據集,想要知道其中有多少個唯一元素(如獨立訪問用戶、IP 數量等),但又不想用 Set 存儲所有元素。 【HyperLogLog 就是為此而生的!】
1. 核心特點🔍
特性 | 描述 |
---|---|
用途 | 基數統計(去重計數) |
空間效率極高 | 最多占用 12KB 內存,可統計上億個唯一元素 |
誤差率可控 | 默認誤差小于 1% |
不可獲取具體元素 | 只能統計數量,不能列出具體內容 |
支持合并操作 | 多個 HLL 可以合并,進行全局統計 |
2. Redis 中 HyperLogLog 的常用命令📦
命令 | 說明 |
---|---|
PFADD key element [element ...] | 向指定的 HyperLogLog 中添加元素 |
PFCOUNT key [key ...] | 返回一個或多個 HyperLogLog 的基數估算值 |
PFMERGE dest source [source ...] | 將多個 HyperLogLog 合并為一個 |
3. 示例操作🔁
# 添加元素
PFADD visitors user1 user2 user3 user4 user5# 查看估計的獨立訪客數
PFCOUNT visitors
(integer) 5# 再添加一些新用戶
PFADD visitors user6 user7 user3 # user3 已存在# 再次查看,user3 不會重復計數
PFCOUNT visitors
(integer) 7# 創建另一個 HyperLogLog
PFADD mobile_visitors user8 user9 user10# 合并兩個 HyperLogLog
PFMERGE all_visitors visitors mobile_visitors# 查看合并后的總訪問人數
PFCOUNT all_visitors
(integer) 10
4. 使用場景
場景 | 描述 |
---|---|
📊 網站 UV 統計 | 每日/每月獨立訪客數統計(比使用 Set 節省內存百倍) |
🌐 IP 去重計數 | 統計攻擊源 IP、訪問來源 IP 的數量 |
👥 用戶行為分析 | 如“每日活躍用戶數”、“不同設備登錄數”等 |
📡 日志聚合 | 在大數據平臺中做輕量級實時統計 |
🧮 數據預處理 | 預估去重數據規模,決定是否需要更精確的計算方式 |
5. 空間效率對比💾
數據結構 | 存儲1萬個字符串所需內存 | 存儲100萬個字符串所需內存 |
---|---|---|
Set | ~幾 MB | 幾百 MB 到 1GB+ |
HyperLogLog | 最多 12KB | 始終是 12KB |
Redis 的 HLL 實現使用了稀疏和密集兩種存儲格式,最終統一壓縮為最多 12KB。
6. 注意事項??
?? 注意:它只是一個 近似算法 ,不能獲取精確值。
問題 | 建議 |
---|---|
是否精確? | ? 否,是一個近似算法(誤差 < 1%) |
是否能查具體元素? | ? 否,只能統計數量 |
是否適合小數據集? | ? 也適合,但如果你要完全精確,還是用 Set 更好 |
是否支持合并? | ? 支持,這是其一大優勢 |
是否支持持久化? | ? 是,寫入 AOF 和 RDB |
小結:HyperLogLog 是 Redis 提供的一個高效估算唯一元素數量的概率數據結構,非常適合用于大規模去重統計(如 UV、IP 數量等),僅需極小內存即可完成超大體量的數據估算,是大數據統計分析中的利器
四、Bitmaps(位圖)
Redis 的 Bitmaps 并不是一種獨立的數據結構 ,而是基于 String 類型 實現的一種高效操作二進制位(bit)的方式。
它非常適合用于:用戶簽到系統、日活統計(DAU)、布爾狀態記錄(如是否已讀、是否登錄等)、緊湊的去重計數
1. 核心特點
特性 | 描述 |
---|---|
存儲方式 | 基于 String,每個字符是 8 bit |
操作粒度 | 每個 bit 可單獨設置、獲取、統計 |
節省內存 | 1 字節可表示 8 個布爾值,極大節省空間 |
支持位運算 | AND、OR、XOR、NOT 等 |
適用場景 | 狀態標記、簽到、訪問統計 |
2. 常用命令📦
命令 | 說明 |
---|---|
SETBIT key offset value | 設置某個 bit 位的值(0 或 1) |
GETBIT key offset | 獲取某個 bit 位的值 |
BITCOUNT key [start end] | 統計被設為 1 的 bit 數量 |
BITPOS key bit [start] [end] | 查找第一個值為指定 bit 的位置 |
BITOP operation destkey key [key ...] | 對多個 bitmap 做位運算(AND/OR/XOR/NOT) |
3. 示例操作🔁
3.1 用戶簽到系統(一個月)
# 用戶 ID 為 1001,表示他在第 0 天(1號)和第 7 天(8號)簽到了
SETBIT user:1001:sign_in 0 1
SETBIT user:1001:sign_in 7 1# 查詢某天是否簽到
GETBIT user:1001:sign_in 0 # 返回 (integer) 1
GETBIT user:1001:sign_in 3 # 返回 (integer) 0# 統計總共簽到幾天
BITCOUNT user:1001:sign_in # 返回 (integer) 2
3.2 統計日活躍用戶(DAU)
假設你有 100,000 個用戶,ID 從 0 到 99999。每天記錄一個用戶是否登錄:
# 今天是 2025-04-05
SETBIT dau:20250405 1001 1 # 用戶 1001 登錄了
SETBIT dau:20250405 2002 1 # 用戶 2002 登錄了# 查詢總登錄人數
BITCOUNT dau:20250405 # 返回 (integer) 2
3.3 多日簽到合并統計(BitOp)
你想知道用戶 1001 在連續三天內的簽到情況:
# 第一天簽到
SETBIT user:1001:day1 0 1
SETBIT user:1001:day1 2 1# 第二天簽到
SETBIT user:1001:day2 1 1
SETBIT user:1001:day2 2 1# 合并兩天簽到情況(按 OR 運算)
BITOP OR user:1001:total_sign user:1001:day1 user:1001:day2# 查看合并后的簽到總數
BITCOUNT user:1001:total_sign # 返回 (integer) 3 (0,1,2 都為 1)
4. 使用場景
場景 | 描述 |
---|---|
📅 用戶簽到系統 | 每個 bit 表示一天是否簽到 |
👥 日活躍用戶統計(DAU) | 每個 bit 表示一個用戶當天是否活躍 |
📮 已讀消息標記 | 記錄用戶是否閱讀過某條消息 |
🎮 游戲成就系統 | 每個 bit 表示一項成就是否完成 |
🧮 緊湊布爾狀態存儲 | 替代多個 key,減少內存開銷 |
📊 數據壓縮 | 用最少的空間表達大量布爾狀態 |
5. 內存占用對比💾
數據類型 | 1w 個布爾值所需內存 | 100w 個布爾值所需內存 |
---|---|---|
Hash / Set | 幾 MB ~ 幾十 MB | 幾百 MB ~ 1GB+ |
Bitmaps | 1250 Bytes | ~124KB |
💡 一個 BitMap 可以用 12KB 內存來表示超過 10 萬個布爾值 !
?? 注意事項
問題 | 建議 |
---|---|
是否精確 | ? 是(無誤差) |
是否支持并發 | ? 是(Redis 是單線程原子操作) |
是否適合小數據集 | ? 是 |
是否能查詢具體哪些 bit 為 1 | ? 否(只能統計數量或查找位置) |
是否支持壓縮 | ? 是(Redis 內部做了優化) |
小結:Redis Bitmaps 是一種基于 String 的高效位操作機制,非常適合用來做簽到系統、日活統計、布爾狀態管理等場景,僅需極小內存即可處理上百萬級別的布爾狀態,是 Redis 中非常實用的“隱藏神器”。
五、Bitfields
Bitfields
是 Redis 提供的一種高級數據結構,它允許你在 Redis 的 String 類型中操作二進制位(bit)的多個字段(field) 。它是對BITFIELD
命令的支持,從 Redis 3.2 版本開始引入。
1. Bitfields 是什么?🧠
Redis 中的 String 是二進制安全的字節數組 ,一個 String 最多可以存儲 512MB 的數據。而 BITFIELD
命令允許我們在這些字節中定義多個 “位字段”(bitfield) ,每個字段可以是:
- 指定長度的 有符號整數(signed integer)
- 或者無符號整數(unsigned integer)
我們可以對這些字段進行 讀取、寫入、增減等操作 ,非常適合用于緊湊的數據表示和高效的狀態管理。
2. 使用場景
📅 用戶簽到系統 | 用 1 bit 表示一天是否簽到,365 天只需 46 字節 |
🎮 游戲角色狀態 | 存儲角色屬性、技能等級、成就等信息 |
📊 高頻計數器 | 精確控制字段大小,節省內存空間 |
🧮 位標志集合 | 代替多個布爾值,減少 key 數量 |
3. BITFIELD 常用命令🛠?
3.1 定義并操作多個字段
BITFIELD key [GET type pos] [SET type pos val] [INCRBY type pos delta]
參數說明:
- type:字段類型,如:
u4
表示 4 位無符號整數(0 ~ 15)i8
表示 8 位有符號整數(-128 ~ 127)
pos
:字段起始的 bit 位置(從 0 開始)val
:要設置的值delta
:增量值
3.2 示例操作🔁
① 記錄用戶每月簽到情況(31天)
# 設置第0天為已簽到(1),第1天未簽到(0)
BITFIELD user:1001:sign_in SET u1 0 1 SET u1 1 0# 查詢第0天和第1天簽到狀態
BITFIELD user:1001:sign_in GET u1 0 GET u1 1# 輸出
1) (integer) 1
2) (integer) 0
② 使用帶符號整數操作計數器
# 設置第0位為 4 位有符號整數,初始值為 5
BITFIELD user:points SET i4 0 5# 查詢該字段的值
BITFIELD user:points GET i4 0
(integer) 5# 增加 2
BITFIELD user:points INCRBY i4 0 2
(integer) 7# 減少 5
BITFIELD user:points INCRBY i4 0 -5
(integer) 2
③ 緊湊存儲個狀態字段
比如你想存儲一個游戲角色的狀態:
字段 | 類型 | 占用位數 | 取值范圍 |
---|---|---|---|
HP(生命值) | 無符號 | 10 bits | 0 ~ 1023 |
MP(魔法值) | 無符號 | 8 bits | 0 ~ 255 |
狀態標志 | 有符號 | 2 bits | -2 ~ 1 |
# 初始化 HP=500, MP=200, 狀態=-1
BITFIELD player:101 SET u10 0 500 SET u8 10 200 SET i2 18 -1# 查詢所有字段
BITFIELD player:101 GET u10 0 GET u8 10 GET i2 18# 輸出
1) (integer) 500
2) (integer) 200
3) (integer) -1
4. Bitfields 的優勢
優點 | 說明 |
---|---|
💾 內存占用極低 | 比多個 key 更節省內存 |
? 高效訪問 | 所有操作都在一個 key 中完成 |
🧮 支持多種數據格式 | 支持有符號/無符號整數 |
🔄 原子性操作 | 支持原子增減,適合并發計數 |
🧩 結構靈活 | 可自定義字段大小和偏移量 |
5. 注意事項??
- 所有操作都是基于 bit 級別 ,需要自己計算偏移位置。
- 不支持直接刪除某個字段,只能重置整個 key。
- 如果字段超出范圍,會自動截斷(wrap around)或溢出。
- 適用于內部狀態存儲,不適合頻繁查詢的業務邏輯。
結論:Redis 的 Bitfields 是一種強大的二進制字段操作工具,它允許你在字符串中按位定義多個字段,并對其進行讀寫、增減等操作,非常適合用來做狀態管理、簽到系統、緊湊計數器等高性能、低內存消耗的場景
六、Module 擴展模塊
Redis 支持通過加載模塊來擴展新的數據結構。例如:
📌 RedisJSON(JSON 數據結構)
- 存儲 JSON 格式數據
- 支持 JSONPath 查詢和修改字段
JSON.SET user:1001 $ '{"name":"Tom","age":25}'
JSON.GET user:1001 $.name
📌 RedisTimeSeries(時間序列數據庫)
- 高效存儲和查詢時間序列數據(如監控指標、傳感器數據)
TS.CREATE temperature:1 LABELS sensor type temp
TS.ADD temperature:1 * 25.5
TS.RANGE temperature:1 0 -1
📌 其他常見模塊:
- RedisGraph(圖數據庫)
- RedisSearch(全文搜索 + 聚合)
- RedisAI(機器學習模型部署)