當前位置:首頁 » 硬碟大全 » redis怎麼分布式緩存
擴展閱讀
webinf下怎麼引入js 2023-08-31 21:54:13
堡壘機怎麼打開web 2023-08-31 21:54:11

redis怎麼分布式緩存

發布時間: 2023-01-31 18:41:35

1. 細說分布式redis

IT培訓>資料庫教程
細說分布式Redis架構設計和踩過的那些坑

作者:課課家教育2015-12-14 10:15:25

摘要:本文章主要分成五個步驟內容講解
Redis、RedisCluster和Codis;
我們更愛一致性;
Codis在生產環境中的使用的經驗和坑們;
對於分布式資料庫和分布式架構的一些看法;
Q & A環節。
Codis是一個分布式Redis解決方案,與官方的純P2P的模式不同,Codis採用的是Proxy-based的方案。今天我們介紹一下Codis及下一個大版本RebornDB的設計,同時會介紹一些Codis在實際應用場景中的tips。最後拋磚引玉,會介紹一下我對分布式存儲的一些觀點和看法,望各位首席們雅正。

細說分布式Redis架構設計和踩過的那些坑_redis 分布式_ redis 分布式鎖_分布式緩存redis
一、 Redis,RedisCluster和Codis
Redis:想必大家的架構中,Redis已經是一個必不可少的部件,豐富的數據結構和超高的性能以及簡單的協議,讓Redis能夠很好的作為資料庫的上游緩存層。但是我們會比較擔心Redis的單點問題,單點Redis容量大小總受限於內存,在業務對性能要求比較高的情況下,理想情況下我們希望所有的數據都能在內存裡面,不要打到資料庫上,所以很自然的就會尋求其他方案。 比如,SSD將內存換成了磁碟,以換取更大的容量。更自然的想法是將Redis變成一個可以水平擴展的分布式緩存服務,在Codis之前,業界只有Twemproxy,但是Twemproxy本身是一個靜態的分布式Redis方案,進行擴容/縮容時候對運維要求非常高,而且很難做到平滑的擴縮容。Codis的目標其實就是盡量兼容Twemproxy的基礎上,加上數據遷移的功能以實現擴容和縮容,最終替換Twemproxy。從豌豆莢最後上線的結果來看,最後完全替換了Twem,大概2T左右的內存集群。
Redis Cluster :與Codis同期發布正式版的官方cl

2. 常用的緩存技術

第一章 常用的緩存技術
1、常見的兩種緩存

本地緩存:不需要序列化,速度快,緩存的數量與大小受限於本機內存
分布式緩存:需要序列化,速度相較於本地緩存較慢,但是理論上緩存的數量與大小無限(因為緩存機器可以不斷擴展)
2、本地緩存

Google guava cache:當下最好用的本地緩存
Ehcache:spring默認集成的一個緩存,以spring cache的底層緩存實現類形式去操作緩存的話,非常方便,但是欠缺靈活,如果想要靈活使用,還是要單獨使用Ehcache
Oscache:最經典簡單的頁面緩存
3、分布式緩存

memcached:分布式緩存的標配
Redis:新一代的分布式緩存,有替代memcached的趨勢
3.1、memcached

經典的一致性hash演算法
基於slab的內存模型有效防止內存碎片的產生(但同時也需要估計好啟動參數,否則會浪費很多的內存)
集群中機器之間互不通信(相較於Jboss cache等集群中機器之間的相互通信的緩存,速度更快<--因為少了同步更新緩存的開銷,且更適合於大型分布式系統中使用)
使用方便(這一點是相較於Redis在構建客戶端的時候而言的,盡管redis的使用也不困難)
很專一(專做緩存,這一點也是相較於Redis而言的)
3.2、Redis

可以存儲復雜的數據結構(5種)
strings-->即簡單的key-value,就是memcached可以存儲的唯一的一種形式,接下來的四種是memcached不能直接存儲的四種格式(當然理論上可以先將下面的一些數據結構中的東西封裝成對象,然後存入memcached,但是不推薦將大對象存入memcached,因為memcached的單一value的最大存儲為1M,可能即使採用了壓縮演算法也不夠,即使夠,可能存取的效率也不高,而redis的value最大為1G)
hashs-->看做hashTable
lists-->看做LinkedList
sets-->看做hashSet,事實上底層是一個hashTable
sorted sets-->底層是一個skipList
有兩種方式可以對緩存數據進行持久化
RDB
AOF
事件調度
發布訂閱等
4、集成緩存

