Redis 高并发 (史上最全)
Redis的架構模式
單機版
特點:簡單
問題:
1、內存容量有限 2、處理能力有限 3、無法高可用。
主從復制
Redis 的復制(replication)功能允許用戶根據一個 Redis 服務器來創建任意多個該服務器的復制品,其中被復制的服務器為主服務器(master),而通過復制創建出來的服務器復制品則為從服務器(slave)。 只要主從服務器之間的網絡連接正常,主從服務器兩者會具有相同的數據,主服務器就會一直將發生在自己身上的數據更新同步 給從服務器,從而一直保證主從服務器的數據相同。
特點:
1、master/slave 角色
2、master/slave 數據相同
3、降低 master 讀壓力在轉交從庫
問題:
無法保證高可用
沒有解決 master 寫的壓力
哨兵
Redis sentinel 是一個分布式系統中監控 redis 主從服務器,并在主服務器下線時自動進行故障轉移。其中三個特性:
監控(Monitoring): Sentinel 會不斷地檢查你的主服務器和從服務器是否運作正常。
提醒(Notification): 當被監控的某個 Redis 服務器出現問題時, Sentinel 可以通過 API 向管理員或者其他應用程序發送通知。
自動故障遷移(Automatic failover): 當一個主服務器不能正常工作時, Sentinel 會開始一次自動故障遷移操作。
特點:
1、保證高可用
2、監控各個節點
3、自動故障遷移
缺點:主從模式,切換需要時間丟數據
沒有解決 master 寫的壓力
集群模式:
業界主流的Redis集群化方案主要包括以下幾個:
客戶端分片
代理分片
服務端分片
代理分片包括:
Codis
Twemproxy
服務端分片包括:
Redis Cluster
它們還可以用是否中心化來劃分,其中客戶端分片、Redis Cluster屬于無中心化的集群方案,Codis、Tweproxy屬于中心化的集群方案。
是否中心化是指客戶端訪問多個Redis節點時,是直接訪問還是通過一個中間層Proxy來進行操作,直接訪問的就屬于無中心化的方案,通過中間層Proxy訪問的就屬于中心化的方案,它們有各自的優劣,下面分別來介紹。
1 客戶端分片
客戶端分片主要是說,我們只需要部署多個Redis節點,具體如何使用這些節點,主要工作在客戶端。
客戶端通過固定的Hash算法,針對不同的key計算對應的Hash值,然后對不同的Redis節點進行讀寫。
客戶端分片集群模式 需要業務開發人員事先評估業務的請求量和數據量,然后讓DBA部署足夠的節點交給開發人員使用即可。
這個方案的優點是部署非常方便,業務需要多少個節點DBA直接部署交付即可,剩下的事情就需要業務開發人員根據節點數量來編寫key的請求路由邏輯,制定一個規則,一般采用固定的Hash算法,把不同的key寫入到不同的節點上,然后再根據這個規則進行數據讀取。
可見,它的缺點是業務開發人員使用Redis的成本較高,需要編寫路由規則的代碼來使用多個節點,而且如果事先對業務的數據量評估不準確,后期的擴容和遷移成本非常高,因為節點數量發生變更后,Hash算法對應的節點也就不再是之前的節點了。
所以后來又衍生出了一致性哈希算法,就是為了解決當節點數量變更時,盡量減少數據的遷移和性能問題。
這種客戶端分片的方案一般用于業務數據量比較穩定,后期不會有大幅度增長的業務場景下使用,只需要前期評估好業務數據量即可。
客戶端分片的特點
這實際上是一種靜態分片技術。Redis 實例的增減,都得手工調整分片程序。基于此分片機制的開源產品,現在仍不多見。
這種分片機制的性能比代理式更好(少了一個中間分發環節)。但缺點是升級麻煩,對研發人員的個人依賴性強——需要有較強的程序開發能力做后盾。如果主力程序員離職,可能新的負責人,會選擇重寫一遍。
所以,這種方式下,可運維性較差。出現故障,定位和解決都得研發和運維配合著解決,故障時間變長。
這種方案,難以進行標準化運維,不太適合中小公司(除非有足夠的 DevOPS)。
2 代理分片 (代理型集群)
這種方案,將分片工作交給專門的代理程序來做。代理程序接收到來自業務程序的數據請求,根據路由規則,將這些請求分發給正確的 Redis 實例并返回給業務程序。
這種機制下,一般會選用第三方代理程序(而不是自己研發),因為后端有多個 Redis 實例,所以這類程序又稱為分布式中間件。
這樣的好處是,業務程序不用關心后端 Redis 實例,運維起來也方便。雖然會因此帶來些性能損耗,但對于 Redis 這種內存讀寫型應用,相對而言是能容忍的。
Codis
隨著業務和技術的發展,人們越發覺得,當我需要使用Redis時,我們不想關心集群后面有多少個節點,我們希望我們使用的Redis是一個大集群,當我們的業務量增加時,這個大集群可以增加新的節點來解決容量不夠用和性能問題。
這種方式就是服務端分片方案,客戶端不需要關心集群后面有多少個Redis節點,只需要像使用一個Redis的方式去操作這個集群,這種方案將大大降低開發人員的使用成本,開發人員可以只需要關注業務邏輯即可,不需要關心Redis的資源問題。
多個節點組成的集群,如何讓開發人員像操作一個Redis時那樣來使用呢?這就涉及到多個節點是如何組織起來提供服務的,一般我們會在客戶端和服務端中間增加一個代理層,客戶端只需要操作這個代理層,代理層實現了具體的請求轉發規則,然后轉發請求到后面的多個節點上,因此這種方式也叫做中心化方式的集群方案,Codis就是以這種方式實現的集群化方案。
Proxy集群模式
Codis是由國人前豌豆莢大神開發的,采用中心化方式的集群方案。因為需要代理層Proxy來進行所有請求的轉發,所以對Proxy的性能要求很高,Codis采用Go語言開發,兼容了開發效率和性能。
Codis包含了多個組件:
codis-proxy:主要負責對請求的讀寫進行轉發
codis-dashbaord:統一的控制中心,整合了數據轉發規則、故障自動恢復、數據在線遷移、節點擴容縮容、自動化運維API等功能
codis-group:基于Redis 3.2.8版本二次開發的Redis Server,增加了異步數據遷移功能
codis-fe:管理多個集群的UI界面
可見Codis的組件還是挺多的,它的功能非常全,除了請求轉發功能之外,還實現了在線數據遷移、節點擴容縮容、故障自動恢復等功能。
Codis的Proxy就是負責請求轉發的組件,它內部維護了請求轉發的具體規則,Codis把整個集群劃分為1024個槽位,在處理讀寫請求時,采用crc32Hash算法計算key的Hash值,然后再根據Hash值對1024個槽位取模,最終找到具體的Redis節點。
Codis最大的特點就是可以在線擴容,在擴容期間不影響客戶端的訪問,也就是不需要停機。這對業務使用方是極大的便利,當集群性能不夠時,就可以動態增加節點來提升集群的性能。
為了實現在線擴容,保證數據在遷移過程中還有可靠的性能,Codis針對Redis進行了修改,增加了針對異步遷移數據相關命令,它基于Redis 3.2.8進行開發,上層配合Dashboard和Proxy組件,完成對業務無損的數據遷移和擴容功能。
因此,要想使用Codis,必須使用它內置的Redis,這也就意味著Codis中的Redis是否能跟上官方最新版的功能特性,可能無法得到保障,這取決于Codis的維護方,目前Codis已經不再維護,所以使用Codis時只能使用3.2.8版的Redis,這是一個痛點。
另外,由于集群化都需要部署多個節點,因此操作集群并不能完全像操作單個Redis一樣實現所有功能,主要是對于操作多個節點可能產生問題的命令進行了禁用或限制,具體可參考Codis不支持的命令列表。
但這不影響它是一個優秀的集群化方案,由于我司使用Redis集群方案較早,那時Redis Cluster還不夠成熟,所以我司使用的Redis集群方案就是Codis。目前我的工作主要是圍繞Codis展開的,我們公司對Codis進行了定制開發,還對Redis進行了一些改造,讓Codis支持了跨多個數據中心的數據同步,因此我對Codis的代碼比較熟悉,后面會專門寫一些文章來剖析Codis的實現原理,學習它的原理,這對我們理解分布式存儲有很大的幫助!
Twemproxy
Twemproxy是由Twitter開源的集群化方案,它既可以做Redis Proxy,還可以做Memcached Proxy。
它的功能比較單一,只實現了請求路由轉發,沒有像Codis那么全面有在線擴容的功能,它解決的重點就是把客戶端分片的邏輯統一放到了Proxy層而已,其他功能沒有做任何處理。
Tweproxy推出的時間最久,在早期沒有好的服務端分片集群方案時,應用范圍很廣,而且性能也極其穩定。
但它的痛點就是無法在線擴容、縮容,這就導致運維非常不方便,而且也沒有友好的運維UI可以使用。
Twemproxy 是一個 Twitter 開源的一個 redis 和 memcache 快速/輕量級代理服務器; Twemproxy 是一個快速的單線程代理程序,支持 Memcached ASCII 協議和 redis 協議。
特點:
1、多種 hash 算法:MD5、CRC16、CRC32、CRC32a、hsieh、murmur、Jenkins
2、支持失敗節點自動刪除
3、后端 Sharding 分片邏輯對業務透明,業務方的讀寫方式和操作單個 Redis 一致
缺點:
增加了新的 proxy,需要維護其高可用。failover 邏輯需要自己實現,其本身不能支持故障的自動轉移可擴展性差,進行擴縮容都需要手動干預
Codis就是因為在這種背景下才衍生出來的。
3 服務端分片(直連型集群)
采用中間加一層Proxy的中心化模式時,這就對Proxy的要求很高,因為它一旦出現故障,那么操作這個Proxy的所有客戶端都無法處理,要想實現Proxy的高可用,還需要另外的機制來實現,例如Keepalive。
而且增加一層Proxy進行轉發,必然會有一定的性能損耗,那么除了客戶端分片和上面提到的中心化的方案之外,還有比較好的解決方案么?
直連型集群。從redis 3.0之后版本支持redis-cluster集群,Redis-Cluster采用無中心結構,每個節點保存數據和整個集群狀態,每個節點都和其他所有節點連接。
Redis Cluster
Redis官方推出的Redis Cluster另辟蹊徑,它沒有采用中心化模式的Proxy方案,而是把請求轉發邏輯一部分放在客戶端,一部分放在了服務端,它們之間互相配合完成請求的處理。
Redis Cluster是在Redis 3.0推出的,早起的Redis Cluster由于沒有經過嚴格的測試和生產驗證,所以并沒有廣泛推廣開來。也正是在這樣的背景下,業界衍生了出了上面所說的中心化集群方案:Codis和Tweproxy。
但隨著Redis的版本迭代,Redis官方的Cluster也越來越穩定,更多人開始采用官方的集群化方案。也正是因為它是官方推出的,所以它的持續維護性可以得到保障,這就比那些第三方的開源方案更有優勢。
Redis Cluster沒有了中間的Proxy代理層,那么是如何進行請求的轉發呢?
Redis把請求轉發的邏輯放在了Smart Client中,要想使用Redis Cluster,必須升級Client SDK,這個SDK中內置了請求轉發的邏輯,所以業務開發人員同樣不需要自己編寫轉發規則,Redis Cluster采用16384個槽位進行路由規則的轉發。
沒有了Proxy層進行轉發,客戶端可以直接操作對應的Redis節點,這樣就少了Proxy層轉發的性能損耗。
Redis Cluster也提供了在線數據遷移、節點擴容縮容等功能,內部還內置了哨兵完成故障自動恢復功能,可見它是一個集成所有功能于一體的Cluster。因此它在部署時非常簡單,不需要部署過多的組件,對于運維極其友好。
Redis Cluster在節點數據遷移、擴容縮容時,對于客戶端的請求處理也做了相應的處理。當客戶端訪問的數據正好在遷移過程中時,服務端與客戶端制定了一些協議,來告知客戶端去正確的節點上訪問,幫助客戶端訂正自己的路由規則。
雖然Redis Cluster提供了在線數據遷移的功能,但它的遷移性能并不高,遷移過程中遇到大key時還有可能長時間阻塞遷移的兩個節點,這個功能相較于Codis來說,Codis數據遷移性能更好。關注公眾號Java技術棧可以閱讀一些集群搭建實戰的文章。
現在越來越多的公司開始采用Redis Cluster,有能力的公司還在它的基礎上進行了二次開發和定制,來解決Redis Cluster存在的一些問題,我們期待Redis Cluster未來有更好的發展。
特點:
1、無中心架構(不存在哪個節點影響性能瓶頸),少了 proxy 層。
2、數據按照 slot 存儲分布在多個節點,節點間數據共享,可動態調整數據分布。
3、可擴展性,可線性擴展到 1000 個節點,節點可動態添加或刪除。
4、高可用性,部分節點不可用時,集群仍可用。通過增加 Slave 做備份數據副本
5、實現故障自動 failover,節點之間通過 gossip 協議交換狀態信息,用投票機制完成 Slave到 Master 的角色提升。
缺點:
1、資源隔離性較差,容易出現相互影響的情況。
2、數據通過異步復制,不保證數據的強一致性
集群的必要性
所謂的集群,就是通過添加服務器的數量,提供相同的服務,從而讓服務器達到一個穩定、高效的狀態。
1.1.1 使用 redis 集群的必要性
問題:我們已經部署好了redis,并且能啟動一個redis,實現數據的讀寫,為什么還要學習redis集群?
答:(1)單個redis存在不穩定性。當redis服務宕機了,就沒有可用的服務了。
(2)單個redis的讀寫能力是有限的。
總結:redis集群是為了強化redis的讀寫能力。
1.1.2 如何學習redis集群
說明:(1)redis集群中,每一個redis稱之為一個節點。
? (2)redis集群中,有兩種類型的節點:主節點(master)、從節點(slave)。
? (3)redis集群,是基于redis主從復制實現。
?
所以,學習redis集群,就是從學習redis主從復制模型開始的。
redis主從復制
1.1 概念
? 主從復制模型中,有多個redis節點。
? 其中,有且僅有一個為主節點Master。從節點Slave可以有多個。
只要網絡連接正常,Master會一直將自己的數據更新同步給Slaves,保持主從同步。
1.1 特點
(1)主節點Master可讀、可寫.
(2)從節點Slave只讀。(read-only)
因此,主從模型可以提高讀的能力,在一定程度上緩解了寫的能力。因為能寫仍然只有Master節點一個,可以將讀的操作全部移交到從節點上,變相提高了寫能力。
1.1 基于配置實現
1.1.1 需求
| 主節點 | 6380 |
|---|---|
| 從節點(兩個) | 6381、6382 |
1.1.2 配置步驟
(1)在/usr/local目錄下,創建一個/redis/master-slave目錄
[root@node0719 local]# mkdir -p redis/master-slave
(2)在master-slave目錄下,創建三個子目錄6380、6381、6382
[root@node0719 master-slave]# mkdir 6380 6381 6382
(3)依次拷貝redis解壓目錄下的redis.conf配置文件,到這三個子目錄中。
[root@node0719 master-slave]# cp /root/redis-3.2.9/redis.conf ./6380/
[root@node0719 master-slave]# cp /root/redis-3.2.9/redis.conf ./6381/
[root@node0719 master-slave]# cp /root/redis-3.2.9/redis.conf ./6382/
(4)進入6380目錄,修改redis.conf,將port端口修改成6380即可。
[root@node0719 master-slave]# cd ./6380
[root@node0719 6380]# vim redis.conf
(5)進入6381目錄,修改redis.conf,將port端口改成6381,同時指定開啟主從復制。
[root@node0719 6380]# cd ../6381
[root@node0719 6381]# vim redis.conf
(6)進入6382目錄,修改redis.conf,將port端口改成6382,同時指定開啟主從復制。
[root@node0719 6380]# cd ../6382
[root@node0719 6381]# vim redis.conf
1.1.1 測試
(1)打開三個xshell窗口,在每一個窗口中,啟動一個redis節點。查看日志輸出。(不要改成后臺模式啟動,看不到日志,不直觀)
[root@node0719 master-slave]# cd 6380 && redis-server ./redis.conf
[root@node0719 master-slave]# cd 6381 && redis-server ./redis.conf
[root@node0719 master-slave]# cd 6382 && redis-server ./redis.conf
(2)另外再打開三個xshell窗口,在每一個窗口中,登陸一個redis節點
[root@node0719 ~]# redis-cli -p 6380
[root@node0719 ~]# redis-cli -p 6381
[root@node0719 ~]# redis-cli -p 6382
(3)在主節點6380上,進行讀寫操作,操作成功
[root@node0719 ~]# redis-cli -p 6380
127.0.0.1:6380> set user:name zs
OK
127.0.0.1:6380> get user:name
"zs"
127.0.0.1:6380>
(4)在從節點6381上
讀操作執行成功,并且成功從6380上同步了數據
[root@node0719 ~]# redis-cli -p 6381
127.0.0.1:6381> get user:name
"zs"
寫操作執行失敗。(從節點,只能讀,不能寫)
127.0.0.1:6381> set user:age 18
(error) READONLY You can't write against a read only slave.
Sentinel哨兵模式
1.1 主從模式的缺陷
當主節點宕機了,整個集群就沒有可寫的節點了。
由于從節點上備份了主節點的所有數據,那在主節點宕機的情況下,如果能夠將從節點變成一個主節點,是不是就可以解決這個問題了呢?
答:是的,這個就是Sentinel哨兵的作用。
1.2 哨兵的任務
Redis 的 Sentinel 系統用于管理多個 Redis 服務器(instance), 該系統執行以下三個任務:
監控(Monitoring****): Sentinel 會不斷地檢查你的主服務器和從服務器是否運作正常。
提醒(Notification****): 當被監控的某個 Redis 服務器出現問題時, Sentinel 可以通過 API 向管理員或者其他應用程序發送通知。
自動故障遷移(Automatic failover****): 當一個主服務器不能正常工作時, Sentinel 會開始一次自動故障遷移操作, 它會進行選舉,將其中一個從服務器升級為新的主服務器, 并讓失效主服務器的其他從服務器改為復制新的主服務器; 當客戶端試圖連接失效的主服務器時, 集群也會向客戶端返回新主服務器的地址, 使得集群可以使用新主服務器代替失效服務器。
1.2.1 監控(Monitoring)
(1)Sentinel可以監控任意多個Master和該Master下的Slaves。(即多個主從模式)
(2)同一個哨兵下的、不同主從模型,彼此之間相互獨立。
(3)Sentinel會不斷檢查Master和Slaves是否正常。
1.2.2 自動故障切換(Automatic failover)
1.2.2.1 Sentinel網絡
監控同一個Master的Sentinel會自動連接,組成一個分布式的Sentinel網絡,互相通信并交換彼此關于被監視服務器的信息。下圖中,三個監控s1的Sentinel,自動組成Sentinel網絡結構。
疑問:為什么要使用sentinel網絡呢?
答:當只有一個sentinel的時候,如果這個sentinel掛掉了,那么就無法實現自動故障切換了。
在sentinel網絡中,只要還有一個sentinel活著,就可以實現故障切換。
1.1.1.1 故障切換的過程
(1)投票(半數原則)
當任何一個Sentinel發現被監控的Master下線時,會通知其它的Sentinel開會,投票確定該Master是否下線(半數以上,所以sentinel通常配奇數個)。
(2)選舉
當Sentinel確定Master下線后,會在所有的Slaves中,選舉一個新的節點,升級成Master節點。
其它Slaves節點,轉為該節點的從節點。
(3)原Master重新上線
當原Master節點重新上線后,自動轉為當前Master節點的從節點。
1.1 哨兵模式部署
1.1.1 需求
前提:已經存在一個正在運行的主從模式。
另外,配置三個Sentinel實例,監控同一個Master節點。
1.1.2 配置Sentinel
(1)在/usr/local目錄下,創建/redis/sentinels/目錄
[root@node0719 local]# mkdir -p redis/sentinels
(2)在/sentinels目錄下,以次創建s1、s2、s3三個子目錄中
[root@node0719 sentinels]# mkdir s1 s2 s3
(3)依次拷貝redis解壓目錄下的sentinel.conf文件,到這三個子目錄中
[root@node0719 sentinels]# cp /root/redis-3.2.9/sentinel.conf ./s1/
[root@node0719 sentinels]# cp /root/redis-3.2.9/sentinel.conf ./s2/
[root@node0719 sentinels]# cp /root/redis-3.2.9/sentinel.conf ./s3/
(4)依次修改s1、s2、s3子目錄中的sentinel.conf文件,修改端口,并指定要監控的主節點。(從節點不需要指定,sentinel會自動識別)
S1哨兵配置如下:
S2哨兵配置如下:
S3哨兵配置如下:
(5)再打開三個xshell窗口,在每一個窗口中,啟動一個哨兵實例,并觀察日志輸出
[root@node0719 sentinels]# redis-sentinel ./s1/sentinel.conf
[root@node0719 sentinels]# redis-sentinel ./s2/sentinel.conf
[root@node0719 sentinels]# redis-sentinel ./s3/sentinel.conf
對于用redis-server啟動哨兵的方式如下:
[root@node0719 sentinels]# redis-server ./s1/sentinel.conf --sentinel
1.1.3 測試
(1)先關閉6380節點(kill掉)。發現,確實重新指定了一個主節點
(2)再次上線6380節點。發現,6380節點成為了新的主節點的從節點。
1.2 結論
Sentinel哨兵模式,確實能實現自動故障切換。提供穩定的服務
redis集群搭建
Redis Cluster屬于無中心化的集群方案,
一、Redis Cluster(Redis集群)簡介
RedisCluster 是 Redis 的親兒子,它是 Redis 作者自己提供的 Redis 集群化方案。 相對于 Codis 的不同,它是去中心化的,如圖所示,該集群有三個 Redis 節點組成, 每個節點負責整個集群的一部分數據,每個節點負責的數據多少可能不一樣。這三個節點相 互連接組成一個對等的集群,它們之間通過一種特殊的二進制協議相互交互集群信息。
Redis Cluster 將所有數據劃分為 16384 的 slots,它比 Codis 的 1024 個槽劃分得更為精細,每個節點負責其中一部分槽位。槽位的信息存儲于每個節點中,它不像 Codis,它不 需要另外的分布式存儲來存儲節點槽位信息。
當 Redis Cluster 的客戶端來連接集群時,它也會得到一份集群的槽位配置信息。這樣當客戶端要查找某個 key 時,可以直接定位到目標節點。
這點不同于 Codis,Codis 需要通過 Proxy 來定位目標節點,RedisCluster 是直接定 位。客戶端為了可以直接定位某個具體的 key 所在的節點,它就需要緩存槽位相關信息,這樣才可以準確快速地定位到相應的節點。同時因為槽位的信息可能會存在客戶端與服務器不一致的情況,還需要糾正機制來實現槽位信息的校驗調整。 另外,RedisCluster 的每個節點會將集群的配置信息持久化到配置文件中,所以必須確保配置文件是可寫的,而且盡量不要依靠人工修改配置文件。
redis是一個開源的key value存儲系統,受到了廣大互聯網公司的青睞。redis3.0版本之前只支持單例模式,在3.0版本及以后才支持集群,我這里用的是redis3.0.0版本;
redis集群采用P2P模式,是完全去中心化的,不存在中心節點或者代理節點;
redis集群是沒有統一的入口的,客戶端(client)連接集群的時候連接集群中的任意節點(node)即可,集群內部的節點是相互通信的(PING-PONG機制),每個節點都是一個redis實例;
為了實現集群的高可用,即判斷節點是否健康(能否正常使用),redis-cluster有這么一個投票容錯機制:如果集群中超過半數的節點投票認為某個節點掛了,那么這個節點就掛了(fail)。這是判斷節點是否掛了的方法;
那么如何判斷集群是否掛了呢? -> 如果集群中任意一個節點掛了,而且該節點沒有從節點(備份節點),那么這個集群就掛了。這是判斷集群是否掛了的方法;
那么為什么任意一個節點掛了(沒有從節點)這個集群就掛了呢? -> 因為集群內置了16384個slot(哈希槽),并且把所有的物理節點映射到了這16384[0-16383]個slot上,或者說把這些slot均等的分配給了各個節點。當需要在Redis集群存放一個數據(key-value)時,redis會先對這個key進行crc16算法,然后得到一個結果。再把這個結果對16384進行求余,這個余數會對應[0-16383]其中一個槽,進而決定key-value存儲到哪個節點中。所以一旦某個節點掛了,該節點對應的slot就無法使用,那么就會導致集群無法正常工作。
綜上所述,每個Redis集群理論上最多可以有16384個節點。
二、集群搭建需要的環境
防火墻設置
redis集群中的每個節點都需要建立2個tcp連接,監聽這2個端口:一個端口稱之為“客戶端端口”,用于接受客戶端指令,與客戶端交互,比如6379;另一個端口稱之為“集群總線端口”,是在客戶端端口號上加10000,比如16379,用于節點之間通過二進制協議通訊。各節點通過集群總線檢測宕機節點、更新配置、故障轉移驗證等。客戶端只能使用客戶端端口,不能使用集群總線端口。請確保你的防火墻允許打開這兩個端口,否則redis集群沒法工作。客戶端端口和集群總線端口之間的差值是固定的,集群總線端口比客戶端端口高10000。
注意,關于集群的2個端口:
客戶端端口(一般是6379)需要對所有客戶端和集群節點開放,因為集群節點需要通過該端口轉移數據。
集群總線端口(一般是16379)只需對集群中的所有節點開放
這2個端口必須打開,否則集群沒法正常工作。
集群節點之間通過集群總線端口交互數據,使用的協議不同于客戶端的協議,是二進制協議,這可以減少帶寬和處理時間。
Redis集群數據的分片
Redis集群不是使用一致性哈希,而是使用哈希槽。整個redis集群有16384個哈希槽,決定一個key應該分配到那個槽的算法是:計算該key的CRC16結果再模16834。
集群中的每個節點負責一部分哈希槽,比如集群中有3個節點,則:
節點A存儲的哈希槽范圍是:0 – 5500
節點B存儲的哈希槽范圍是:5501 – 11000
節點C存儲的哈希槽范圍是:11001 – 16384
這樣的分布方式方便節點的添加和刪除。比如,需要新增一個節點D,只需要把A、B、C中的部分哈希槽數據移到D節點。同樣,如果希望在集群中刪除A節點,只需要把A節點的哈希槽的數據移到B和C節點,當A節點的數據全部被移走后,A節點就可以完全從集群中刪除。
因為把哈希槽從一個節點移到另一個節點是不需要停機的,所以,增加或刪除節點,或更改節點上的哈希槽,也是不需要停機的。
如果多個key都屬于一個哈希槽,集群支持通過一個命令(或事務, 或lua腳本)同時操作這些key。通過“哈希標簽”的概念,用戶可以讓多個key分配到同一個哈希槽。哈希標簽在集群詳細文檔中有描述,這里做個簡單介紹:如果key含有大括號”{}”,則只有大括號中的字符串會參與哈希,比如”this{foo}”和”another{foo}”這2個key會分配到同一個哈希槽,所以可以在一個命令中同時操作他們。
Redis集群的一致性保證
Redis集群不能保證強一致性。一些已經向客戶端確認寫成功的操作,會在某些不確定的情況下丟失。
產生寫操作丟失的第一個原因,是因為主從節點之間使用了異步的方式來同步數據。
一個寫操作是這樣一個流程:
1)客戶端向主節點B發起寫的操作
2)主節點B回應客戶端寫操作成功
3)主節點B向它的從節點B1,B2,B3同步該寫操作
從上面的流程可以看出來,主節點B并沒有等從節點B1,B2,B3寫完之后再回復客戶端這次操作的結果。所以,如果主節點B在通知客戶端寫操作成功之后,但同步給從節點之前,主節點B故障了,其中一個沒有收到該寫操作的從節點會晉升成主節點,該寫操作就這樣永遠丟失了。
就像傳統的數據庫,在不涉及到分布式的情況下,它每秒寫回磁盤。為了提高一致性,可以在寫盤完成之后再回復客戶端,但這樣就要損失性能。這種方式就等于Redis集群使用同步復制的方式。
基本上,在性能和一致性之間,需要一個權衡。
如果真的需要,Redis集群支持同步復制的方式,通過WAIT指令來實現,這可以讓丟失寫操作的可能性降到很低。但就算使用了同步復制的方式,Redis集群依然不是強一致性的,在某些復雜的情況下,比如從節點在與主節點失去連接之后被選為主節點,不一致性還是會發生。
這種不一致性發生的情況是這樣的,當客戶端與少數的節點(至少含有一個主節點)網絡聯通,但他們與其他大多數節點網絡不通。比如6個節點,A,B,C是主節點,A1,B1,C1分別是他們的從節點,一個客戶端稱之為Z1。
當網絡出問題時,他們被分成2組網絡,組內網絡聯通,但2組之間的網絡不通,假設A,C,A1,B1,C1彼此之間是聯通的,另一邊,B和Z1的網絡是聯通的。Z1可以繼續往B發起寫操作,B也接受Z1的寫操作。當網絡恢復時,如果這個時間間隔足夠短,集群仍然能繼續正常工作。如果時間比較長,以致B1在大多數的這邊被選為主節點,那剛才Z1發給B的寫操作都將丟失。
注意,Z1給B發送寫操作是有一個限制的,如果時間長度達到了大多數節點那邊可以選出一個新的主節點時,少數這邊的所有主節點都不接受寫操作。
這個時間的配置,稱之為節點超時(node timeout),對集群來說非常重要,當達到了這個節點超時的時間之后,主節點被認為已經宕機,可以用它
Redis集群參數配置
我們后面會部署一個Redis集群作為例子,在那之前,先介紹一下集群在redis.conf中的參數。
cluster-enabled <yes/no>: 如果配置”yes”則開啟集群功能,此redis實例作為集群的一個節點,否則,它是一個普通的單一的redis實例。
cluster-config-file <filename>: 注意:雖然此配置的名字叫“集群配置文件”,但是此配置文件不能人工編輯,它是集群節點自動維護的文件,主要用于記錄集群中有哪些節點、他們的狀態以及一些持久化參數等,方便在重啟時恢復這些狀態。通常是在收到請求之后這個文件就會被更新。
cluster-node-timeout <milliseconds>: 這是集群中的節點能夠失聯的最大時間,超過這個時間,該節點就會被認為故障。如果主節點超過這個時間還是不可達,則用它的從節點將啟動故障遷移,升級成主節點。注意,任何一個節點在這個時間之內如果還是沒有連上大部分的主節點,則此節點將停止接收任何請求。
cluster-slave-validity-factor <factor>: 如果設置成0,則無論從節點與主節點失聯多久,從節點都會嘗試升級成主節點。如果設置成正數,則cluster-node-timeout乘以cluster-slave-validity-factor得到的時間,是從節點與主節點失聯后,此從節點數據有效的最長時間,超過這個時間,從節點不會啟動故障遷移。假設cluster-node-timeout=5,cluster-slave-validity-factor=10,則如果從節點跟主節點失聯超過50秒,此從節點不能成為主節點。注意,如果此參數配置為非0,將可能出現由于某主節點失聯卻沒有從節點能頂上的情況,從而導致集群不能正常工作,在這種情況下,只有等到原來的主節點重新回歸到集群,集群才恢復運作。
cluster-migration-barrier <count>:主節點需要的最小從節點數,只有達到這個數,主節點失敗時,它從節點才會進行遷移。更詳細介紹可以看本教程后面關于副本遷移到部分。
cluster-require-full-coverage <yes/no>:在部分key所在的節點不可用時,如果此參數設置為”yes”(默認值), 則整個集群停止接受操作;如果此參數設置為”no”,則集群依然為可達節點上的key提供讀操作。
port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
開啟集群模式只需打開cluster-enabled配置項即可。每一個redis實例都包含一個配置文件,默認是nodes.conf,用于存儲此節點的一些配置信息。這個配置文件由redis集群的節點自行創建和更新,不能由人手動地去修改。
一個最小的集群需要最少3個主節點。第一次測試,強烈建議你配置6個節點:3個主節點和3個從節點
搭建準備
2.1 Redis集群至少需要3個節點,因為投票容錯機制要求超過半數節點認為某個節點掛了該節點才是掛了,所以2個節點無法構成集群。
2.2 要保證集群的高可用,需要每個節點都有從節點,也就是備份節點,所以Redis集群至少需要6臺服務器。因為我沒有那么多服務器,也啟動不了那么多虛擬機,所在這里搭建的是偽分布式集群,即一臺服務器虛擬運行6個redis實例,修改端口號為(7001-7006),當然實際生產環境的Redis集群搭建和這里是一樣的。
將redis-cluster/redis01文件復制5份到redis-cluster目錄下(redis02-redis06),創建6個redis實例,模擬Redis集群的6個節點。然后將其余5個文件下的redis.conf里面的端口號分別修改為7002-7006。分別如下圖所示:
創建redis02-06目錄
接著啟動所有redis節點,由于一個一個啟動太麻煩了,所以在這里創建一個批量啟動redis節點的腳本文件,命令為start-all.sh,文件內容如下:
cd redis01
./redis-server redis.conf
cd ..
cd redis02
./redis-server redis.conf
cd ..
cd redis03
./redis-server redis.conf
cd ..
cd redis04
./redis-server redis.conf
cd ..
cd redis05
./redis-server redis.conf
cd ..
cd redis06
./redis-server redis.conf
cd ..
創建好啟動腳本文件之后,需要修改該腳本的權限,使之能夠執行,指令如下:
chmod +x start-all.sh
執行start-all.sh腳本,啟動6個redis節點
至此6個redis節點啟動成功,接下來正式開啟搭建集群,
三:實戰:搭建集群
Redis 官方提供了 redis-trib.rb 這個工具,作為搭建集群的專門工具。
Redis的實例全部運行之后,還需要redis-trib.rb工具來完成集群的創建,redis-trib.rb二進制文件在Redis包主目錄下的src目錄中,運行該工具依賴Ruby環境和gem,因此需要提前安裝Ruby。
因為這個工具是一個ruby腳本文件,所以這個工具的運行需要ruby的運行環境,該環境相當于JVM虛擬機環境,是的ruby腳本運行時,就相當于java語言的運行需要在jvm上。
安裝ruby,指令如下:
yum install ruby
/usr/local/redis/redis-4.0.9/src/redis-trib.rb create --replicas 1
10.10.1.129:7001 10.10.1.129:7002 10.10.1.129:7003
10.10.1.130:7004 10.10.1.130:7005 10.10.1.130:7006
10.10.1.131:7007 10.10.1.131:7008 10.10.1.131:7009
--replicas 1 表示主從復制比例為 1:1,即一個主節點對應一個從節點;然后,默認給我們分配好了每個主節點和對應從節點服務,以及 solt 的大小,因為在 Redis 集群中有且僅有 16383 個 solt ,默認情況會給我們平均分配,當然你可以指定,后續的增減節點也可以重新分配。
/usr/local/redis/redis-4.0.9/src/redis-trib.rb create --replicas 1
> 10.10.1.129:7001 10.10.1.129:7002 10.10.1.129:7003
> 10.10.1.130:7004 10.10.1.130:7005 10.10.1.130:7006
> 10.10.1.131:7007 10.10.1.131:7008 10.10.1.131:7009
>>> Creating cluster
>>> Performing hash slots allocation on 9 nodes...
Using 4 masters:
10.10.1.129:7001
10.10.1.130:7004
10.10.1.131:7007
10.10.1.129:7002
Adding replica 10.10.1.131:7008 to 10.10.1.129:7001
Adding replica 10.10.1.129:7003 to 10.10.1.130:7004
Adding replica 10.10.1.130:7006 to 10.10.1.131:7007
Adding replica 10.10.1.131:7009 to 10.10.1.129:7002
Adding replica 10.10.1.130:7005 to 10.10.1.129:7001
M: 7a047cfaae70c30d0d7e1a5d9854eb7f11afe957 10.10.1.129:7001
slots:0-4095 (4096 slots) master
M: 924d61969343b5cc2200bd3a2277e815dc76048c 10.10.1.129:7002
slots:12288-16383 (4096 slots) master
S: b9ba251f575b5396da4bea307e25a98d85b3c504 10.10.1.129:7003
replicates a4a5de0be9bb5704eec17cbe0223076eb38fc4a4
M: a4a5de0be9bb5704eec17cbe0223076eb38fc4a4 10.10.1.130:7004
slots:4096-8191 (4096 slots) master
S: bdc2f6b254459a6a6d038d93e5a3d3a67fe3e936 10.10.1.130:7005
replicates 7a047cfaae70c30d0d7e1a5d9854eb7f11afe957
S: 4ae20400d02e57e274f9b9f29d4ba120aa2b574c 10.10.1.130:7006
replicates 2b0f974e151cd798f474107ac68a47e188cc88a2
M: 2b0f974e151cd798f474107ac68a47e188cc88a2 10.10.1.131:7007
slots:8192-12287 (4096 slots) master
S: b240d86fdf6abc73df059baf64b930387664da15 10.10.1.131:7008
replicates 7a047cfaae70c30d0d7e1a5d9854eb7f11afe957
S: 5b7989d5370aef41679e92a6bd34c30ac3be3581 10.10.1.131:7009
replicates 924d61969343b5cc2200bd3a2277e815dc76048c
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join........
>>> Performing Cluster Check (using node 10.10.1.129:7001)
M: 7a047cfaae70c30d0d7e1a5d9854eb7f11afe957 10.10.1.129:7001
slots:0-4095 (4096 slots) master
2 additional replica(s)
S: 4ae20400d02e57e274f9b9f29d4ba120aa2b574c 10.10.1.130:7006
slots: (0 slots) slave
replicates 2b0f974e151cd798f474107ac68a47e188cc88a2
M: 924d61969343b5cc2200bd3a2277e815dc76048c 10.10.1.129:7002
slots:12288-16383 (4096 slots) master
1 additional replica(s)
M: 2b0f974e151cd798f474107ac68a47e188cc88a2 10.10.1.131:7007
slots:8192-12287 (4096 slots) master
1 additional replica(s)
S: 5b7989d5370aef41679e92a6bd34c30ac3be3581 10.10.1.131:7009
slots: (0 slots) slave
replicates 924d61969343b5cc2200bd3a2277e815dc76048c
S: b9ba251f575b5396da4bea307e25a98d85b3c504 10.10.1.129:7003
slots: (0 slots) slave
replicates a4a5de0be9bb5704eec17cbe0223076eb38fc4a4
S: b240d86fdf6abc73df059baf64b930387664da15 10.10.1.131:7008
slots: (0 slots) slave
replicates 7a047cfaae70c30d0d7e1a5d9854eb7f11afe957
S: bdc2f6b254459a6a6d038d93e5a3d3a67fe3e936 10.10.1.130:7005
slots: (0 slots) slave
replicates 7a047cfaae70c30d0d7e1a5d9854eb7f11afe957
M: a4a5de0be9bb5704eec17cbe0223076eb38fc4a4 10.10.1.130:7004
slots:4096-8191 (4096 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
出現上述的輸出則代表集群搭建成功啦!!!
說明:
M: 7a047cfaae70c30d0d7e1a5d9854eb7f11afe957 為主節點id
S: b240d86fdf6abc73df059baf64b930387664da15 為從節點的id
目前來看,7001、7002、7004、7007 為主節點,7003、7005、7008、7009 為從節點,并向你確認是否同意這么配置。輸入 yes 后,會開始集群創建。
四:redis cluster小結
redis cluster在設計的時候,就考慮到了去中心化、去中間件,也就是說,集群中的每個節點都是平等關系,都是對等的,每個節點都保存各自的數據和整個集群的狀態。每個節點都和其他所有節點連接,而且這些連接保持活躍,這樣就保證了我們只需要連接集群中的任意一個節點,就可以獲取到其他節點的數據。
Redis 集群沒有并使用傳統的一致性哈希來分配數據,而是采用另外一種叫做哈希槽 (hash slot)的方式來分配的。redis cluster 默認分配了 16384 個 slot,當我們 set 一個 key 時,會用CRC16算法來取模得到所屬的 slot,然后將這個 key 分到哈希槽區間的節點上,具體算法就是:CRC16(key) % 16384。所以我們在測試的時候看到 set 和 get 的時候,直接跳轉到了7000端口的節點。
Redis 集群會把數據存在一個 master 節點,然后在這個 master 和其對應的 salve 之間進行數據同步。當讀取數據時,也根據一致性哈希算法到對應的 master 節點獲取數據。只有當一個 master 掛掉之后,才會啟動一個對應的 salve 節點,充當 master 。
需要注意的是:必須要3個或以上的主節點,否則在創建集群時會失敗,并且當存活的主節點數小于總節點數的一半時,整個集群就無法提供服務了。
Codis
Codis 是一個分布式 Redis 解決方案, 對于上層的應用來說, 連接到 Codis Proxy 和連接原生的 Redis Server 沒有明顯的區別 (有一些命令不支持), 上層應用可以像使用單機的 Redis 一樣使用, Codis 底層會處理請求的轉發, 不停機的數據遷移等工作, 所有后邊的一切事情, 對于前面的客戶端來說是透明的, 可以簡單的認為后邊連接的是一個內存無限大的 Redis 服務,當然,前段時間redis官方的3.0出了穩定版,3.0支持集群功能,codis的實現原理和3.0的集群功能差不多,我了解的現在美團、阿里已經用了3.0的集群功能了,
1.什么是Codis?
Codis 是一個分布式 Redis 解決方案, 對于上層的應用來說, 連接到 Codis Proxy 和連接原生的 Redis Server 沒有明顯的區別
(不支持的命令列表), 上層應用可以像使用單機的 Redis 一樣使用, Codis 底層會處理請求的轉發, 不停機的數據遷移等工作,
所有后邊的一切事情, 對于前面的客戶端來說是透明的, 可以簡單的認為后邊連接的是一個內存無限大的 Redis 服務。
2.codis介紹
Codis是一個分布式Redis解決方案,對于上層的應用來說,連接到Codis Proxy和連接原生的RedisServer沒有明顯的區別,有部分命令不支持。
Codis底層會處理請求的轉發,不停機的數據遷移等工作,所有后邊的一切事情,
對于前面的客戶端來說是透明的,可以簡單的認為后邊連接的是一個內存無限大的Redis服務.
Codis由四部分組成
| Codis-proxy | 實現redis協議,由于本身是無狀態的,因此可以部署很多個節點 |
|---|---|
| Codis-config | 是codis的管理工具,包括添加/刪除redis節點添加/刪除proxy節點,發起數據遷移等操作,自帶httpserver,支持管理后臺方式管理配置 |
| Codis-server | 是codis維護的redis分支,基于2.8.21分支,加入了slot的支持和原子的數據遷移指令; codis-proxy和codis-config只能和這個版本的redis交互才能正常運行 |
| Zookeeper | 用于codis集群元數據的存儲,維護codis集群節點 |
3.Codis的架構
4.Codis的優缺點
(1)優點
對客戶端透明,與codis交互方式和redis本身交互一樣
支持在線數據遷移,遷移過程對客戶端透明有簡單的管理和監控界面
支持高可用,無論是redis數據存儲還是代理節點
自動進行數據的均衡分配
最大支持1024個redis實例,存儲容量海量
高性能
(2)缺點
采用自有的redis分支,不能與原版的redis保持同步
如果codis的proxy只有一個的情況下, redis的性能會下降20%左右
某些命令不支持,比如事務命令muti
國內開源產品,活躍度相對弱一些
常見問題
1 如何解決緩存穿透與緩存雪崩
如何解決緩存穿透與緩存雪崩。這是基本問題也是面試常問問題。
作為一個內存數據庫,redis也總是免不了有各種各樣的問題,這篇文章主要是針對其中兩個問題進行講解:緩存穿透和緩存雪崩。
一、緩存穿透
1、概念
緩存穿透的概念很簡單,用戶想要查詢一個數據,發現redis內存數據庫沒有,也就是緩存沒有命中,于是向持久層數據庫查詢。發現也沒有,于是本次查詢失敗。當用戶很多的時候,緩存都沒有命中,于是都去請求了持久層數據庫。這會給持久層數據庫造成很大的壓力,這時候就相當于出現了緩存穿透。
這里需要注意和緩存擊穿的區別,緩存擊穿,是指一個key非常熱點,在不停的扛著大并發,大并發集中對這一個點進行訪問,當這個key在失效的瞬間,持續的大并發就穿破緩存,直接請求數據庫,就像在一個屏障上鑿開了一個洞。
為了避免緩存穿透其實有很多種解決方案。下面介紹幾種。
2、解決方案
(1)布隆過濾器
布隆過濾器是一種數據結構,垃圾網站和正常網站加起來全世界據統計也有幾十億個。網警要過濾這些垃圾網站,總不能到數據庫里面一個一個去比較吧,這就可以使用布隆過濾器。假設我們存儲一億個垃圾網站地址。
可以先有一億個二進制比特,然后網警用八個不同的隨機數產生器(F1,F2, …,F8) 產生八個信息指紋(f1, f2, …, f8)。接下來用一個隨機數產生器 G 把這八個信息指紋映射到 1 到1億中的八個自然數 g1, g2, …,g8。最后把這八個位置的二進制全部設置為一。過程如下:
有一天網警查到了一個可疑的網站,想判斷一下是否是XX網站,首先將可疑網站通過哈希映射到1億個比特數組上的8個點。如果8個點的其中有一個點不為1,則可以判斷該元素一定不存在集合中。
那這個布隆過濾器是如何解決redis中的緩存穿透呢?很簡單首先也是對所有可能查詢的參數以hash形式存儲,當用戶想要查詢的時候,使用布隆過濾器發現不在集合中,就直接丟棄,不再對持久層查詢。
這個形式很簡單。
2、緩存空對象
當存儲層不命中后,即使返回的空對象也將其緩存起來,同時會設置一個過期時間,之后再訪問這個數據將會從緩存中獲取,保護了后端數據源;
但是這種方法會存在兩個問題:
如果空值能夠被緩存起來,這就意味著緩存需要更多的空間存儲更多的鍵,因為這當中可能會有很多的空值的鍵;即使對空值設置了過期時間,還是會存在緩存層和存儲層的數據會有一段時間窗口的不一致,這對于需要保持一致性的業務會有影響。
如何避免?
1:對查詢結果為空的情況也進行緩存,緩存時間設置短一點,或者該key對應的數據insert了之后清理緩存。
2:對一定不存在的key進行過濾。可以把所有的可能存在的key放到一個大的Bitmap中,查詢時通過該bitmap過濾。
二、緩存雪崩
1、概念
緩存雪崩是指,緩存層出現了錯誤,不能正常工作了。于是所有的請求都會達到存儲層,存儲層的調用量會暴增,造成存儲層也會掛掉的情況。
2、解決方案
(1)redis高可用
這個思想的含義是,既然redis有可能掛掉,那我多增設幾臺redis,這樣一臺掛掉之后其他的還可以繼續工作,其實就是搭建的集群。
(2)限流降級
這個解決方案的思想是,在緩存失效后,通過加鎖或者隊列來控制讀數據庫寫緩存的線程數量。比如對某個key只允許一個線程查詢數據和寫緩存,其他線程等待。
(3)數據預熱
數據加熱的含義就是在正式部署之前,我先把可能的數據先預先訪問一遍,這樣部分可能大量訪問的數據就會加載到緩存中。在即將發生大并發訪問前手動觸發加載緩存不同的key,設置不同的過期時間,讓緩存失效的時間點盡量均勻。
當緩存服務器重啟或者大量緩存集中在某一個時間段失效,這樣在失效的時候,會給后端系統帶來很大壓力。導致系統崩潰。
如何避免?
1:在緩存失效后,通過加鎖或者隊列來控制讀數據庫寫緩存的線程數量。比如對某個key只允許一個線程查詢數據和寫緩存,其他線程等待。
2:做二級緩存,A1為原始緩存,A2為拷貝緩存,A1失效時,可以訪問A2,A1緩存失效時間設置為短期,A2設置為長期
3:不同的key,設置不同的過期時間,讓緩存失效的時間點盡量均勻。
2 什么是Redis持久化?Redis有哪幾種持久化方式?優缺點是什么?
持久化就是把內存的數據寫到磁盤中去,防止服務宕機了內存數據丟失。
Redis 提供了兩種持久化方式:RDB(默認) 和AOF
RDB:
rdb是Redis DataBase縮寫
功能核心函數rdbSave(生成RDB文件)和rdbLoad(從文件加載內存)兩個函數
AOF:
Aof是Append-only file縮寫
每當執行服務器(定時)任務或者函數時flushAppendOnlyFile 函數都會被調用, 這個函數執行以下兩個工作
aof寫入保存:
WRITE:根據條件,將 aof_buf 中的緩存寫入到 AOF 文件
SAVE:根據條件,調用 fsync 或 fdatasync 函數,將 AOF 文件保存到磁盤中。
存儲結構:
內容是redis通訊協議(RESP )格式的命令文本存儲。
比較:
1、aof文件比rdb更新頻率高,優先使用aof還原數據。
2、aof比rdb更安全也更大
3、rdb性能比aof好
4、如果兩個都配了優先加載AOF
3 什么是一致性哈希算法
Redis 集群會把數據存在一個 master 節點,然后在這個 master 和其對應的 salve 之間進行數據同步。當讀取數據時,也根據一致性哈希算法到對應的 master 節點獲取數據。只有當一個 master 掛掉之后,才會啟動一個對應的 salve 節點,充當 master 。什么是一致性哈希算法?
一致哈希 是一種特殊的哈希算法。在使用一致哈希算法后,哈希表槽位數(大小)的改變平均只需要對 K/n 個關鍵字重新映射,其中K是關鍵字的數量, n是槽位數量。然而在傳統的哈希表中,添加或刪除一個槽位的幾乎需要對所有關鍵字進行重新映射。
簡單的說,一致性哈希是將整個哈希值空間組織成一個虛擬的圓環,如假設哈希函數H的值空間為0-2^32-1(哈希值是32位無符號整形),整個哈希空間環如下:
整個空間按順時針方向組織,0和2^32-1在零點中方向重合。
接下來,把服務器按照IP或主機名作為關鍵字進行哈希,這樣就能確定其在哈希環的位置。
然后,我們就可以使用哈希函數H計算值為key的數據在哈希環的具體位置h,根據h確定在環中的具體位置,從此位置沿順時針滾動,遇到的第一臺服務器就是其應該定位到的服務器。
例如我們有A、B、C、D四個數據對象,經過哈希計算后,在環空間上的位置如下:
根據一致性哈希算法,數據A會被定為到Server 1上,數據B被定為到Server 2上,而C、D被定為到Server 3上。
3.1 容錯性和擴展性
那么使用一致性哈希算法的容錯性和擴展性如何呢?
3.1.1 容錯性
假如RedisService2宕機了,那么會怎樣呢?
那么,數據B對應的節點保存到RedisService3中。因此,其中一臺宕機后,干擾的只有前面的數據(原數據被保存到順時針的下一個服務器),而不會干擾到其他的數據。
3.1.2 擴展性
下面考慮另一種情況,假如增加一臺服務器Redis4,具體位置如下圖所示:
原本數據C是保存到Redis3中,但由于增加了Redis4,數據C被保存到Redis4中。干擾的也只有Redis3而已,其他數據不會受到影響。
因此,一致性哈希算法對于節點的增減都只需重定位換空間的一小部分即可,具有較好的容錯性和可擴展性
3.2 虛擬節點
前面部分都是講述到Redis節點較多和節點分布較為均衡的情況,如果節點較少就會出現節點分布不均衡造成數據傾斜問題。
例如,我們的的系統有兩臺Redis,分布的環位置如下圖所示:
這會產生一種情況,Redis4的hash范圍比Redis3的hash范圍大,導致數據大部分都存儲在Redis4中,數據存儲不平衡。
為了解決這種數據存儲不平衡的問題,一致性哈希算法引入了虛擬節點機制,即對每個節點計算多個哈希值,每個計算結果位置都放置在對應節點中,這些節點稱為虛擬節點。
具體做法可以在服務器IP或主機名的后面增加編號來實現,例如上面的情況,可以為每個服務節點增加三個虛擬節點,于是可以分為 RedisService1#1、 RedisService1#2、 RedisService1#3、 RedisService2#1、 RedisService2#2、 RedisService2#3,具體位置如下圖所示:
對于數據定位的hash算法仍然不變,只是增加了虛擬節點到實際節點的映射。例如,數據C保存到虛擬節點Redis1#2,實際上數據保存到Redis1中。這樣,就能解決服務節點少時數據不平均的問題。在實際應用中,通常將虛擬節點數設置為32甚至更大,因此即使很少的服務節點也能做到相對均勻的數據分布。
總結
以上是生活随笔為你收集整理的Redis 高并发 (史上最全)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: cad注册表怎么删除?CAD安装报错14
- 下一篇: 1、koala软件的安装和使用--les