1. 在項目中是如何使用緩存的?爲何要用?不用行不行?用了可能會有哪些不良後果?redis
結合項目業務,主要兩個目的:高性能和高併發。緩存走內存,自然支持高併發。算法
不良後果:緩存
2. Redis的線程模型是什麼?網絡
Redis內部使用文件事件處理器(file event handler),這個處理器是單線程,全部Redis才叫單線程模型。多線程
採用IO 多路複用機制同時監聽多個 socket,將產生事件的 socket 壓入內存隊列中,事件分派器根據 socket 上的事件類型來選擇對應的事件處理器進行處理。架構
文件處理器包含4個部分:併發
socket併發不一樣操做,對應不一樣文件事件處理器,IO多路複用監聽多個socket,將產生事件的socket放入隊列排隊,less
文件事件分發器每次從隊列中取出一個socket,根據socket事件類型交給對應的事件處理器進行處理。dom
I/O多路複用:異步
在同一個線程裏面, 經過撥開關的方式,來同時傳輸多個I/O流。
Redis 通訊過程:
3. 爲何Redis單線程模型也能效率這麼高?
4. Redis 都有哪些數據類型?分別在哪些場景下使用比較合適?
數據類型:
string:最基本。普通Set get,簡單K-V緩存
set name zhangsan
hash:類map結構。能夠把結構化數據(對象)緩存。把簡單對象緩存,後續操做能夠只修改對象中某個字段。
key=user value={ "id": 100, "name":"zhangsan", "age",20 }
hset user id 100
hset user name zhangsan
hset user age 20
list:有序列表。微博大V粉絲以list格式放Redis緩存。
key=某大V value=[zhangsan, li, wangwu]
list的lrange命令:從某個元素開始讀取多少個元素,能夠基於list實現簡單分頁。
lrange mylist 0 -1
0開始位置,-1結束位置,結束位置爲-1時,表示列表的最後一個位置,即查看全部。
lpush, lpop //棧(FILO)
set: 無序,自動去重。
JVM的hashset能夠去重,可是多臺機器的呢?這個Redis的set適用於分佈式全集去重。能夠基於set作交集,並集,差集。查看大V共同好友等等。
sadd myset 1 // 添加元素1 smembers myset // 查看所有元素 sismember myset 2 // 判斷是否包含某個元素 srem myset 1 // 刪除元素1 srem myset 1 3 // 刪除某些元素 scrad myset // 查看元素個數 spop myset // 隨機刪除一個元素 smove testset myset abc // 將testset的元素abc移到myset sinter testset myset // 求兩個set交集 sunion testset myset // 求兩個元素並集 sdiff testset myset //求差集 在testset中而不宅myset的元素
sorted set: 排序的set,去重還能夠排序。例如寫入元素帶分數,自動根據分數排序。
zadd board 85 zhangsan zadd board 72 lisi zadd board 96 wangwu zadd board 63 zhaoliu zrevrange board 0 3 // 獲取排名前三 rev 改降序 zrank board zhaoliu
5. Redis的過時策略都有哪些?內存淘汰機制都有哪些?手寫一下 LRU 代碼實現?(往Redis寫入數據怎麼會沒了?)
緩存基於內存,內存有限,寫入超過內存容量,確定有數據失效。要麼設置過時時間,要麼被redis幹掉。
設置過時時間:
若是設置一批key只能存活1個小時,1個消息後,redis是怎麼對這批數據進行刪除的?
redis過時策略是:按期刪除 + 惰性刪除。
按期刪除:隨機抽取一些過時key來檢查和刪除。
爲何?若是不少key,10W個key設了過時時間,每隔幾百毫秒,去檢查,會形成高CPU負載。因此實際上redis時隨機抽取key來刪除。
惰性刪除:並非key到時間就被刪除,而是過時後查詢這個key時,redis查詢下這key過時了,刪除。不會返回值。
數據明明過時了,怎麼還佔用着內存?
可是實際上這仍是有問題的,若是按期刪除漏掉了不少過時 key,而後你也沒及時去查,也就沒走惰性刪除,此時會怎麼樣?
若是大量過時 key 堆積在內存裏,致使 redis 內存塊耗盡了,咋整?
解決方案是:走內存淘汰機制。
內存淘汰機制:
Redis內存淘汰機制以下:
手寫一個 LRU(Least Recent Used) 算法:
利用JDK實現一個Java版LRU.
class LRUCache<K, V> extends LinkedHashMap<K, V> { private final int CACHE_SIZE; // 傳遞進來最多能緩存多少數據 public LRUCache(int cacheSize) { // 設置一個hashmap的初始大小 // true 表示讓 linkedHashMap 按照訪問順序來進行排序,最近訪問的放在頭部,最老訪問的放在尾部 super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true); CACHE_SIZE = cacheSize; } @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { // 當 map中的數據量大於指定的緩存個數的時候,就自動刪除最老的數據。 return size() > CACHE_SIZE; } }
用到的LinkedHashMap的構造函數:
public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); this.accessOrder = accessOrder; }
參數說明:
initialCapacity: 初始容量大小,使用無參構造方法時,此值默認是16
loadFactor: 負載因子,使用無參構造方法時,此值默認是 0.75f
accessOrder: false: 基於插入順序 true: 基於訪問順序
重點看看accessOrder的做用,使用無參構造方法時,此值默認是false。
accessOrder = true: 基於訪問的順序,get一個元素後,這個元素被加到最後(使用了LRU 最近最少被使用的調度算法)
6. 如何保證 redis 的高併發和高可用?redis 的主從複製原理能介紹一下麼?redis 的哨兵原理能介紹一下麼?
Redis實現高併發:
主從架構,一主多從,讀寫分離,主節點負責寫,將數據同步到其餘從節點,從節點負責讀。
好處:輕鬆水平擴容,支撐高併發。
Redis Replication 的核心機制:
Master節點異步複製到Slave節點。
注意點:1. Master節點必須使用持久化。
2. Master的各類備份方案。如從備份中挑一份RDB去恢復Master,才能確保Master啓動時,是有數據的。
Redis 主從複製的核心原理:
Slave第一次連Master, 發送PSYNC給Master, 觸發full resynchronization全量複製。
Master生產快照RDB文件,同時在內存緩存最新數據(從客戶端接收最新寫命令),發送RDB給Slave。
Slave先把RDB寫磁盤,再加載到內存。而後Master再把內存中緩存的寫命令發到Slave,Slave再同步這些寫命令。
若是Slave與Master因網絡緣由斷開後,再鏈接時,Master只會發缺乏的部分數據到Slave。
主從複製的斷點續傳:
master內存中維護一個backlog,master和slave都有一個replica offset,還有一個master run id.
master run id:是一個節點的惟一id, Host + IP有可能能變動。
若是鏈接斷開,再連上後slave就從上次replica offset開始複製,若是沒有找到,就全量複製。
無磁盤化複製:
master在內存中建立RDB,不寫磁盤,發給Slave。
repl-diskless-sync yes
# 等待 5s 後再開始複製,由於要等更多 slave 從新鏈接過來
repl-diskless-sync-delay 5
複製的流程:
全量複製:
若是在複製期間,內存緩衝區持續消耗超過 64MB,或者一次性超過 256MB,那麼中止複製,複製失敗。
client-output-buffer-limit slave 256MB 64MB 60
heartbeat:
master默認每隔10秒發送一次heartbeat,slave每隔1秒發送一個 heartbeat。
Redis如何作到高可用:
redis的高可用架構,叫作failover故障轉移,也能夠叫作主備切換。
master在故障時,自動檢測,而且將某個slave自動切換爲master的過程,叫作主備切換。
Redis高可用,作主從架構,加上哨兵機制,實現主備切換。
Redis 哨兵集羣實現高可用:
sentinel(哨兵)主要功能:
哨兵至少須要 3 個實例,來保證本身的健壯性。
經典的 3 節點哨兵集羣:
配置 quorum=2,若是 M1 所在機器宕機了,那麼三個哨兵還剩下 2 個,S2 和 S3 能夠一致認爲 master 宕機了,而後選舉出一個來執行故障轉移,同時3個哨兵的majority是2,因此還剩下的2個哨兵運行着,就能夠容許執行故障轉移。
redis 哨兵主備切換的數據丟失問題:
兩種狀況和致使數據丟失:
1. 異步複製致使的數據丟失
有部分數據尚未來得及複製到slave,master就掛了,這部分數據就丟失。
2. 腦裂致使的數據丟失
腦裂,指某個master忽然脫離正常網絡,沒法與其餘slave鏈接,但master還在運行,
此時哨兵認爲master掛了,開啓選舉,將其餘slave選爲master。這個時候集羣內有2個mster, 就叫腦裂。
此時,客戶端會向舊master寫數據,當舊master恢復成一個新的slave掛到新master時,新master並無這段時間數據,就丟失了。
數據丟失問題的解決方案:
min-slaves-to-write 1
min-slaves-max-lag 10
要求至少有 1 個 slave(min-slaves-to-write),數據複製和同步的延遲不能超過 10 秒,若是超過了,master再也不接收請求。
有了 min-slaves-max-lag 這個配置,減小異步複製數據的丟失量。
若是不能繼續給指定數量slave發送數據,且slave超過10秒沒有給本身發ack,則拒絕客戶端寫請求,最多丟10秒數據。
sdown 和 odown 轉換機制:
sdown 是主觀宕機,就一個哨兵若是本身以爲一個 master 宕機了,那麼就是主觀宕機。
odown 是客觀宕機,若是 quorum 數量的哨兵都以爲一個 master 宕機了,那麼就是客觀宕機。
sdown: 若是一個哨兵 ping 一個 master,超過了 is-master-down-after-milliseconds 指定的毫秒數以後,就主觀認爲 master 宕機了
哨兵集羣的自動發現機制:
哨兵以前經過Redis的pub/sub實現互相發現。每隔兩秒,每一個哨兵會往本身監控的master+slave對應的__sentinel__:hello這個channel裏發送消息,
消息包括本身的host,ip, run id還有對master的監控配置。每一個哨兵都監聽本身監控的channel, 消費其餘哨兵的消息,感知其餘哨兵的存在。
slave 配置的自動糾正:
當一個slave成爲master時,哨兵確保其他slave鏈接到新的master上。
slave -> master選舉算法:
slave選舉考慮因素:
跟master斷開鏈接的時長,slave優先級,複製數據的offset,run id
若是一個 slave 跟 master 斷開鏈接的時間已經超過了 down-after-milliseconds 的 10 倍,外加 master 宕機的時長,那麼 slave 就被認爲不適合選舉爲 master。
(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
選舉排序:
1. slave priority越低,優先級越高。默認配置中slave-priority=100
2. slave priority相同時,看replica offset,哪一個複製的數據多,offset靠後,優先級高
3. 以上相同時,選run id小的slave
quorum和majority:
哨兵主備切換時,須要quorum數量的哨兵認爲sdown(主觀down),才能轉換爲odown。
這個時候選一個哨兵來作切換,這個哨兵還須要獲得majority哨兵的受權,才能正式執行切換。
configuration epoch(version):
哨兵會對一套redis的 master + slave 進行監控,有相應的監控配置。
執行切換的哨兵,會重新master(slave->master)獲得一個configuration epoch,是一個version號,每次切換的version號必須惟一。
若是選舉出的哨兵進行切換失敗,則其餘哨兵等待failover-timeout時間後,接替作切換,從新獲取一個新的configuration epoch做爲新的version。
configuration傳播:
哨兵完成切換後,在本身本地更新生成最新master配置,而後經過hello channel同步給其餘哨兵。
新的master配置是跟着新的version號,其餘哨兵都是根據版本號的大小來更新本身的master配置。
Part1總結:
redis高併發:主從架構,一主多從,通常項目足夠,一主寫入數據,單機幾W的QPS,多從用來查詢數據,多個實例能夠提供10W的QPS。
好比Redis主只有8G內存,其實最多隻能容納8G的數據量。若是要容量的數據量更大,就須要redis集羣,用集羣后能夠提供每秒幾十萬的讀寫併發。
redis高可用:若是主從架構,加上哨兵集羣就能夠實現。一個實例宕機,自動進行主備切換。