專指spring cache,spring cache自己繼承了ehcache作為了緩存的實現類,我們也可以使用guava cache、memcached、redis自己來實現spring cache的底層。當然,spring cache可以根據實現類來將緩存存在本地還是存在遠程機器上。

5、頁面緩存

在使用jsp的時候,我們會將一些復雜的頁面使用Oscache進行頁面緩存,使用非常簡單,就是幾個標簽的事兒;但是,現在一般的企業,前台都會使用velocity、freemaker這兩種模板引擎,本身速度就已經很快了,頁面緩存使用的也就很少了。

總結:

在實際生產中,我們通常會使用guava cache做本地緩存+redis做分布式緩存+spring cache就集成緩存(底層使用redis來實現)的形式
guava cache使用在更快的獲取緩存數據,同時緩存的數據量並不大的情況
spring cache集成緩存是為了簡單便捷的去使用緩存(以註解的方式即可),使用redis做其實現類是為了可以存更多的數據在機器上
redis緩存單獨使用是為了彌補spring cache集成緩存的不靈活
就我個人而言,如果需要使用分布式緩存,那麼首先redis是必選的,因為在實際開發中,我們會緩存各種各樣的數據類型,在使用了redis的同時,memcached就完全可以舍棄了,但是現在還有很多公司在同時使用memcached和redis兩種緩存。

3. 什麼是分布式緩存

分布式緩存能夠處理大量的動態數據,因此比較適合應用在Web 2.0時代中的社交網站等需要由用戶生成內容的場景。從本地緩存擴展到分布式緩存後,關注重點從CPU、內存、緩存之間的數據傳輸速度差異也擴展到了業務系統、資料庫、分布式緩存之間的數據傳輸速度差異。

常用的分布式緩存包括Redis和Memcached。

Memcached

Memcached是一個高性能的分布式內存對象緩存系統,用於動態Web應用以減輕資料庫負載。Memcached通過在內存中緩存數據和對象來減少讀取資料庫的次數,從而提高動態、資料庫驅動網站的速度。

特點:哈希方式存儲;全內存操作;簡單文本協議進行數據通信;只操作字元型數據;集群由應用進行控制,採用一致性哈希演算法。

限制性:數據保存在內存當中的,一旦機器重啟,數據會全部丟失;只能操作字元型數據,數據類型貧乏;以root許可權運行,而且Memcached本身沒有任何許可權管理和認證功能,安全性不足;能存儲的數據長度有限,最大鍵長250個字元,儲存數據不能超過1M。

Redis

Redis是一個開源的使用ANSI C語言編寫、支持網路、可基於內存亦可持久化的日誌型、Key-Value資料庫,並提供多種語言的API。

特點:

Redis支持的數據類型包括:字元串、string、hash、set、sortedset、list;Redis實現持久化的方式:定期將內存快照寫入磁碟;寫日誌;Redis支持主從同步。

限制性:單核運行,在存儲大數據的時候性能會有降低;不是全內存操作;主從復制是全量復制,對實際的系統運營造成了一定負擔。

4. Spring本地緩存的使用方法

我們現在在用的Spring Cache,可以直接看Spring Boot提供的緩存枚舉類,有如下這些:

EhCache:一個純Java的進程內緩存框架,所以也是基於本地緩存的。(注意EhCache2.x和EhCache3.x相互不兼容)。
Redis:分布式緩存,只有Client-Server(CS)模式,Java一般使用Jedis/Luttuce來操縱。
Hazelcast:基於內存的數據網格。雖然它基於內存,但是分布式應用程序可以使用Hazelcast進行分布式緩存、同步、集群、處理、發布/訂閱消息等。
Guava:它是Google Guava工具包中的一個非常方便易用的本地化緩存實現,基於LRU(最近最少使用)演算法實現,支持多種緩存過期策略。在Spring5.X以後的版本已經將他標記為過期了。
Caffeine:是使用Java8對Guava緩存的重寫版本,在Spring5中將取代了Guava,支持多種緩存過期策略。
SIMPLE:使用ConcurrentMapCacheManager,因為不支持緩存過期時間,所以做本地緩存基本不考慮該方式。

