redis持久化、可用性及壓力過大問題的解決


通過學習整理其他優秀資源,本文解決三個問題:

  • redis如何持久化?
  • 生產環境中,redis的可用性如何保證?
  • redis中遇到存到存儲上限如何解決?

一、redis持久化

redis是基於內存的, 內存特點是斷電易失。就必然涉及到持久化操作。redis持久化有兩種方式:快照(rdb日志(aof

1.快照和日志

快照類似於序列化和反序列化過程。把數據序列化成二進制放到磁盤,加載時把磁盤當中的數據直接反序列化,讀過來,不需要執行什么。

日志:redis每用一個操作(寫、修改),就把操作寫到日志文件里去。日志里存放的是操作指令(快照存的是數據自身)。日志是恢復的時候,讀到日志的東西還要去執行,變成數據。因此,日志比快照恢復的慢。但快照比日志丟東西丟得多(兩次快照之間掛機了,那么中間的東西會丟失。)

日志有三個級別:

  • 每操作(就是每次操作就保存數據fsync做數據同步)。好處:數據完整性比較強。缺點是使redis性能下降。
  • 每秒鍾

(定時器每到1秒,就刷寫)最多也就丟1秒鍾的數據。操作系統os的緩沖(緩沖滿了,os自己會刷寫),所以保證至多丟一個buffer(如果1秒之內buffer滿了,os自己刷寫;如果1s內buffer沒滿,定時器設置1s,也會刷寫。因此最多丟一個buffer的內容)

  • os緩沖刷寫,丟一個buffer的內容
一般是寫滿buffer才寫入磁盤:程序要調系統調用,即調用內核,操作系統里有buffer緩沖區,程序要向磁盤中寫數據,要先把數據交給內核的緩沖區,緩沖區滿了之后,
內核才會把數據刷新到磁盤。程序可以flush緩沖區,讓內核現在就把數據從緩沖區存到磁盤。(這就是為什么在關閉之前要flush,不然可能丟東西。)
調用fsync()函數可讓操作系統現在把緩沖區的數據寫到磁盤上,而不是等待緩沖區裝滿數據。

  

redis的日志默認級別是每秒鍾。追求完整性和一致性的時候,就要把級別調高至每操作

快照rdb的優勢是恢復的快但丟失的多,日志aof的優勢是恢復的數據更完整但恢復速度慢。

2.使用方法

早期是默認開啟快照rdb。因為不會頻繁的對磁盤有io操作。那時用於緩存,而不是數據庫,丟失一些數據也無所謂。也可以手動開啟日志aof,但開啟了快照rdb也就不起作用了。

后來(redis4.0)可以混合使用(各有優缺點),日志aof中包含一個歷史狀態的快照rdb。比如說8點的快照,然后后面追加的日志。這個混合的還叫做aof日志。(全是日志比較慢,混合的是周期(比如每個小時)的把前面的rof變成rdb(二進制),然后再把后面的日志追加進去)。整體性能高於上面兩者。

 

在redis配置文件中, APPEND ONLY MODE下 的appendonly默認是no,即關閉的。可以打開yes,那么就開啟了aof。目的文件是appendfilename "appendonly.aof"。三個級別:# appendfsync always(每筆操作都向磁盤刷寫)、appendfsync everysec(每秒鍾刷寫)和# appendfsync no(程序不觸發,讓操作系統自己觸發刷寫)。

新版本在配置文件的APPEND ONLY MODE最后有個aof-use-rdb-preamble yes表示混合模式。在SNAPSHOTTING下,有個dir ./表示存放的路徑是當前文件夾。

 

假如只是aof(沒有rdb)的模式(開啟了aofappendonly yes,默認是rdb,關閉混合的aof-use-rdb-preamble no),當在redis里操作時,在./文件夾下的appendonly.aof里就有指令:*2代表后面有兩個東西。S6代表后面的單詞有6個字符。redis重啟時要讀取這個文件,然后要把里面set這個過程執行一遍,內存里面才會有數據。

 

有個問題,如果刪除k1,再setk1,一直這么操作,.aof文件會變大,但內存數據反而很少,所以有個壓縮機制,將創建和刪除抵消掉。比如都對k1進行set操作(set k1 aaa; set k1 222.....)日志只會記錄最后一條。BGREWRITEAOF通過后台的方式重寫aof(重寫會創建一個當前 AOF 文件的體積優化版本)。

  

當開啟了混合的(aof使用rdb),aof-use-rdb-preamble yes,此時設置值,再BGREWRITEAOF就.aof文件就由指令變成rdb二進制文件(序列化內容:rdb原生的文件做個序列化放到里面了)。這個時候再設置值,沒有BGREWRITEAOF再打開.aof,就在序列化的內容后面追加剛才設置值的指令。redis重啟時,.aof二進制內容直接讀,后面的指令就執行。---》比單單執行所有指令快很多。

 

總之,就是利用鏡像的速度,利用日志的全量(完整性)。

 

二、redis可用性

redis用在生產系統中,有兩種問題:可用性(單點故障,會掛);壓力(存儲上限:存不進去或者高並發訪問不過來)。也適用其他服務。

解決可用性問題,最先想到的是集群。但更細來說,應該是主從/主備集群。壓力過大可以使用分片集群或者代理集群。都是集群,因此單單說集群就不恰當。

 

單點掛了,那么就多准備幾個備份的(多機),首先就涉及到這幾個redis的數據同步問題,注意這是主從/主備,才會有數據同步,而分片集群是不會有數據同步這概念(分片是每個redis存不同的數據)

數據同步

數據同步方式:(1)強一致性:客戶端給主寫了,主先不給客戶端回送消息,而是讓備也去寫數據,備寫成功就通知主,然后主再通知客戶端。(強一致性就是主備內容絕對一致)。問題:主這邊沒問題,而主和備之間有問題(延時、備掛了),那么會阻塞客戶端,就是強制一致性破壞可用性(從外界來看應用不可用了)問題。

(2)弱一致性:客戶端給主寫了,主立即告訴客戶端成功,然后異步的通知給備(中間有些地方可能有問題)。主掛掉后,備接管可能之前的數據有些丟失,因此叫弱一致性。

(3)最終一致性:假設有個黑盒,具備自身是可靠的,是集群的(不會掛),主先把數據寫到可靠的黑盒中,這個過程是同步和強一致的,然后不可靠的其他redis在可用的時候就從黑盒中讀回內容,那么備就會最終具備主掛機之前的狀態。redis沒有使用最終一致性。

redis主從復制默認工作在弱一致性級別。(強一致性也可開啟,但響應速度退化到關系型數據庫)。redis做緩存場景比較多,弱一致性是接受的(丟失點數據)

 redis基於單點故障需要主從復制(集群方式),特點是節點間數據是全量的(每個redis里的內容是一樣的,全部都有)。

 

三、負載問題

1.akf划分

redis實例中數據太多(壓力過大)怎末辦?(1)根據業務划分數據到不同的redis實例,不同維度存儲。(比如,一個維度是用戶信息的redis的主從,再一個維度是購物車的,再一個是商品詳情的.....)。(2)分片:將一個redis實例的全量數據分散到不同實例,具體分法可以是hash取模(單號hash模上實例的個數),也可以按照區間划分(0-100,101-200...)

 

 

x軸解決問題:冗余數據,提高可靠性。y軸解決問題:業務划分,分治(不同業務治理自己的數據)。z軸對同類數據做負載均衡或者是分片sharding。(相同業務分片)。

這個划分划小的過程就是akf。akf是微服務划分的拆分原則。

2.分片

每個redis存放1/n的數據,但單個redis也可能掛,因此針對單個redis做主從復制。因此分片和主從要混合使用

實現方式:
在客戶端實現。任何大的數據流經客戶端后,就通過算法(映射算法)最終流向不同的redis實例中。弊端:多個客戶端同時消耗這批數據,就誰涉及到算法同步問題,因此在部署和更新版本上有一定難度;客戶端是個service,會消耗指令集占用cpu內存等-----》拆出去好點。

代理集群。多個客戶端里都有個輕量級線程,把數據扔給代理,代理里面有算法,代理給不同的redis寫不同的數據。如果代理壓力大,也可以做個負載均衡。

兩個問題:數據分片存儲,如果現在是分片2個節點,但未來又要擴展到更多節點,這個擴展好做嗎?客戶端並發時,代理層處理負載均衡,那么redis能不能不需要代理層,自發滿足負載均衡?

 

解決分片的問題:

redis提出一個槽位(虛擬)的概念。第一步:假設槽位數是1000,算法對數據的key進行hash計算,然后模的是槽位1000,而不是物理節點數,得到key的模數值(0-999)。第二步:用物理節點綁定(映射)不同的模數值。比如0-500的模數值放到node1500-999映射到node2。好處:未來增加物理節點,將前面node里的槽位屬於新增節點的遷移到新增節點。

應用場景:HBASE和Elastic SearchHBASE里有個預分區(HBASE是分布式的,數據分布在不同節點),剛開始建表時分100個小表,未來數據放在小表里。Elastic Search有個分片的概念,分片分多點,以后數據量增大,直接增加物理台數,就可以動態擴展集群的分布式能力。(動態擴縮容)

 

解決負載的問題:

無主模型/無主集群。(ES在使用環節也是無主的:應用業務接入的尋址操作是無主的,即客戶端可以發送請求給任何一節點,該節點就會變成代理節點,幫助把客戶端的請求分散到所有節點或者找到正確的節點)。redis也是。算法在每一台redis實例里,實例里除了有算法,還有一個映射(節點和槽位的對應)。客戶端現在隨便連個節點,該節點的算法算出區間,然后對應映射,找到正確的節點,再告訴客戶端,客戶端就重新連到正確的節點(這樣客戶端的增刪改查就在正確的節點上。)。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM