Redis 技術棧
- Author: HuiFer
安裝
yum & apt
apt-get install Redis-server
yum install Redis
編譯
- 下載
wget http://download.Redis.io/releases/Redis-5.0.5.tar.gz
- 編譯
tar -zxvf Redis-5.0.5.tar.gz
cd Redis-5.0.5
make && make install
- 配置
vim Redis.conf
- 啓動
- Redis-server
- 關閉
- Redis-cli shutdown
key 命令
keys
- 搜索key根據匹配模式,例如: keys *
exists
- 判斷key是否存在,存在返回1,不存在返回0
del
- 刪除key
type
- 獲取key類型
randomkey
- 隨機返回key
expire
- 設置key過期時間
sortzh
- 字符串排序
- 數字排序
服務端命令
flushall
- 強制清空所有key(目標對象所有數據庫)
flushdb
- 清空當前數據庫
client list
- 連接信息列表
client kill
- 關閉ip:port客戶端
ping
- 判斷是否正常運行,正常運行返回pong
數據類型(操作命令非完整)
string
- 一個key最大存儲512M的數據
操作方法
- get /set(單key操作)
- mget / mset(多key操作)
- getrange 獲取指定下標範圍內的字符串
- setrange 設置指定下標範圍內的字符串
- setex => set expire 組合
- incr 自增1
- incrby 自增指定數字
- decr 自減1
- decrby 自減指定數字
- append 追加字符串
- strlen 返回字符串長度
list
- blpop key 刪除並獲取第一個元素
- brpop key 刪除並獲取最後一個元素
- brpoplpush source destination 複製列表從source -> destination
- lindex key index 根據key 和index 獲取value
- llen key 獲取key的列表長度
- lpop key 從左側出列
- lpush key value 從左側入列
- lrange key start stop 根據start,stop獲取key一定範圍內的列表
- lset key index value 根據key + index 設置value
- lrem key count value 刪除列表元素
- rpush等不做贅述
set
- sadd key value 向key插入值
- scard key 獲取集合成員數量
- sdiff key1 key2 返回集合差集
- sinter key1 key2 返回集合交集
- sunion key1 key2 返回集合並集
- sismember key value 判斷集合中是否包含value
- smembers key 返回集合
- srem key value 刪除集合中的一個value
hash
- hget key filed 獲取hash中指定key->filed的值
- hgetall key 獲取hash中指定key的所有制
- hexists key filed 查看是否存在filed
- hdel key filed 刪除指定key下的filed
- hkeys key 獲取所有filed
- hset key filed value 設置key->filed->value
sortedSet
- zadd key source member 向key追加source,member
- zcard key 獲取成員數量
數據存儲(持久化)
RDB
將某個時間點的所有數據都以二進制形式存放到硬盤上
- 可以將快照複製到其它服務器從而創建具有相同數據的服務器副本.
- 如果系統發生故障,將會丟失最後一次創建快照之後的數據.
- 如果數據量很大,保存快照的時間會很長,建議異步寫入.
- 存在的問題:時間、性能開銷大,不可控且容易丟失數據.
- 同步機制
- save 命令,阻塞其他命令,直到save命令結束
- bgsave 命令,異步化,創建子線程進行持久化,不會阻塞其他命令
- 自動化觸發
- 根據修改數量、時間進行命令執行
- save 900 1 # 900秒之內,對數據庫進行了一次修改就執行 bgsave 命令
- save 300 10 # 300秒之內,對數據庫進行了十次修改就執行 bgsave 命令
- save 60 10000 # 60秒之內,對數據庫進行了一萬次修改就執行 bgsav e命令
AOF
將寫命令添加到 AOF 文件(Append Only File)的末尾(MySQL Binlog、HBase HLog).
- 隨着服務器寫請求的增多,AOF 文件會越來越大.Redis 提供了一種將 AOF 重寫的特性,能夠去除 AOF 文件中的冗餘寫命令
- 使用 AOF 持久化需要設置同步選項,從而確保寫命令什麼時候會同步到磁盤文件上.這是因爲對文件進行寫入並不會馬上將內容同步到磁盤上,而是先存儲到緩衝區,然後由操作系統決定什麼時候同步到磁盤.
同步機制
- always 每個命令都同步
- eversec 每秒同步
- no 操作系統決定同步時間
AOF重寫
- 對多條原生命令進行優化,重寫成簡化的命令以減少磁盤佔用量、提高故障恢復效率.
- 當 AOF 文件過大或增長速度過快時自動觸發
- 配置
-
auto-aof-rewrite-min-size:AOF 文件重寫需要的大小
-
auto-aof-rewrite-percentage:AOF 文件增長率
-
aofcurrentsize:AOF 當前大小
-
aof-base-size:AOF 上次啓動和重寫的大小
-
- 觸發條件
- aof_current_size > auto-aof-rewrite-min-size
- aof_current_size - aof_base_size/aof_base_size > auto-aof-rewrite-percentage
集羣
主從複製
主從鏈(拓撲結構)
複製模式
- 全量複製:master 全部同步到 slave
- 部分複製:slave 數據丟失進行備份
問題點
- 同步故障
- 複製數據延遲(不一致)
- 讀取過期數據(Slave 不能刪除數據)
- 從節點故障
- 主節點故障
- 配置不一致
- maxmemory 不一致:丟失數據
- 優化參數不一致:內存不一致.
- 避免全量複製
- 選擇小主節點(分片)、低峯期間操作.
- 如果節點運行 id 不匹配(如主節點重啓、運行 id 發送變化),此時要執行全量複製,應該配合哨兵和集羣解決.
- 主從複製擠壓緩衝區不足產生的問題(網絡中斷,部分複製無法滿足),可增大複製緩衝區( rel_backlog_size 參數).
- 複製風暴
哨兵機制
拓撲圖
節點下線
- 客觀下線
- 所有 Sentinel 節點對 Redis 節點失敗要達成共識,即超過 quorum 個統一.
- 主管下線
- 即 Sentinel 節點對 Redis 節點失敗的偏見,超出超時時間認爲 Master 已經宕機.
leader選舉
- 選舉出一個 Sentinel 作爲 Leader:集羣中至少有三個 Sentinel 節點,但只有其中一個節點可完成故障轉移.通過以下命令可以進行失敗判定或領導者選舉.
- 選舉流程
- 每個主觀下線的 Sentinel 節點向其他 Sentinel 節點發送命令,要求設置它爲領導者.
- 收到命令的 Sentinel 節點如果沒有同意通過其他 Sentinel 節點發送的命令,則同意該請求,否則拒絕.
- 如果該 Sentinel 節點發現自己的票數已經超過 Sentinel 集合半數且超過 quorum,則它成爲領導者.
- 如果此過程有多個 Sentinel 節點成爲領導者,則等待一段時間再重新進行選舉.
故障轉移
- 轉移流程
- Sentinel 選出一個合適的 Slave 作爲新的 Master(slaveof no one 命令).
- 向其餘 Slave 發出通知,讓它們成爲新 Master 的 Slave( parallel-syncs 參數).
- 等待舊 Master 復活,並使之稱爲新 Master 的 Slave.
- 向客戶端通知 Master 變化.
- 從 Slave 中選擇新 Master 節點的規則(slave 升級成 master 之後)
- 選擇 slave-priority 最高的節點.
- 選擇複製偏移量最大的節點(同步數據最多).
- 選擇 runId 最小的節點.
讀寫分離
定時任務
- 每 1s 每個 Sentinel 對其他 Sentinel 和 Redis 執行 ping,進行心跳檢測.
- 每 2s 每個 Sentinel 通過 Master 的 Channel 交換信息(pub - sub).
- 每 10s 每個 Sentinel 對 Master 和 Slave 執行 info,目的是發現 Slave 節點、確定主從關係.
分佈式集羣(Cluster)
拓撲圖
通訊
集中式
將集羣元數據(節點信息、故障等等)幾種存儲在某個節點上.
- 優勢
- 元數據的更新讀取具有很強的時效性,元數據修改立即更新
- 劣勢
- 數據集中存儲
Gossip
尋址分片
hash取模
- hash(key)%機器數量
- 問題
- 機器宕機,造成數據丟失,數據讀取失敗
- 伸縮性
一致性hash
-
問題
- 一致性哈希算法在節點太少時,容易因爲節點分佈不均勻而造成緩存熱點的問題。
- 解決方案
- 可以通過引入虛擬節點機制解決:即對每一個節點計算多個 hash,每個計算結果位置都放置一個虛擬節點。這樣就實現了數據的均勻分佈,負載均衡。
- 解決方案
- 一致性哈希算法在節點太少時,容易因爲節點分佈不均勻而造成緩存熱點的問題。
hash槽
- CRC16(key)%16384
使用場景
熱點數據
會話維持 session
分佈式鎖 SETNX
表緩存
消息隊列 list
計數器 string
緩存設計
更新策略
- LRU、LFU、FIFO 算法自動清除:一致性最差,維護成本低.
- 超時自動清除(key expire):一致性較差,維護成本低.
- 主動更新:代碼層面控制生命週期,一致性最好,維護成本高.
更新一致性
- 讀請求:先讀緩存,緩存沒有的話,就讀數據庫,然後取出數據後放入緩存,同時返回響應.
- 寫請求:先刪除緩存,然後再更新數據庫(避免大量地寫、卻又不經常讀的數據導致緩存頻繁更新).
緩存粒度
- 通用性:全量屬性更好.
- 佔用空間:部分屬性更好.
- 代碼維護成本.
緩存穿透
當大量的請求無命中緩存、直接請求到後端數據庫(業務代碼的 bug、或惡意攻擊),同時後端數據庫也沒有查詢到相應的記錄、無法添加緩存.
這種狀態會一直維持,流量一直打到存儲層上,無法利用緩存、還會給存儲層帶來巨大壓力.
解決方案
- 請求無法命中緩存、同時數據庫記錄爲空時在緩存添加該 key 的空對象(設置過期時間),缺點是可能會在緩存中添加大量的空值鍵(比如遭到惡意攻擊或爬蟲),而且緩存層和存儲層數據短期內不一致;
- 使用布隆過濾器在緩存層前攔截非法請求、自動爲空值添加黑名單(同時可能要爲誤判的記錄添加白名單).但需要考慮布隆過濾器的維護(離線生成/ 實時生成).
緩存雪崩
緩存崩潰時請求會直接落到數據庫上,很可能由於無法承受大量的併發請求而崩潰,此時如果只重啓數據庫,或因爲緩存重啓後沒有數據,新的流量進來很快又會把數據庫擊倒
出現後應對
- 事前:Redis 高可用,主從 + 哨兵,Redis Cluster,避免全盤崩潰.
- 事中:本地 ehcache 緩存 + hystrix 限流 & 降級,避免數據庫承受太多壓力.
- 事後:Redis 持久化,一旦重啓,自動從磁盤上加載數據,快速恢復緩存數據.
請求過程
- 用戶請求先訪問本地緩存,無命中後再訪問 Redis,如果本地緩存和 Redis 都沒有再查數據庫,並把數據添加到本地緩存和 Redis;
- 由於設置了限流,一段時間範圍內超出的請求走降級處理(返回默認值,或給出友情提示).