- 為什么用緩存?
- 高性能:減少查詢同一個數據時的響應速度
- 高並發:減少數據庫的承載壓力(2000/s),緩存走內存,天然支撐高並發
- 緩存的不良后果:
- 緩存與數據庫的雙寫不一致
- 緩存雪崩
- 緩存穿透
- 緩存並發競爭
- Redis和memcached區別(單線程、NIO、異步)
- Redis支持服務器端數據操作:數據類型更多,功能更全
- 內存使用效率對比:簡單key-value時memcached效率高,hash結構存儲Redis高
- 性能對比:Redis單線程,小數據時Redis高;大數據時多線程的memcached高
- 集群模式:memcached不支持集群,Redis支持
- Redis單線程模型:
- 文件事件處理器,單線程,通過IO多路復用程序同時監聽多個socket,通過socket上的事件選擇對應的事件處理器處理事件
- AE_READABLE:socket可讀(連接操作、客戶端對Redis執行write操作,close操作)
- AE_WRITABLE:socket可寫(客戶端對Redis執行read操作)

- Redis單線程模型為什么效率高?
- 純內存操作(事件處理器在內存操作,性能高1ms)
- 核心基於非阻塞IO多路復用機制(不處理、直接壓隊列)
- 單線程避免了多線程的頻繁上下文切換問題
- Redis有哪些數據類型?
- string
- hash
- 結構化數據,存放對象
- key=150
- value={
- “id”: 150,
- “name”: “zhangsan”,
- “age”: 20
- }
- list
- 列表性數據結構(微博粉絲)
- key=某大v
- value=[zhangsan, lisi, wangwu]
- set
- 無需集合,自動去重
- sorted set
- 去重&排序
- Redis的過期策略、內存淘汰機制
- 定期刪除+惰性刪除
- 每隔100ms抽取設置過期時間的key,檢查是否過期后刪除(定期)
- 獲取某個key時,redis檢查是否過期(惰性)
- 內存淘汰
1)noeviction:當內存不足以容納新寫入數據時,新寫入操作會報錯,這個一般沒人用吧,實在是太惡心了
2)allkeys-lru:當內存不足以容納新寫入數據時,在鍵空間中,移除最近最少使用的key(這個是最常用的)
3)allkeys-random:當內存不足以容納新寫入數據時,在鍵空間中,隨機移除某個key,這個一般沒人用吧,為啥要隨機,肯定是把最近最少使用的key給干掉啊
4)volatile-lru:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,移除最近最少使用的key(這個一般不太合適)
5)volatile-random:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,隨機移除某個key
6)volatile-ttl:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,有更早過期時間的key優先移除
- 手寫LRU算法
- Redis高並發、高可用、緩存一致性:
- Redis高並發:主從架構,一主多從,單主用來寫入數據,單機幾萬QPS,多從用來查詢數據,多個從實例可以提供每秒10W的QPS。如果需要緩存容納數據量大,用Redis集群。
- Redis高可用:如果你做主從架構部署,其實就是加上哨兵就可以了,就可以實現,任何一個實例宕機,自動會進行主備切換。
- 高並發
- 主從架構,實現讀寫分離(master寫,slave讀),水平擴容
- redis replication->主從架構->讀寫分離->水平擴容支撐讀高並發
- master開啟持久化
- 主從架構核心原理:
- master啟動線程,生成RDB快照,發送給slave
- 斷點續傳,記錄offset繼續復制
- 無磁盤復制:在內存創建rdb,發送給slave
- Redis replication(復制)的完整流程
- 數據同步的核心機制:
- master和slave都會維護一個offset
- backlog:用來全量復制中斷后的增量復制
- master run id:區分master(重啟或者數據發生變化)
- psync:psync runid offset,從節點使用此命令從master node進行復制
- 全量復制
- master生成快照
- 發送給slave node
- master在生成快照時,將所有新的寫命令緩存在內存中,slave保存rdb后,將新的寫命令復制
- slave在接收到rdb后,清空舊數據,加載rdb
- 如slave開啟aof,立即重寫aof
- 增量復制
- 全量復制中斷
- master用runid和offset從backlog中獲取到丟失數據,發送給slave
- heartbeat
- master默認每隔10秒發送一次heartbeat,salve node每隔1秒發送一個heartbeat
- 異步復制
- master每次接收到寫命令之后,現在內部寫入數據,然后異步發送給slave node
- 高可用性
- 基於哨兵(sentinal)的高可用,故障轉移(failover),主備切換
- 哨兵:
- 哨兵的作用:
- 集群監控,負責監控redis master和slave進程是否正常工作
- 消息通知,如果某個redis實例有故障,那么哨兵負責發送消息作為報警通知給管理員
- 故障轉移,如果master node掛掉了,會自動轉移到slave node上
- 配置中心,如果故障轉移發生了,通知client客戶端新的master地址
- 分布式,哨兵集群運行
- 主備切換數據丟失問題:
- 異步復制導致:
- master->slave復制是異步的,可能沒復制完,master宕機
- 集群腦裂導致:
- master主節點,出現異常性的相同工作的兩個節點
- client可以和舊master寫數據,恢復后舊master成為slave后數據丟失
- 解決:
- min-slaves-to-write 1
- min-slaves-max-lag 10
- 要求至少有1個slave,數據復制和同步的延遲不能超過10秒
- 如果說一旦所有的slave,數據復制和同步的延遲都超過了10秒鍾,那么這個時候,master就不會再接收任何請求了
- 哨兵的核心原理:
- sdown和odown轉換:
- sdown主觀宕機
- odown客觀宕機
- quorum指定數量
- 哨兵的自動發現機制:
- __sentinel__:hello這個channel里發送一個消息
- slave配置的自動糾正
- slave->master選舉算法
- 跟master斷開連接的時長
- slave優先級
- 復制offset
- run id
- Redis重啟數據恢復(數據持久化)
- 持久化的意義:數據備份、故障恢復
- 兩種持久化:RDB、AOF
- RDB:對Redis中數據執行周期性的持久化
- AOF:對於每條寫入命令作為日志,以append-only模式寫入一個日志文件中(更加完整)
- Redis重啟后,通過回放AOF日志中的指令重新構建數據集
- AOF大到一等程度后,rewrite操作,構建更小的AOF文件
- RDB優劣勢:
- 優點:(冷備)
- 生成多個數據文件,適合冷備。AOF需要寫腳本處理。
- RDB對Redis性能影響小,fork子進行進行RDB持久化
- RDB重啟恢復速度快
- 缺點:
- 恢復效果差,數據丟失
- fork子進程時,數據文件特別大,會對客戶端服務暫停數毫秒
- AOF優劣勢:
- 優點:
- 保護數據不丟失
- append-only寫入,沒有磁盤尋址開銷,寫入性能高,文件不易損壞
- AOF日志文件過大rewrite對客戶端讀寫性能影響小
- 日志文件可讀
- 缺點:
- 日志文件大
- 支持的寫QPS低
- 健壯性低
- 結合RDB和AOF同時使用
- Redis集群模式
- 分布式數據的核心算法,數據分布的算法:
- redis cluster——hash slot算法
- 每個master持有部分slot
- 節點間的內部通信機制
- 集群的元數據維護:集中式(ZooKeeper)、gossip
- gossip協議:元數據分散,降低壓力,更新有延遲(meet ping pong fail)
- 10000端口:用於節點間通信,為服務接口+10000,ping->pong
- 交換信息的內容:故障信息、節點的增刪、hash slot信息。。。
- jedis: Redis的java client客戶端
- smart jedis:本地維護hashslot->node的映射表,緩存,不需要節點moved重定向
- hash tag手動指定對應slot,set key1:{100}
- 高可用與主備切換:
- pfail(主觀宕機) fail(客觀宕機)
- 與哨兵類似
- Redis緩存雪崩與緩存穿透
- 緩存雪崩:
- 事前:Redis高可用(主從+哨兵、Redis Cluster)
- 事中:本地ehcache緩存(系統內部小緩存)+hystrix限流(限流降級組件)
- 事后:Redis持久化,快速恢復緩存數據
- 緩存穿透:
- 沒查到,寫空值到緩存中(set -999 UNKNOWN)
- 使用布隆過濾器或者壓縮filter提前攔截
- 緩存與數據庫雙寫數據一致性(讀寫串行化):
- 緩存+數據庫讀寫模式:cache aside pattern
- 讀。先緩存,再數據庫,讀完放入緩存
- 更新,先刪除緩存(緩存復雜計算得到、不會頻繁訪問),再更新數據庫
- 簡單場景:先刪緩存,在修改數據庫
- 復雜場景(刪除緩存,修改數據庫之前,讀后緩存舊數據,數據庫新修改)
- 數據庫與緩存更新讀取操作進行異步(線程)串行化
- 內存隊列放請求,一個隊列對應一個線程,串行進行操作
- 讀請求長時阻塞
- 隊列里寫請求過多(部署多個服務,分攤數據更新)
- 加多機器,每個機器上部署的服務實例處理更少的數據
- 多服務實例部署的請求路由
- 同一個商品的讀寫請求,路由到同一台機器上
- nginx,hash路由功能
- 熱點商品的路由問題(讀寫請求高)
- 商品更新清空緩存,導致讀寫並發,更新頻率不高影響不大
- Redis並發競爭問題:
- 多客戶端並發寫一個key
- CAS類的樂觀鎖方案
- zookeeper提供分布式鎖
- 每個value的時間戳要比緩存中的時間戳更新,才可以寫
- 生產環境中的Redis怎么部署