關於分布式緩存,我們需要後面會專門討論Redis的用法,這里只看本地緩存。性能從高到低,依次是Caffeine,Guava,ConcurrentMapCacheManager,其中Caffeine在讀寫上都快了Guava近一倍。

這里我們只討論在Spring Boot裡面怎麼整合使用Caffeine和EhCache。

主要有以下幾個步驟:

1)加依賴包:

2)配置緩存:
這里有兩種方法,通過文件配置或者在配置類裡面配置,先看一下文件配置,我們可以寫一個properties文件,內容像這樣:

然後還要在主類中加上@EnableCaching註解:

另外一種更靈活的方法是在配置類中配置:

應用類:

測試類:

導入依賴包,分為2.x版本和3.x版本。
其中2.x版本做如下導入:

3.x版本做如下導入:

導包完成後,我們使用JCacheManagerFactoryBean + ehcache.xml的方式配置:

參考資料:

https://blog.csdn.net/f641385712/article/details/94982916

http://www.360doc.com/content/17/1017/20/16915_695800687.shtml

5. Redis怎麼實現分布式鎖

阿粉最近迷上了 Redis,為什麼呢?感覺 Redis 確實功能很強大呀,一個基於內存的系統 Key-Value 存儲的資料庫,竟然有這么多的功能,而阿粉也要實實在在地把 Redis 來弄一下,畢竟面試的時候,Redis 可以說是一個非常不錯的加分項。

為什麼需要分布式鎖?

目前很多的大型項目全部都是基於分布式的,而分布式場景中的數據一致性問題一直是一個不可忽視的問題,大家知道關於分布式的 CAP 理論么?

CAP 理論就是說任何一個分布式系統都無法同時滿足一致性(Consistency)、可用性(Availability)和分區容錯性(Partition tolerance),最多隻能同時滿足兩項。

而我們的系統最終滿足的永遠都是最終一致性,而這種最終一致性,有些時候有人會喜歡問關於分布式事務,而有些人則偏重在分布式鎖上。

但是阿粉選擇的就是使用緩存來實現分布式鎖,也就是我們在項目中最經常使用的 Redis ,談到 Redis,那真是可以用在太多地方了,比如說:

我們今天就來實現用 Redis 來實現分布式鎖,並且要學會怎麼使用。

1.准備使用 Jedis 的 jar 包,在項目中導入 jar 包。

jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime); 這個加鎖的姿勢才是我們最需要了解的,不然你用的時候都不知道怎麼使用。

key:加鎖的鍵,實際上就是相當於一個唯一的標志位,不同的業務,你可以使用不同的標志位進行加鎖。

requestId:這個東西實際上就是用來標識他是哪一個請求進行的加鎖,因為在分布式鎖中,我們要知道一件事,就是加鎖的和解鎖的,必須是同一個客戶端才可以。

而且還有一種比較經典的就是 B 把 A 的鎖給釋放了,導致釋放混亂,如果你不加相同的請求,A 線程處理業務,執行了加鎖,鎖的過期時間是5s, B線程嘗試獲取鎖,如果 A 處理業務時間超過5s,這時候 A 就要開始釋放鎖,而B在這時候沒有檢測到這個鎖,從而進行了加鎖,這時候加鎖的時候,A還沒處理完對應業務,當他處理完了之後,再釋放鎖的話,要是就是直接把 B 剛加的鎖釋放了,要麼就是壓根都沒辦法釋放鎖。

SET_IF_NOT_EXIST:看字面意思,如果 key 不存在,我們進行Set操作,如果存在,啥都不幹,也就不在進行加鎖。

SET_WITH_EXPIRE_TIME:是否過期

