今天工作群裡,有小夥伴問了一個問題,從Redis獲取的資料,一會是0,一會是OK。
這引起了我們對Redis資料儲存和讀寫的疑問。
以下是整理的一些技術研究內容。
在 Redis 中,所有的資料儲存都是基於字串的。無論你插入的是 String、int 還是 DateTime 型別的資料,最終都會以字串的形式儲存在 Redis 中。具體來說:
-
String 型別資料:
- 儲存:直接以字串形式儲存,無需額外處理。
- 取出:直接取出字串即可。
-
int 型別資料:
- 儲存:將 int 型別轉換為字串,然後儲存。
- 取出:從 Redis 中取出字串後,再轉換為 int 型別。
-
DateTime 型別資料:
- 儲存:通常將 DateTime 型別轉換為某種標準格式的字串(如 ISO 8601 格式),然後儲存。
- 取出:從 Redis 中取出字串後,再解析為 DateTime 型別。
具體的操作示例如下:
插入資料
import redis
from datetime import datetime
# 連線到 Redis
r = redis.StrictRedis(host='localhost', port=6379, db=0)
# 儲存 String 資料
r.set('my_string', 'Hello, Redis!')
# 儲存 int 資料
r.set('my_int', str(12345))
# 儲存 DateTime 資料
now = datetime.now().isoformat()
r.set('my_datetime', now)
取出資料
# 取出 String 資料
my_string = r.get('my_string').decode('utf-8')
# 取出 int 資料
my_int = int(r.get('my_int'))
# 取出 DateTime 資料
my_datetime = datetime.fromisoformat(r.get('my_datetime').decode('utf-8'))
資料型別轉換:當你從 Redis 取出資料時,Redis 返回的是位元組字串,你需要根據儲存的資料型別進行相應的轉換:
- **String 資料:**直接解碼為字串。
- **int 資料:**先解碼為字串,然後轉換為整數。
- **DateTime 資料:**先解碼為字串,然後解析為 DateTime 物件。
這種方式雖然簡單直接,但需要在應用層處理資料的序列化和反序列化。
我們可以根據需要選擇不同的序列化格式(如 JSON、Protobuf 等)來儲存更復雜的資料結構。
這種設計會出現資料序列化和反序列化的效能問題嗎?
資料的序列化和反序列化確實會引入一些效能開銷,但在大多數情況下,這些開銷是可以接受的。具體的效能影響取決於以下幾個因素:
-
資料量大小: 如果資料量較小,序列化和反序列化的開銷通常是微不足道的。但是對於大資料量,這些操作的成本會顯著增加。
-
序列化方式: 不同的序列化方式有不同的效能特徵。例如,簡單的字串序列化通常比複雜的格式(如 JSON、XML 或 Protobuf)要快。
-
資料型別和複雜度: 簡單的資料型別(如整數和字串)比複雜的資料型別(如物件或巢狀結構)更容易序列化和反序列化。
為了減少序列化和反序列化的效能問題,可以考慮以下幾種最佳化方法:
-
選擇高效的序列化格式: 對於簡單的資料型別,直接使用字串或二進位制格式可以減少開銷。對於複雜資料型別,可以選擇效能更好的序列化格式,如 MessagePack 或 Protobuf。
-
減少資料量: 僅儲存和傳輸必要的資料,儘量減少每次操作的資料量。
-
批次操作: 批次操作可以減少網路開銷。例如,使用 Redis 的批次命令(如 MSET 和 MGET)一次性操作多個鍵值對。
-
快取計算結果: 對於需要頻繁計算的資料,可以將計算結果快取到 Redis 中,避免每次都進行序列化和反序列化操作。
Redis內部資料的儲存原理是什麼?
Redis 是一個基於記憶體的高效能鍵值儲存系統,採用了多種資料結構來最佳化資料儲存和訪問效率。下面是 Redis 內部資料儲存的一些關鍵原理和機制:
資料結構
Redis 支援多種資料結構,每種資料結構有其特定的儲存方式:
-
字串(String)
- 儲存:Redis 的字串是二進位制安全的,最長可以儲存 512 MB 的資料。字串資料結構採用簡單動態字串(SDS)來實現,這種結構允許高效的字串操作和記憶體管理。
- 資料結構:SDS 是一種帶有後設資料(如長度和空餘空間)的動態陣列。
-
雜湊(Hash)
- 儲存:雜湊表用於儲存鍵值對集合,適合儲存物件的屬性資料。
- 資料結構:採用雜湊表(dict)實現。小雜湊表(元素少於一定數量時)採用 ziplist(壓縮列表)最佳化儲存,大雜湊表則使用標準的雜湊表實現。
-
列表(List)
- 儲存:列表用於儲存有序的字串集合,支援快速的頭尾插入和刪除操作。
- 資料結構:列表小於一定大小時使用 ziplist 實現,較大時使用雙向連結串列(quicklist)實現。
-
集合(Set)
- 儲存:集合用於儲存無序的字串集合,支援快速的新增、刪除和成員檢查操作。
- 資料結構:小集合使用整數陣列(intset)實現,大集合使用雜湊表實現。
-
有序集合(Sorted Set)
- 儲存:有序集合在集合的基礎上為每個成員關聯一個分數,成員按分數排序。
- 資料結構:採用跳錶(skiplist)和雜湊表聯合實現,跳錶用於排序,雜湊表用於快速查詢。
-
點陣圖(Bitmap)
- 儲存:點陣圖用於高效地儲存和操作大量的二進位制資料。
- 資料結構:底層實現為字串,按位操作。
-
HyperLogLog
- 儲存:用於基數估計演算法,估算集合中不重複元素的數量。
- 資料結構:底層使用稀疏和稠密兩種表示法來最佳化空間使用。
-
地理空間(Geo)
- 儲存:用於儲存地理位置資訊。
- 資料結構:基於有序集合實現,透過 GEOADD、GEORADIUS 等命令操作。
記憶體管理
Redis 採用多種記憶體管理技術來最佳化效能和記憶體使用:
- 記憶體分配:Redis 使用 jemalloc 作為預設的記憶體分配器,可以高效地管理記憶體碎片和分配。
- 物件共享:對於常用的小物件(如小整數),Redis 會在內部共享這些物件,減少記憶體開銷。
- 記憶體壓縮:對於字串等資料,Redis 可以使用壓縮技術來減少記憶體使用。
- LRU/LFU 淘汰:當記憶體達到設定的上限時,Redis 可以根據設定的策略(如 LRU 或 LFU)淘汰不常用的資料。
持久化
為了防止資料丟失,Redis 提供了多種持久化機制:
- RDB 快照:定期將記憶體中的資料快照儲存到磁碟。優點是資料恢復速度快,但可能丟失最近的資料。
- AOF 日誌:記錄每個寫操作,定期將日誌重新整理到磁碟。優點是資料持久化更可靠,但恢復速度較慢。
- 混合模式:結合 RDB 和 AOF 的優點,先載入 RDB 快照,然後應用 AOF 日誌。
哨兵和叢集
為了實現高可用性和擴充套件性,Redis 提供了哨兵模式和叢集模式:
- 哨兵模式:透過哨兵程序監控 Redis 主從節點的狀態,自動進行故障轉移。
- 叢集模式:將資料分片儲存到多個節點上,透過雜湊槽(hash slot)實現資料分佈和訪問。
Redis 內部透過這些機制和原理,提供了高效、可靠的鍵值儲存和訪問服務。
以上。
周國慶
2024/6/2