對於一款已經發展了十多年的產品來說,發現其中的技術已經過時也就不足為奇了。 但是,如果六個月內您必須保持高 10 倍的負載,並且跌倒的成本將增加數百倍怎麼辦? 在這種情況下,您需要一位出色的高負載工程師。 但在沒有女傭的情況下,他們就委託我來解決這個問題。 在文章的第一部分中,我將告訴您我們如何從 Redis 遷移到 Redis-cluster,在第二部分中,我將給出有關如何開始使用叢集以及使用叢集時需要注意的事項的建議。
技術選型
有那麼糟嗎? 單獨的Redis (獨立redis)在1個主伺服器和N個從伺服器的設定? 為什麼我稱之為過時的技術?
不,Redis 並沒有那麼糟……但是,也有一些不容忽視的缺點。
-
首先,Redis不支援master故障後的災難復原機制。 為了解決這個問題,我們使用了一個配置,可以自動將 VIP 轉移到新的主伺服器,更改其中一個從伺服器的角色並切換其餘的伺服器。 這種機制確實有效,但還不能稱為可靠的解決方案。 首先會出現誤報,其次是一次性的,使用後需要手動對彈簧儲能。
-
其次,只有一個master導致了分片的問題。 我們必須創建幾個獨立的叢集“1 個主伺服器和N 個從伺服器”,然後在這些機器之間手動分配資料庫,並希望明天其中一個資料庫不會膨脹太多,以至於必須將其移動到單獨的實例。
有什麼選擇?
- 最昂貴、最豐富的解決方案是 Redis-Enterprise。 這是一個盒裝解決方案,提供全面的技術支援。 儘管從技術角度來看它看起來很理想,但出於意識形態原因它並不適合我們。
- Redis 集群。 開箱即用地支援主故障轉移和分片。 介面與普通版本幾乎沒有區別。 看起來很有希望,我們稍後會討論其中的陷阱。
- Tarantool、Memcache、Aerospike 等。 所有這些工具都做幾乎相同的事情。 但每個都有其自身的缺點。 我們決定不把所有雞蛋放在同一個籃子裡。 我們使用 Memcache 和 Tarantool 來完成其他任務,展望未來,我會說在我們的實踐中它們有更多問題。
使用細節
讓我們來看看歷史上我們使用 Redis 解決了哪些問題以及使用了哪些功能:
- 在向 2GIS 等遠端服務發出請求之前進行快取 | 戈蘭
取得設定 MGET MSET“選擇資料庫”
- MYSQL之前的快取 | PHP
取得設定 MGET MSET 掃描“按模式鍵入”“選擇資料庫”
- 會話和驅動程式座標工作服務的主要儲存 | 戈蘭
取得設定 MGET MSET「選擇資料庫」「新增地理金鑰」「取得地理金鑰」掃描
正如你所看到的,沒有高等數學。 那麼有什麼困難呢? 讓我們分別看看每個方法。
方法
描述
Redis集群的特點
解決方法
開始設置
寫/讀鍵
MGET MSET
寫入/讀取多個金鑰
密鑰將位於不同的節點上。 現成的函式庫只能在一個節點內執行多種操作
將 MGET 替換為包含 N 個 GET 操作的管道
選擇資料庫
選擇我們將合作的基地
不支援多資料庫
將所有內容放入一個資料庫中。 為鍵添加前綴
SCAN
遍歷資料庫中的所有鍵
由於我們只有一個資料庫,因此遍歷叢集中的所有鍵成本太高
維護一個鍵內的不變量並對該鍵執行 HSCAN。 或完全拒絕
GEO
使用 geokey 進行操作
geokey 未分片
按模式鍵入
按模式搜尋鍵
由於我們只有一個資料庫,因此我們將搜尋叢集中的所有鍵。 太貴了
拒絕或維持不變式,如 SCAN 的情況
Redis 與 Redis 集群
切換到叢集時我們會失去什麼,又會得到什麼?
- 缺點:我們失去了一些資料庫的功能。
- 如果我們想將邏輯上不相關的資料儲存在一個叢集中,我們就必須以前綴的形式做拐杖。
- 我們遺失了所有「基本」操作,例如 SCAN、DBSIZE、CLEAR DB 等。
- 多操作變得更加難以實現,因為它可能需要存取多個節點。
- 優點:
- 以主故障轉移形式的容錯。
- Redis 端的分片。
- 以原子方式在節點之間傳輸數據,無需停機。
- 新增並重新分配容量和負載,無需停機。
我的結論是,如果您不需要提供高水準的容錯能力,那麼遷移到集群就不值得,因為這可能是一項不平凡的任務。 但是,如果您最初在獨立版本和集群版本之間進行選擇,那麼您應該選擇集群,因為它並不更糟,而且還能減輕您的一些麻煩
準備搬家
我們先來談談搬家的要求:
- 它應該是無縫的。 完全停止服務 5 分鐘不適合我們。
- 它應該盡可能安全和漸進。 我想對局勢有所控制。 我們不想立即轉儲所有內容並祈禱回滾按鈕。
- 移動時資料遺失最少。 我們知道原子移動非常困難,因此我們允許常規 Redis 和群集 Redis 中的資料之間存在一定程度的不同步。
集群維護
在搬家之前,我們應該考慮是否可以支援集群:
- 圖表。 我們使用 Prometheus 和 Grafana 來繪製 CPU 負載、記憶體使用量、客戶端數量、GET、SET、AUTH 操作數量等圖表。
- 專業知識。 想像一下,明天您將負責一個巨大的集群。 如果它壞了,除了你之外沒有人可以修復它。 如果他開始放慢速度,每個人都會跑向你。 如果您需要新增資源或重新分配負載,請回來找您。 為了不在 25 歲時變灰,建議考慮這些情況並提前檢查該技術在某些操作下的表現。 讓我們在「專業知識」部分更詳細地討論這一點。
- 監控和警報。 當叢集發生故障時,您希望成為第一個知道的人。 在這裡,我們僅限於通知所有節點返回有關叢集狀態的相同資訊(是的,它的發生方式不同)。 而其他問題可以透過 Redis 用戶端服務的警報更快發現。
路口
我們將如何移動:
- 首先,您需要準備一個與叢集一起使用的庫。 我們以 go-redis 作為 Go 版本的基礎,並對其進行了一些更改以適合我們自己。 我們透過管道實現了多方法,並且也稍微修正了重複請求的規則。 PHP版本有更多問題,但我們最終選擇了php-redis。 他們最近引入了集群支持,我們認為它看起來不錯。
- 接下來您需要部署叢集本身。 這實際上是根據設定檔透過兩個命令完成的。 我們將在下面更詳細地討論該設定。
- 對於逐漸移動,我們使用乾燥模式。 由於我們有兩個版本的庫具有相同的介面(一個用於常規版本,另一個用於叢集),因此創建一個包裝器無需花費任何成本,該包裝器將與單獨的版本一起使用並並行地將所有請求複製到集群,比較回應並在日誌中寫入差異(在我們的例子中是 NewRelic)。 這樣,即使叢集版本在上線過程中出現故障,我們的生產也不會受到影響。
- 在乾模式下推出集群後,我們可以冷靜地查看響應差異圖。 如果錯誤率緩慢但穩定地向某個小常數移動,那麼一切都很好。 為什麼仍然存在差異? 由於單獨版本中的記錄比群集中的記錄發生得稍早,並且由於微滯後,數據可能會出現偏差。 剩下的就是查看差異日誌,如果它們都可以透過記錄的非原子性來解釋,那麼我們就可以繼續。
- 現在您可以向相反方向切換乾燥模式。 我們將從叢集中寫入和讀取,並將其複製到單獨的版本中。 為了什麼? 在接下來的一周裡,我想觀察集群的工作。 如果突然發現尖峰負載出現問題,或者我們沒有考慮到某些問題,那麼借助乾燥模式,我們總是可以緊急回滾到舊代碼和當前資料。
- 剩下的就是禁用乾燥模式並拆除單獨的版本。
專業知識
首先簡單介紹一下叢集設計。
首先,Redis 是一個鍵值儲存。 任意字串用作鍵。 數字、字串和整個結構都可以用作值。 後者有很多,但對於理解一般結構來說,這對我們來說並不重要。
鍵之後的下一個抽象層級是槽(SLOTS)。 每個金鑰屬於 16 個插槽之一。 每個槽內可以有任意數量的鑰匙。 因此,所有鍵都被分成 383 個不相交的集合。
接下來,叢集中必須有N個主節點。 每個節點都可以被視為一個單獨的 Redis 實例,它了解叢集中其他節點的所有資訊。 每個主節點包含多個插槽。 每個插槽只屬於一個主節點。 所有時隙都需要在節點之間分配。 如果某些插槽未分配,則儲存在其中的金鑰將無法存取。 在單獨的邏輯或實體機器上運行每個主節點是有意義的。 還值得記住的是,每個節點僅在一個核心上運行,如果您想在同一台邏輯機器上運行多個Redis 實例,請確保它們在不同的核心上運行(我們還沒有嘗試過這一點,但理論上它應該可以工作) 。 本質上,主節點提供定期分片,更多主節點允許擴展寫入和讀取請求。
當所有金鑰都分佈在槽之間,並且槽分散在主節點之間之後,每個主節點可以添加任意數量的從節點。 在每個這樣的主從連結中,正常複製將起作用。 需要從屬設備來擴展讀取請求並在主設備發生故障時進行故障轉移。
現在我們來談談最好能做的操作。
我們將透過 Redis-CLI 存取系統。 由於 Redis 沒有單一入口點,因此您可以在任何節點上執行以下操作。 在每一點上,我都分別提請注意在負載下執行操作的可能性。
- 我們首先需要做的也是最重要的事情是叢集節點的操作。 它會傳回叢集的狀態,顯示節點列表、它們的角色、插槽分佈等。 可以使用集群資訊和集群槽獲取更多資訊。
- 如果能夠新增和刪除節點就太好了。 為此,存在集群相遇和集群遺忘操作。 請注意,叢集遺忘必須應用於每個節點,包括主節點和副本節點。 而cluster meet只需要在一個節點上呼叫即可。 這種差異可能會令人不安,因此最好在使用叢集之前了解它。 添加節點是在戰鬥中安全完成的,不會以任何方式影響叢集的運作(這是合乎邏輯的)。 如果您要從叢集中刪除節點,則應確保該節點上沒有剩餘插槽(否則您可能會失去對該節點上所有金鑰的存取權限)。 另外,不要刪除有從屬設備的主設備,否則將對新主設備進行不必要的投票。 如果節點不再有槽,那麼這是一個小問題,但是如果我們可以先刪除從節點,為什麼我們需要額外的選擇?
- 如果需要強制交換主從位置,那麼叢集故障轉移指令就可以了。 在戰鬥中調用它時,您需要了解主人在操作過程中將無法使用。 通常,切換發生的時間不到一秒,但不是原子的。 您可以預期,在此期間,向 master 發出的某些請求將會失敗。
- 從叢集中刪除節點之前,該節點上不應留下任何插槽。 最好使用 cluster reshard 指令重新指派它們。 插槽將從一個主機轉移到另一個主機。 整個操作可能需要幾分鐘,這取決於傳輸的資料量,但傳輸過程是安全的,不會以任何方式影響叢集的運作。 因此,所有資料都可以在負載下直接從一個節點傳輸到另一個節點,而無需擔心其可用性。 然而,也有一些微妙之處。 首先,資料傳輸與接收方和發送方節點上的某一負載相關。 如果接收節點的處理器負載已經很重,那麼您不應該透過接收新資料來載入它。 其次,一旦發送主設備上沒有剩餘的時隙,其所有從設備將立即轉到這些時隙被傳輸到的主設備。 問題是所有這些從站都希望立即同步資料。 如果是部分同步而不是完全同步,你會很幸運。 考慮到這一點,並結合傳輸插槽和禁用/傳輸從屬設備的操作。 或希望你有足夠的安全邊際。
- 如果在轉帳過程中,您發現自己的名額遺失了,該怎麼辦? 我希望這個問題不會影響您,但如果影響了,則有一個叢集修復操作。 至少,她會以隨機順序將插槽分散在節點上。 我建議先從叢集中刪除具有分散式插槽的節點來檢查其操作。 由於未分配槽中的資料已經不可用,因此擔心這些槽的可用性問題為時已晚。 反過來,該操作不會影響分佈式槽。
- 另一個有用的操作是監視。 它允許您即時查看發送到節點的請求的完整清單。 此外,您可以grep它並查看是否有必要的流量。
另外值得一提的是主故障轉移過程。 簡而言之,它存在,而且在我看來,它效果很好。 但是,不要以為在有主節點的機器上拔掉電源線,Redis會立即切換,客戶端不會注意到這種情況。 在我的實踐中,切換發生在幾秒鐘內。 在此期間,部分資料將不可用:偵測到主站不可用,節點投票選出新主站,切換從站,同步資料。 確保該計劃有效的最佳方法是進行本地練習。 在筆記型電腦上啟動集群,為其提供最小負載,模擬崩潰(例如,透過封鎖連接埠),並評估切換速度。 我認為,只有這樣玩了一兩天,你才能對科技的運作有信心。 好吧,或者希望互聯網上一半人使用的軟體可能有效。
組態
通常,配置是您開始使用工具所需的第一件事。當一切正常時,您甚至不想觸及配置。 強迫自己回到設定並仔細檢查它們需要花費一些努力。 在我的記憶中,由於配置不注意,我們至少發生過兩次嚴重的故障。 特別注意以下幾點:
- 超時0
非活動連線關閉之前的時間(以秒為單位)。 0 - 不關閉
並非我們的每個庫都能夠正確關閉連線。 透過停用此設置,我們可能會遇到客戶端數量限制的風險。 另一方面,如果存在這樣的問題,那麼丟失連接的自動終止會掩蓋它,而我們可能不會注意到。 此外,在使用持久連線時不應啟用此設定。 - 保存 xy 並附加 是
保存 RDB 快照。
我們將在下面詳細討論 RDB/AOF 問題。 - stop-writes-on-bgsave-error 否 & Slave-serve-stale-data 是
如果啟用,如果 RDB 快照損壞,master 將停止接受變更要求。 如果與主站的連線遺失,從站可以繼續回應請求(是)。 或將停止響應(否)
我們對 Redis 變成南瓜的情況並不滿意。 - repl-ping-從屬週期 5
這段時間過後,我們會開始擔心master已經壞掉了,是時候執行failover程式了。
您必須手動在誤報和觸發故障轉移之間找到平衡。 在我們的實踐中,該時間為 5 秒。 - repl-backlog-size 1024mb & epl-backlog-ttl 0
我們可以將這麼多資料儲存在失敗副本的緩衝區中。 如果緩衝區用完,您將必須完全同步。
實踐表明,設定一個較高的值會更好。 副本可能開始滯後的原因有很多。 如果它滯後,那麼很可能你的主人已經在努力應對,而完全同步將是最後一根稻草。 - 最大客戶數 10000
一次性客戶的最大數量。
根據我們的經驗,最好設定一個更高的值。 Redis 可以很好地處理 10k 連接。 只需確保系統上有足夠的套接字即可。 - 最大記憶體策略 易失性 ttl
達到可用記憶體限制時刪除鍵的規則。
這裡重要的不是規則本身,而是理解這將如何發生。 Redis 值得稱讚的是,它在達到記憶體限制時仍能正常運作。
RDB 和 AOF 問題
雖然Redis本身將所有資訊儲存在RAM中,但也有一種將資料保存到磁碟的機制。 更準確地說,三種機制:
- RDB-snapshot - 所有資料的完整快照。 使用 SAVE XY 配置進行設置,並讀取「如果至少 Y 個鍵已更改,則每 X 秒儲存所有資料的完整快照」。
- 僅附加文件 - 按執行順序排列的操作清單。 每 X 秒或每 Y 操作向檔案新增新的傳入操作。
- RDB和AOF是前兩者的組合。
所有方法都有其優點和缺點,我不會全部列出,我只會提請注意我認為不明顯的點。
首先,保存RDB快照需要呼叫FORK。 如果資料量很大,這可能會導致所有 Redis 掛起幾毫秒到一秒的時間。 此外,系統需要為這樣的快照分配內存,這導致需要在邏輯機上保留雙倍的 RAM:如果為 Redis 分配 8 GB,則虛擬機上應有 16 GB 可用它。
其次,存在部分同步的問題。 AOF模式下,當slave重新連接時,可以進行全同步,而不是部分同步。 為什麼會發生這種情況,我無法理解。 但值得記住這一點。
這兩點已經讓我們思考,如果所有內容都已從屬設備複製,我們是否真的需要磁碟上的這些資料。 只有當所有從站都發生故障時,資料才會遺失,這是「DC 火災」等級的問題。 作為折衷方案,您可以建議僅在從屬設備上保存數據,但在這種情況下,您需要確保這些從屬設備在災難恢復期間永遠不會成為主設備(為此,在其配置中存在從屬設備優先級設定)。 對於我們自己來說,在每種具體情況下,我們都會考慮是否有必要將資料保存到磁碟,大多數情況下答案是「否」。
結論
總而言之,我希望能夠讓那些根本沒有聽說過 redis-cluster 的人大致了解它是如何運作的,同時也讓那些已經使用過它的人注意一些不明顯的點許久。
感謝您抽出寶貴的時間,一如既往,歡迎就該主題發表評論。
來源: www.habr.com