expireTime:這是給 key 設置一個過期的時間,萬一你這業務一直被鎖著了,然後之後的業務想加鎖,你直接給一直持有這個這個鎖,不進行過期之後的釋放,那豈不是要涼了。

上面的方法中 tryGetDistributedLock 這個方法也就是我們通常使用的加鎖的方法。

大家看到這個 script 的時候,會感覺有點奇怪,實際上他就是一個 Lua 的腳本,而 Lua 腳本的意思也比較簡單。

其實這時候就有些人說,直接 del 刪除不行么?你試試你如果這么寫的話,你們的領導會不會把你的腿給你打斷。

這種不先判斷鎖的擁有者而直接解鎖的方式,會導致任何客戶端都可以隨時進行解鎖,也就是說,這鎖就算不是我加的,我都能開,這怎麼能行呢?

在這里給大家放一段使用的代碼,比較簡單,但是可以直接用到你們的項目當中

我們先把這個實現方式實現了,然後我們再來說說大家最不願意看的理論知識,畢竟這理論知識是你面試的時候經常會被問到的。

分布式CAP理論:

加州大學伯克利分校的 Eric Brewer 教授在 ACM PODC 會議上提出 CAP 猜想。2年後,麻省理工學院的 Seth Gilbert 和 Nancy Lynch 從理論上證明了 CAP。之後,CAP 理論正式成為分布式計算領域的公認定理。

也就是說,在二十年前的時候,CAP 理論只是個猜想。結果兩年之後被證實了,於是,大家在考慮分布式的時候,就有根據來想了,不再是空想了。

什麼是分布式的 CAP 理論 ?

一個分布式系統最多隻能同時滿足一致性(Consistency)、可用性(Availability)和分區容錯性(Partition tolerance)這三項中的兩項

這個和(Atomicity)不太一樣,因為之前看有些人說,在 CAP 理論中的 A 和資料庫事務中的 A 是一樣的,單詞都不一樣,那能一樣么?

Availability :分布式中的 A 表示的是可用性,也就是說服務一直可用,而且是正常響應時間。

而你在搭建分布式系統的時候,要保證每個節點都是穩定的,不然你的可用性就沒有得到相對應的保證,也談不上是什麼分布式了。只能稱之為一個偽分布式。

Consistency: 一致性

也就是說你的更新操作成功並返回客戶端完成後,所有節點在同一時間的數據完全一致,這個如果你在使用 Redis 做數據展示的時候,很多面試官都會問你,那你們是怎麼保證資料庫和緩存的一致性的呢?

畢竟你只是讀取的話,沒什麼問題,但是設計到更新的時候,不管是先寫資料庫,再刪除緩存;還是先刪除緩存,再寫庫,都有可能出現數據不一致的情況。

所以如果你對這個很感興趣,可以研究一下,比如說:

如果你能在面試的時候把這些都給面試官說清楚,至少感覺你應該能達到你自己的工資要求。

Partition tolerance:分區容錯性

分布式系統在遇到某節點或網路分區故障的時候,仍然能夠對外提供滿足一致性和可用性的服務。

其實在 CAP 理論當中,我們是沒有辦法同時滿足一致性、可用性和分區容錯性這三個特性,所以有所取捨就可以了。

關於使用 Redis 分布式鎖,大家學會了么?

6. Redis-分布式緩存一致性解決方案

一致性Hash演算法也使用取模的方法,剛才描述的取模法是對伺服器的數量進行取模,而一致性Hash演算法是對2^32取模
首先,我們把2^32 想像成一個圓,圓上一共有2^32 個點,編號0-2^32-1,這個圓稱為hash環

7. Redis緩存之(一)分布式鎖

Redisson是架設在Redis基礎上的一個Java駐內存數據網格(In-Memory Data Grid)

Ridisson文檔

8. redis是怎麼分布式緩存數據的

Redis使用單線程的IO復用模型,自己封裝了一個簡單的AeEvent事件處理框架,主要實現了epoll、kqueue和select,對於單純只有IO操作來說,單線程可以將速度優勢發揮到最大,但是Redis也提供了一些簡單的計算功能
比如排序、聚合等,對於這些操作,單線程模型實際會嚴重影響整體吞吐量,CPU計算過程中,整個IO調度都是被阻塞住的。

