GEO
GEO 是用來存儲并操作地理位置信息的數據類型。
隨著移動互聯網時代到來,LBS
服務 (Location-Based Service) 愈發潮流,例如附近的建筑等。
GEO 就是為了解決 Redis 對位置信息的存儲需求而誕生的。
內部實現
GEO 內部使用 Sorted Set
集合類型,使用 [GeoHash](Geohash - Wikipedia) 編碼方法實現了經緯度到 Sorted Set 中元素權重分數的轉換。
將經緯度保存到 Sorted Set 中,利用 Sorted Set 提供的“按權重進行有序范圍查找”的特性,實現 LBS 服務中頻繁使用的“搜索附近”的需求。
# 向locations添加
GEOADD locations 116.034579 39.903628 114
# 從locations獲取10個5km內目標
GEORADIUS locations 116.054579 39.040650 5 km ASC COUNT 10
Stream
Stream 是 Redis 專為 消息隊列
設計的數據類型。
在 Stream 之前,基于原生 Redis 消息隊列的實現方式都有著或多或少的缺陷:
- 發布訂閱模式,不能持久化也就無法可靠的保存消息,并且對于離線重連的客戶端不能讀取歷史消息的缺陷;
- List 實現消息隊列的方式不能重復消費,一個消息消費完就會被刪除,而且生產者需要自行實現全局唯一 ID。
基于此,Stream 用于完美地實現消息隊列,它支持消息的持久化、支持自動生成全局唯一 ID、支持ack確認消息的模式、支持消費組模式。
常用命令
- XADD:插入消息,保證有序,可以自動生成全局唯一 ID;
- XLEN :查詢消息長度;
- XREAD:用于讀取消息,可以按 ID 讀取數據;
- XDEL : 根據消息 ID 刪除消息;
- DEL :刪除整個 Stream;
- XRANGE :讀取區間消息
- XREADGROUP:按消費組形式讀取消息;
- XPENDING 和 XACK:
- XPENDING 命令可以用來查詢每個消費組內所有消費者「已讀取、但尚未確認」的消息;
- XACK 命令用于向消息隊列確認消息處理已完成;
應用場景: 消息隊列
普通消息隊列
生產者向隊列發布消息:
# * 代表由Redis自動生成一個全局唯一ID
> XADD mymq * name xiaolin
"1654254953808-0"
# 返回為生成的唯一ID,由"-"符號分為兩部分
# 前一部分為數據插入時,以毫秒為單位計算的當前服務器時間
# 后一部分為當前毫秒的消息序號,從0開始編號
消費者讀取消息:
# 從 ID 號為 1654254953807-0 的消息開始,讀取后續的所有消息(示例中一共 1 條)
> XREAD STREAMS mymq 1654254953807-0
1) 1) "mymq"
2) 1) 1) "1654254953808-0"
2) 1) "name"
2) "xiaolin"
# 阻塞讀取,表示10000毫秒阻塞讀取最新消息($)
> XREAD BLOCK 10000 STREAMS mymq $
(nil)
(10.00s)
消費組
相較于 List,Stream 有自己新的概念:消費組
。創建組之后,使用 XREADGROUP 命令讓組內的消費者讀取消息。
# 命令最后的參數“>”,表示從第一條尚未被消費的消息開始讀取。
> XREADGROUP GROUP group1 consumer1 STREAMS mymq >
1) 1) "mymq"
2) 1) 1) "1654254953808-0"
2) 1) "name"
2) "xiaolin"
消息隊列中的消息一旦被消費組里的一個消費者讀取了,就不能再被該消費組內的其他消費者讀取了,即同一個消費組里的消費者不能消費同一條消息。
但是,**不同消費組的消費者可以消費同一條消息(但是有前提條件,創建消息組的時候,不同消費組指定了相同位置開始讀取消息)**。
使用消費組的目的是讓組內的多個消費者共同分擔讀取消息。
因此,通常會讓每個消費者讀取部分消息,從而實現消息讀取的負載均衡。
消費者宕機重啟
Streams 使用內部隊列(也稱為 PENDING List)留存消費組里每個消費者讀取的消息,直到消費者使用 XACK 命令通知 “消息已經處理完成”。
如果消費者沒有成功處理消息,它就不會給 Streams 發送 XACK 命令,消息仍然會留存。此時,消費者可以在重啟后,用 XPENDING 命令查看已讀取、但尚未確認處理完成的消息。
消息丟失問題
消息隊列分為三個大部分生產者、消費者和隊列中間件。
逐一分析:
- 生產者不會丟失消息,只要能正常收到 ack 確認響應,就表示發送成功,對異常進行重發就可以避免消息丟失。
- 消費者也不會丟失消息,前文提到,Redis 在使用 Pending List 進行消費確認,所以消費者也不會丟失消息。
- 隊列中間件,在語句場景下是 Redis 會 丟失消息,主要在以下場景:
- AOF持久化時異步寫盤,宕機時會丟失消息。
- 主從復制也是異步操作,也可能丟失消息。