基于Redis構建系統的經驗和教訓
Redis 是一個非常快速和強大的 Key-Value 存儲(持久化)系統, 相對于一般的 NoSQL 存儲系統, 它最大的特點是支持豐富的數據結構. 特別是其 zset(sorted set)數據結構, 堪稱表達能力最強的結構之一(其它強大的數據結構如 sorted hashmap), 可以直接地表達業務邏輯。
拿一個 Messaging(消息傳遞)系統來舉例, 收件箱發件箱這樣的業務邏輯直接用 zset 存儲即可, 因為 zset 的每一個元素都有一個用于排序的權重值, 可以非常方便快速地地進行插入和刪除操作. 如果使用純粹的 KV 系統, 存儲列表等非字符串結構的數據將是無盡的痛苦。
由于 Redis 本身的限制, 它所能處理的數據必須完全放在內存中, 而硬盤上的數據是內存數據的一個鏡像, 所以, 限制了它的容量不能超過內存的容量(VM 模式無實際意義, 已在新版本中去除). 當前, 服務器的內存以 32G 為普遍情況, 96G 算較好, 如果一個系統要存儲 1T 的數據, 那么必須用上 10 臺服務器, 硬件成本非常高 — 且先不談由此面臨的軟件的架構改動. 當前, 1T 的數據只能算零頭, 對于一個100萬活躍用戶的系統, 平均每人每天產生 1K 數據, 便需要 1G 的存儲空間, 這僅相當于每個用戶每天只發10條微博或者10條聊天信息, 真正流行的系統將遠遠超過這個數據規模。
持久化, 復制和備份帶來的系統和網絡問題
一般數據達到幾百 M 或者 1G 時, Redis 必須且只能開啟 aof 操作日志異步寫硬盤的持久化模式, 由于用戶記錄數據變更日志的 aof 文件體積增加比較嚴重, 必須定期對 aof 文件進行收縮(rewrite). 收縮的過程其實是將內存數據鏡像到硬盤的過程, Redis 主進程需要 fork 一個進程出來, 雖然操作系統有寫時拷貝功能, 但仍然要為 fork 出來的進程保留足夠的內存空間, 所以 Redis 只能使用內存容量的 50%。
在寫 aof 文件時, Redis 完全沒有任何速度控制策略, 經常導致硬盤讀寫占滿, 其它進程一旦涉及到文件操作, 都將被阻塞住。
Redis 自身支持主從模式, 可以方便地進行數據備份, 避免單點失敗造成數據丟失. 但是, Redis 的主從模式并不成熟, 例如當網絡出現抖動時, 可以導致主從之間發生一次全量復制, 這對網絡帶寬是一個打擊。
無分布式方案導致軟件設計的復雜度增加
Redis 是一個單機的存儲方案, 當數據超過單臺服務器的內存容量時, 必須由軟件的設計者在軟件邏輯層面設計出一套數據拆分的方案. 這必然導致軟件設計者無法關注于業務邏輯, 而將大量的精力放在數據存儲層, 即增加了軟件的復雜度, 也造成可維護性的下降。
結論
Redis 不適合作為海量數據存儲方案. Redis 適合在數據規模較小, 性能要求較高的條件下應用。
海量數據存儲的備選方案
根據業務公開的經驗, 海量數據的存儲方案主要有:
- Cassadra, 據我所知是純粹的 KV 方案, 對結構化數據表達能力非常弱.
- HBase, 是 Google Bigtable 的一種實現, 在 Facebook 有應用. 其對結構化數據的表達能力的最大優點是數據是一維有序的(按 Key 排序)。
- mongoDB
我們需要什么樣的海量數據存儲方案(個人看法)?
實際經驗來看, 純粹的 KV 方案太過簡單, 簡單到幾乎無法直接表達任何業務數據, 相當于拿著一堆鐵錠, 然后開發者自己再去制作自己需要的零件. 顯然, 對于使用者角色的開發者來說, 這不友好。
真正友好的還是結構化數據存儲方案, 也就是對數據有一定的理論模型, 從而產生一些數據結構. 要創造出一個普遍適用的數據模型是非常困難的, Bigtable 那樣的模型并不是經常被創造出來, 而且在 Bigtable 之外仍然需要更多的模型。
從我自己的開發經驗來看, 最基本的, 數據應該是有序的, 可以根據權重來排序. 一旦數據是有序的, 便能分段訪問和傳輸, 而不必全量拷貝, 海量數據處理的原則是拷貝(傳輸)最少的數據。
大多數業務的模式都是這樣的: 首先, 實體被存儲在一個空間里, 可通過 key 訪問; 其次, 創建實體 key 的若干個有序子集. 這兩種結構似乎已經包含了所有的業務邏輯. 所有聊天消息(實體), 為所有的用戶創建表示其收件箱消息集合(子集). 所有的微博消息(實體), 為每個用戶創建 timeline 消息集合(子集)……