9. redis是怎麼實現的

第一:Redis 是什麼?

Redis是基於內存、可持久化的日誌型、Key-Value資料庫 高性能存儲系統,並提供多種語言的API.

第二:出現背景

  • 數據結構(Data Structure)需求越來越多, 但memcache中沒有, 影響開發效率

  • 性能需求, 隨著讀操作的量的上升需要解決,經歷的過程有:
    資料庫讀寫分離(M/S)–>資料庫使用多個Slave–>增加Cache (memcache)–>轉到Redis

  • 解決寫的問題:
    水平拆分,對表的拆分,將有的用戶放在這個表,有的用戶放在另外一個表;

  • 可靠性需求
    Cache的"雪崩"問題讓人糾結
    Cache面臨著快速恢復的挑戰

  • 開發成本需求
    Cache和DB的一致性維護成本越來越高(先清理DB, 再清理緩存, 不行啊, 太慢了!)
    開發需要跟上不斷湧入的產品需求
    硬體成本最貴的就是資料庫層面的機器,基本上比前端的機器要貴幾倍,主要是IO密集型,很耗硬體;

  • 維護性復雜
    一致性維護成本越來越高;
    BerkeleyDB使用B樹,會一直寫新的,內部不會有文件重新組織;這樣會導致文件越來越大;大的時候需要進行文件歸檔,歸檔的操作要定期做;
    這樣,就需要有一定的down time;

  • 基於以上考慮, 選擇了Redis

    第三:Redis 在新浪微博中的應用

    Redis簡介

    1. 支持5種數據結構

    支持strings, hashes, lists, sets, sorted sets
    string是很好的存儲方式,用來做計數存儲。sets用於建立索引庫非常棒;

    2. K-V 存儲 vs K-V 緩存

    新浪微博目前使用的98%都是持久化的應用,2%的是緩存,用到了600+伺服器
    Redis中持久化的應用和非持久化的方式不會差別很大:
    非持久化的為8-9萬tps,那麼持久化在7-8萬tps左右;
    當使用持久化時,需要考慮到持久化和寫性能的配比,也就是要考慮redis使用的內存大小和硬碟寫的速率的比例計算;

    3. 社區活躍

    Redis目前有3萬多行代碼, 代碼寫的精簡,有很多巧妙的實現,作者有技術潔癖
    Redis的社區活躍度很高,這是衡量開源軟體質量的重要指標,開源軟體的初期一般都沒有商業技術服務支持,如果沒有活躍社區做支撐,一旦發生問題都無處求救;

    Redis基本原理

    redis持久化(aof) append online file:
    寫log(aof), 到一定程度再和內存合並. 追加再追加, 順序寫磁碟, 對性能影響非常小

    1. 單實例單進程

    Redis使用的是單進程,所以在配置時,一個實例只會用到一個CPU;
    在配置時,如果需要讓CPU使用率最大化,可以配置Redis實例數對應CPU數, Redis實例數對應埠數(8核Cpu, 8個實例, 8個埠), 以提高並發:
    單機測試時, 單條數據在200位元組, 測試的結果為8~9萬tps;

    2. Replication

    過程: 數據寫到master–>master存儲到slave的rdb中–>slave載入rdb到內存。
    存儲點(save point): 當網路中斷了, 連上之後, 繼續傳.
    Master-slave下第一次同步是全傳,後面是增量同步;、

    3. 數據一致性

    長期運行後多個結點之間存在不一致的可能性;
    開發兩個工具程序:
    1.對於數據量大的數據,會周期性的全量檢查;
    2.實時的檢查增量數據,是否具有一致性;

    對於主庫未及時同步從庫導致的不一致,稱之為延時問題;
    對於一致性要求不是那麼嚴格的場景,我們只需要要保證最終一致性即可;
    對於延時問題,需要根據業務場景特點分析,從應用層面增加策略來解決這個問題;
    例如:
    1.新注冊的用戶,必須先查詢主庫;
    2.注冊成功之後,需要等待3s之後跳轉,後台此時就是在做數據同步。

    第四:分布式緩存的架構設計

    1.架構設計

    由於redis是單點,項目中需要使用,必須自己實現分布式。基本架構圖如下所示:

    2.分布式實現

    通過key做一致性哈希,實現key對應redis結點的分布。

    一致性哈希的實現:

    lhash值計算:通過支持MD5與MurmurHash兩種計算方式,默認是採用MurmurHash,高效的hash計算.

    l一致性的實現:通過java的TreeMap來模擬環狀結構,實現均勻分布

    3.client的選擇

    對於jedis修改的主要是分區模塊的修改,使其支持了跟據BufferKey進行分區,跟據不同的redis結點信息,可以初始化不同的 ShardInfo,同時也修改了JedisPool的底層實現,使其連接pool池支持跟據key,value的構造方法,跟據不同 ShardInfos,創建不同的jedis連接客戶端,達到分區的效果,供應用層調用

    4.模塊的說明

    l臟數據處理模塊,處理失敗執行的緩存操作。

    l屏蔽監控模塊,對於jedis操作的異常監控,當某結點出現異常可控制redis結點的切除等操作。

    整個分布式模塊通過hornetq,來切除異常redis結點。對於新結點的增加,也可以通過reload方法實現增加。(此模塊對於新增結點也可以很方便實現)

    對於以上分布式架構的實現滿足了項目的需求。另外使用中對於一些比較重要用途的緩存數據可以單獨設置一些redis結點,設定特定的優先順序。另外對 於緩存介面的設計,也可以跟據需求,實現基本介面與一些特殊邏輯介面。對於cas相關操作,以及一些事物操作可以通過其watch機制來實現。

    聲明:所有博客服務於分布式框架,作為框架的技術支持及說明,框架面向企業,是大型互聯網分布式企業架構,後期會介紹linux上部署高可用集群項目。

