一,redis slave連接配接被master斷開,redis版本2.8.0
# I/O error trying to sync with MASTER: connection lost
# I/O error reading bulk count from MASTER: Connection reset by peer
redis log報錯如上,事故場景如下:
網站高峰期每分鐘70萬pv。redis中存放緩存資料,資料量大概6G左右,并且每條資料内容占用空間也較大。redis執行個體啟動時隻有最近三個月資料,expire時間為1個月,之後所有新抓取的資料expire時間為2h。master負責資料的寫入,slave叢集負責資料的讀。master由于機房網絡原因與slave斷開連接配接,slave在端口的幾秒之後連不上master,部分複制不成功。而這時當網站有通路slave沒有的資料,就會觸發抓取服務從mysql中抓取然後不斷寫入master,但是寫入的資料同步不到slave,所有這段時間master的寫入就會逐漸增大,平均每秒3w次寫入,最高每秒7w寫入。部分複制不成功之後,slave開始請求全同步,在slave接收master資料的過程中,連接配接斷開,slave log中報錯如上。
事故分析:
1,基礎知識
redis的client連接配接會有一個output buffer,用于存放對client請求的response data。而不同的client可以在redis配置檔案對buffer進行配置,當buffer增長到限制值的時候,就會報如上錯,master會主動端口連接配接。如下三行配置:
client-output-buffer-limit normal 0 0 0 #對一般的client
client-output-buffer-limit slave 256mb 64mb 60 #對redis的slave
client-output-buffer-limit pubsub 32mb 8mb 60 #對釋出/訂閱這種client
指令格式為:
# client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>
class有三種:normal,slave,pubsub
hard limit:當buffer的量達到硬限制之後,redis立即斷開與client的連接配接
soft limit 和 soft seconds:當buffer的在soft seconds秒内超出了soft limit,redis不會端口client連接配接。如果當buffer的在soft seconds秒之後,仍然超出了soft limit 的限制,則redis立即端口client連接配接。
資料部分同步:
從2.8開始,當Master和Slave之間的連接配接斷開之後,他們之間可以采用持續複制處理方式代替采用全量同步。
Master端為複制流維護一個記憶體緩沖區(in-memory backlog),記錄最近發送的複制流指令;同時,Master和Slave之間都維護一個複制偏移量(replication offset)和目前Master伺服器ID(Master run id)。當網絡斷開,Slave嘗試重連時:
a. 如果MasterID相同(即仍是斷網前的Master伺服器),并且從斷開時到目前時刻的曆史指令依然在Master的記憶體緩沖區中存在,則Master會将缺失的這段時間的所有指令發送給Slave執行,然後複制工作就可以繼續執行了;
b. 否則,依然需要全量複制操作;
對in-memory backlog的介紹:
是redis用于存儲更新指令的一塊buffer,在部分複制的時候Slave會請求Master從這塊buffer中擷取閃斷情況下丢失的更新操作。repl_backlog在redis啟動的時候初始化為NULL,當有Slave連接配接上來的時候,會被指向建立的buffer,預設為1024*1024(即1Mb)。repl_backlog_size表示該buffer的大小(預設1024*1024,即1Mb)。該buffer是作為一個環形緩存區使用的,當有資料超過buffer的大小以後就會重新從buffer的頭部開始寫入。repl_backlog_idx表示目前緩存資料的尾部(因為是環形buffer)。repl_backlog_off是全局緩存的偏移量,從開始緩存資料起一直在增長。如果Master一個Slave都沒有,則超過一段時間以後repl_backlog會被釋放,預設逾時時間是1小時。
完全同步:
無論是初次連接配接還是重新連接配接, 當建立一個從伺服器時, 從伺服器都将向主伺服器發送一個 SYNC 指令。
接到 SYNC 指令的主伺服器将開始執行 BGSAVE , 并在儲存操作執行期間, 将所有新執行的寫入指令都儲存到一個緩沖區裡面。當 BGSAVE 執行完畢後, 主伺服器将執行儲存操作所得的 .rdb 檔案發送給從伺服器, 從伺服器接收這個 .rdb 檔案, 并将檔案中的資料載入到記憶體中。之後主伺服器會以 Redis 指令協定的格式, 将寫指令緩沖區中積累的所有内容都發送給從伺服器。
2,結合事故場景:
1)slave在閃斷之後不能部分同步master:
slave與master斷開連接配接後,由于資料量大,master的in-memory backlog被很快寫滿并重新整理,故導緻部分複制失敗。
2)slave不能進行完全同步master:
redis slave到master進行資料完全複制,但是從複制開始到結束這段時間内,redis master的output buffer會積累大量的redis更新資料,導緻buffer超限,進而被redis master斷開連接配接。解決辦法調整如下指令的值即可:
client-output-buffer-limit slave
二,redis key過期政策
1,鍵空間通知使得用戶端可以通過訂閱頻道或模式, 來接收那些以某種方式改動了 Redis 資料集的事件。
以下是一些鍵空間通知發送的事件的例子:
1)所有修改鍵的指令。
2)所有接收到 LPUSH 指令的鍵。
3)資料庫中所有已過期的鍵。
事件通過 Redis 的訂閱與釋出功能(pub/sub)來進行分發, 是以所有支援訂閱與釋出功能的用戶端都可以在無須做任何修改的情況下, 直接使用鍵空間通知功能。
因為 Redis 目前的訂閱與釋出功能采取的是發送即忘(fire and forget)政策, 是以如果你的程式需要可靠事件通知(reliable notification of events), 那麼目前的鍵空間通知可能并不适合你: 當訂閱事件的用戶端斷線時, 它會丢失所有在斷線期間分發給它的事件。
2,過期通知的發送時間
Redis 使用以下兩種方式删除過期的鍵:
1)當一個鍵被通路時,程式會對這個鍵進行檢查,如果鍵已經過期,那麼該鍵将被删除。
2)redis會每秒10次的做如下事情:
步驟1:從設定過期的key集合中随機抽100個key
步驟2:删除100個key中所有過期的key
步驟3:如果100個key中有25個以上的key過期了,就從步驟1繼續開始。
當過期鍵被以上兩個程式的任意一個發現、 并且将鍵從資料庫中删除時, Redis 會産生一個 expired 通知。Redis 并不保證生存時間(TTL)變為 0 的鍵會立即被删除: 如果程式沒有通路這個過期鍵, 或者帶有生存時間的鍵非常多的話, 那麼在鍵的生存時間變為 0 , 直到鍵真正被删除這中間, 可能會有一段比較顯著的時間間隔。
是以, Redis 産生 expired 通知的時間為過期鍵被删除的時候, 而不是鍵的生存時間變為 0 的時候。
3,key的過期屬性修改
1),當redis中一個key有expire屬性,我們隻能用修改value的方式才能清楚expire屬性,如set,del指令。想rename指令,不能呢個清楚expire屬性。
2),當在一個已經有expire屬性的key上,再使用expire屬性的話,過期時間将會被更新。
三,redis高可用問題解決
1,Twemproxy
twemproxy,也叫nutcraker,是一個用C語言實作的快速的單線程redis和memcache代理代理程式。twemproxy官方給出的性能測試結果為,使用此代理,隻會有20%的性能損耗。proxy主要功能如下:
1)支援失敗節點自動删除
可以設定重新連接配接該節點的時間
可以設定連接配接多少次之後删除該節點
該方式适合作為cache存儲
2)支援設定HashTag
通過HashTag可以自己設定将帶有相同tag的不同的KEY hash到同一個redis執行個體上去。
3)減少與redis的直接連接配接數
保持與redis的長連接配接
可設定代理與背景每個redis連接配接的數目
4)自動分片到後端多個redis執行個體上
多種hash算法
可以設定後端執行個體的權重
5)避免單點問題
可以平行部署多個代理層,client自動選擇可用的一個
6)支援redis pipelining request
但是對于事物機制
7)支援狀态監控
可設定狀态監控ip和端口,通路ip和端口可以得到一個json格式的狀态資訊串
可設定監控資訊重新整理間隔時間
8)高吞吐量
連接配接複用,記憶體複用。
将多個連接配接請求,組成reids pipelining統一向redis請求。
nutcraker的xml配置案例如下:
mpc_12251:
listen: 0.0.0.0:12251
hash: fnv1a_64
distribution: ketama
auto_eject_hosts: true
redis: true
timeout: 1000
server_retry_timeout: 60000
server_failure_limit: 2
servers:
- 10.13.83.108:12251:1
- 10.13.83.118:12251:1
- 10.13.83.128:12251:1
- 10.13.83.40:12251:1
- 10.13.83.52:12251:1
- 10.13.83.78:12251:1
項目位址:https://github.com/twitter/twemproxy
2,sentinel
這是redis中自帶的管理軟體,主要執行是三個任務:
1)監控(Monitoring): Sentinel 會不斷地檢查你的主伺服器和從伺服器是否運作正常。
2)提醒(Notification): 當被監控的某個 Redis 伺服器出現問題時, Sentinel 可以通過 API 向管理者或者其他應用程式發送通知。
3)自動故障遷移(Automatic failover): 當一個主伺服器不能正常工作時, Sentinel 會開始一次自動故障遷移操作, 它會将失效主伺服器的其中一個從伺服器更新為新的主伺服器, 并讓失效主伺服器的其他從伺服器改為複制新的主伺服器; 當用戶端試圖連接配接失效的主伺服器時, 叢集也會向用戶端傳回新主伺服器的位址, 使得叢集可以使用新主伺服器代替失效伺服器。
Redis Sentinel 是一個分布式系統, 你可以在一個架構中運作多個 Sentinel 程序(progress), 這些程序使用流言協定(gossip protocols)來接收關于主伺服器是否下線的資訊, 并使用投票協定(agreement protocols)來決定是否執行自動故障遷移, 以及選擇哪個從伺服器作為新的主伺服器。
具體參考文章:
http://redis.readthedocs.org/en/latest/topic/sentinel.html
sentinel的配置檔案,配置選項不多,在sentinel.conf檔案中都有詳細的說明
四,作為Nosql的優勢
1,性能方面
由于redis和memcached都是把資料直接存放在記憶體,故速度都非常快
2,提供的資料結構
memcache資料結構單一,隻能提供簡單的key/value方式
redis資料結構豐富,有list,set,hash等資料結構的存儲方式
3,資料可靠性
memcache不支援,通常用在做緩存,提升性能,不支援資料持久化
redis支援(快照、AOF):依賴快照進行持久化,aof增強了可靠性的同時,對性能有所影響。redis的資料會儲存在rdb資料檔案中,等redis重新開機之後,資料會從rdb檔案進行加載
4,可用性
Memcache本身沒有資料備援機制,隻能做緩存
redis支援master-slave架構,版本2.8.0在某些情況下支援增量複制,可以用sentinel來實作高可用,實作故障自動切換。并且,redis3.0系列正在增加叢集功能,相信叢集redis的功能會更好的給使用者提供高可用的解決方案。