10. Redis分布式緩存搭建

花了兩天時間整理了之前記錄的Redis單體與哨兵模式的搭建與使用,又補齊了集群模式的使用和搭建經驗,並對集群的一些個原理做了理解。

筆者安裝中遇到的一些問題:

如果make報錯,可能是沒裝gcc或者gcc++編輯器,安裝之 yum -y install gcc gcc-c++ kernel-devel ,有可能還是提示一些個c文件編譯不過,gcc -v查看下版本,如果不到5.3那麼升級一下gcc:

在 /etc/profile 追加一行 source /opt/rh/devtoolset-9/enable

scl enable devtoolset-9 bash

重新make clean, make

這回編譯通過了,提示讓你最好make test一下/

執行make test ,如果提示 You need tcl 8.5 or newer in order to run the Redis test

那就升級tcl, yum install tcl

重新make test,如果還有error就刪了目錄,重新tar包解壓重新make , make test

o/ All tests passed without errors! ,表示編譯成功。

然後make install即可。

直接運行命令: ./redis-server /usr/redis-6.0.3/redis.conf &

redis.conf 配置文件里 bind 0.0.0.0 設置外部訪問, requirepass xxxx 設置密碼

redis高可用方案有兩種:

常用搭建方案為1主1從或1主2從+3哨兵監控主節點, 以及3主3從6節點集群。

(1)sentinel哨兵

/usr/redis-6.0.3/src/redis-sentinel /usr/redis-6.0.3/sentinel2.conf &

sentinel2.conf配置:

坑1:master節點也會在故障轉移後成為從節點,也需要配置masterauth

當kill master進程之後,經過sentinel選舉,slave成為了新的master,再次啟動原master,提示如下錯誤:

原因是此時的master再次啟動已經是slave了,需要向現在的新master輸入密碼,所以需要在master.conf
中配置:

坑2:哨兵配置文件要暴露客戶端可以訪問到的master地址

在 sentinel.conf 配置文件的 sentinel monitor mymaster 122.xx.xxx.xxx 6379 2 中,配置該哨兵對應的master名字、master地址和埠,以及達到多少個哨兵選舉通過認為master掛掉。其中master地址要站在redis訪問者(也就是客戶端)的角度、配置訪問者能訪問的地址,例如sentinel與master在一台伺服器(122.xx.xxx.xxx)上,那麼相對sentinel其master在本機也就是127.0.0.1上,這樣 sentinel monitor mymaster 127.0.0.1 6379 2 邏輯上沒有問題,但是如果另外伺服器上的springboot通過lettuce訪問這個redis哨兵,則得到的master地址為127.0.0.1,也就是springboot所在伺服器本機,這顯然就有問題了。

附springboot2.1 redis哨兵配置:

坑3:要注意配置文件.conf會被哨兵修改

redis-cli -h localhost -p 26379 ,可以登到sentinel上用info命令查看一下哨兵的信息。

曾經遇到過這樣一個問題,大致的信息如下

slaves莫名其妙多了一個,master的地址也明明改了真實對外的地址,這里又變成127.0.0.1 !
最後,把5個redis進程都停掉,逐個檢查配置文件,發現redis的配置文件在主從哨兵模式會被修改,master的配置文件最後邊莫名其妙多了一行replicaof 127.0.0.1 7001, 懷疑應該是之前配置錯誤的時候(見坑2)被哨兵動態加上去的! 總之,實踐中一定要多注意配置文件的變化。

(2)集群

當數據量大到一定程度,比如幾十上百G,哨兵模式不夠用了需要做水平拆分,早些年是使用codis,twemproxy這些第三方中間件來做分片的,即 客戶端 -> 中間件 -> Redis server 這樣的模式,中間件使用一致性Hash演算法來確定key在哪個分片上。後來Redis官方提供了方案,大家就都採用官方的Redis Cluster方案了。

Redis Cluster從邏輯上分16384個hash slot,分片演算法是 CRC16(key) mod 16384 得到key應該對應哪個slot,據此判斷這個slot屬於哪個節點。

每個節點可以設置1或多個從節點,常用的是3主節點3從節點的方案。

reshard,重新分片,可以指定從哪幾個節點移動一些hash槽到另一個節點去。重新分片的過程對客戶端透明,不影響線上業務。

搭建Redis cluster

redis.conf文件關鍵的幾個配置:

啟動6個集群節點

[root@VM_0_11_centos redis-6.0.3]# ps -ef|grep redis
root 5508 1 0 21:25 ? 00:00:00 /usr/redis-6.0.3/src/redis-server 0.0.0.0:7001 [cluster]
root 6903 1 0 21:32 ? 00:00:00 /usr/redis-6.0.3/src/redis-server 0.0.0.0:7002 [cluster]
root 6939 1 0 21:33 ? 00:00:00 /usr/redis-6.0.3/src/redis-server 0.0.0.0:7003 [cluster]
root 6966 1 0 21:33 ? 00:00:00 /usr/redis-6.0.3/src/redis-server 0.0.0.0:7004 [cluster]
root 6993 1 0 21:33 ? 00:00:00 /usr/redis-6.0.3/src/redis-server 0.0.0.0:7005 [cluster]
root 7015 1 0 21:33 ? 00:00:00 /usr/redis-6.0.3/src/redis-server 0.0.0.0:7006 [cluster]

這時候這6個節點還是獨立的,要把他們配置成集群:

說明: -a xxxx 是因為筆者在redis.conf中配置了requirepass xxxx密碼,然後 --cluster-replicas 1 中的1表示每個master節點有1個從節點。

上述命令執行完以後會有一個詢問: Can I set the above configuration? yes同意自動做好的分片即可。

最後 All 16384 slots covered. 表示集群中16384個slot中的每一個都有至少有1個master節點在處理,集群啟動成功。

查看集群狀態:

坑1:暴露給客戶端的節點地址不對

使用lettuce連接發現連不上,查看日誌 Connection refused: no further information: /127.0.0.1:7002 ,跟之前哨兵配置文件sentinel.conf里邊配置master地址犯的錯誤一樣,集群啟動的時候帶的地址應該是提供給客戶端訪問的地址。

我們要重建集群:先把6個redis進程停掉,然後刪除 nodes-7001.conf 這些節點配置文件,刪除持久化文件 mp.rdb 、 appendonly.aof ,重新啟動6個進程,在重新建立集群:

然後,還是連不上,這次報錯 connection timed out: /172.xx.0.xx:7004 ,發現連到企鵝雲伺服器的內網地址上了!

解決辦法,修改每個節點的redis.conf配置文件,找到如下說明:

所以增加配置:

然後再重新構建集群,停進程、改配置、刪除節點文件和持久化文件、啟動進程、配置集群。。。再來一套(累死了)

重新使用Lettuce測試,這次終於連上了!

坑2:Lettuce客戶端在master節點故障時沒有自動切換到從節點

name這個key在7002上,kill這個進程模擬master下線,然後Lettuce一直重連。我們期望的是應該能自動切換到其slave 7006上去,如下圖:

重新啟動7002進程,

7006已成為新master,7002成為它的slave,然後Lettuce也能連接上了。
解決辦法,修改Lettuce的配置:

筆者用的是springboot 2.1 spring-boot-starter-data-redis 默認的Lettuce客戶端,當使用Redis cluster集群模式時,需要配置一下 RedisConnectionFactory 開啟自適應刷新來做故障轉移時的自動切換從節點進行連接。

重新測試:停掉master 7006,這次Lettuce可以正常切換連到7002slave上去了。(仍然會不斷的在日誌里報連接錯誤,因為需要一直嘗試重連7006,但因為有7002從節點頂上了、所以應用是可以正常使用的)

Redis不保證數據的強一致性

Redis並不保證數據的強一致性,也就是取CAP定理中的AP

關於一致性Hash演算法,可以參考 一致性Hash演算法 - (jianshu.com)

Redis cluster使用的是hash slot演算法,跟一致性Hash演算法不太一樣,固定16384個hash槽,然後計算key落在哪個slot里邊(計算key的CRC16值再對16384取模),key找的是slot而不是節點,而slot與節點的對應關系可以通過reshard改變並通過gossip協議擴散到集群中的每一個節點、進而可以為客戶端獲知,這樣key的節點定址就跟具體的節點個數沒關系了。也同樣解決了普通hash取模演算法當節點個數發生變化時,大量key對應的定址都發生改動導致緩存失效的問題。

比如集群增加了1個節點,這時候如果不做任何操作,那麼新增加的這個節點上是沒有slot的,所有slot都在原來的節點上且對應關系不變、所以沒有因為節點個數變動而緩存失效,當reshard一部分slot到新節點後,客戶端獲取到新遷移的這部分slot與新節點的對應關系、定址到新節點,而沒遷移的slot仍然定址到原來的節點。

關於熱遷移,猜想,內部應該是先做復制遷移,等遷移完了,再切換slot與節點的對應關系,復制沒有完成之前仍按照原來的slot與節點對應關系去原節點訪問。復制結束之後,再刪除原節點上已經遷移的slot所對應的key。

與哨兵模式比較類似,當1個節點發現某個master節點故障了、會對這個故障節點進行pfail主觀宕機,然後會通過gossip協議通知到集群中的其他節點、其他節點也執行判斷pfail並gossip擴散廣播這一過程,當超過半數節點pfail時那麼故障節點就是fail客觀宕機。接下來所有的master節點會在故障節點的從節點中選出一個新的主節點,此時所有的master節點中超過半數的都投票選舉了故障節點的某個從節點,那麼這個從節點當選新的master節點。

所有節點都持有元數據,節點之間通過gossip這種二進制協議進行通信、發送自己的元數據信息給其他節點、故障檢測、集群配置更新、故障轉移授權等等。

這種去中心化的分布式節點之間內部協調,包括故障識別、故障轉移、選主等等,核心在於gossip擴散協議,能夠支撐這樣的廣播協議在於所有的節點都持有一份完整的集群元數據,即所有的節點都知悉當前集群全局的情況。

Redis高可用方案 - (jianshu.com)

面試題:Redis 集群模式的工作原理能說一下么 - 雲+社區 - 騰訊雲 (tencent.com)

深度圖解Redis Cluster原理 - detectiveHLH - 博客園 (cnblogs.com)

Redis學習筆記之集群重啟和遇到的坑-阿里雲開發者社區 (aliyun.com)

雲伺服器Redis集群部署及客戶端通過公網IP連接問題