李 翀, 劉利娜,2, 劉學敏, 張士波
1(中國科學院 計算機網絡信息中心, 北京 100190)
2(中國科學院大學, 北京 100049)
隨著Web2.0和大數據時代到來, 單機緩存系統已無法承受當下大型互聯網應用高并發、大數據、快速響應的需求.分布式緩存[1]是學術界和工業界公認的解決方案, 通過將數據和請求分布到不同節點, 實現水平擴展和負載均衡, 進而提高并發數、數據吞吐量和快速響應能力.目前分布式緩存系統的主流實現方式主要有兩種:基于Memcached[2]和基于Redis[3]的分布式緩存系統.Memcached是一種開源高性能分布式內存對象緩存系統, 通過減少數據庫負載來實現對動態Web應用加速.但由于其支持數據類型單一、客戶端實現分片方式導致集群擴容困難、一致性維護成本、不支持數據持久化和內存管理方式導致內存浪費等問題.Redis是一種開源基于內存鍵值對存儲數據庫, 具有分布式緩存、高并發快速訪問、豐富的數據類型、數據持久化及備份機制、消息隊列機制、高擴展性和可維護性等優點, 使得基于Redis構建分布式緩存系統開始成為熱點, 其分布式主要通過分片實現, 具體方式主要有三種:基于客戶端、基于Proxy和基于Redis Cluster[4,5]的分片方式.
基于客戶端的分片方式, 因其水平擴展困難、業務變更導致不可控等原因, 實際應用較少.而基于Proxy的分片方式在開發簡單, 運維便捷, 對應用幾乎透明, 相對比較成熟, 例如 Twemproxy[6]和 Codis[7].但是隨著系統規模增大, 逐漸暴露出代理影響性能、組件較多復雜, 對資源消耗較大、擴展復雜、代碼升級困難、硬件環境要求高等缺點.
Redis Cluster成為研究熱點.Redis3.0 以后開始支持 Redis Cluster, 目前最新穩定版本 Redis4.0于2017年7月發布, 因剛發布不久, 可借鑒解決案例不多.同時, Redis4.0 Cluster官方給出的 Redis Cluster部署方案比較單一, 集群數量很大時部署不便, 也沒有較好的可視化監控和管理工具, 當業務擴大, 集群節點成百上千的時候, 命令行運維工具將會十分困難.基于以上原因, 本文基于Redis4.0研究并實現了一種Redis Cluster分布式緩存系統[8,9], 集成了CacheCloud[10]開源可視化工具進行實時監控和管理, 并進行了功能和性能驗證.實驗表明, 該系統各功能高效運轉, 管理便捷,可擴展性強, 體現了高可用、高性能和高擴展的特點.
Redis Cluster是基于 Redis 的一個分布式實現, 由Redis官網推出;它引入了哈希槽的概念;支持動態添加或刪除節點, 可線性擴展至1000個節點;多個Redis節點間數據共享, 部分節點不可達時, 集群仍然可用;數據通過異步復制, 不保證數據的強一致性;具備自動Failover的能力;客戶端直接連接Redis Server,免去了Proxy的性能損耗.
Redis Cluster是一種去中心化結構, 如圖 1 所示,所有節點之間互相連接, 通過Gossip協議來發布廣播消息, 每間隔時間內互發PING/PONG心跳包來檢測其他節點狀態, 來保持信息同步.客戶端直接連接任意Redis Server, 并由 Redis Cluster路由轉發客戶端請求到真正請求數據的節點.
Redis Cluster中每個Redis節點需要打開兩個TCP連接, 一個是為客戶端提供服務的常規端口, 另一個是節點間互相通信所需要的端口.這兩種端口之間偏移量是 10 000.對于分片, Redis Cluster并沒有使用一致性哈希來實現, 而是引入了哈希槽的概念.Redis Cluster共有 16 384個哈希槽, 每個 Master節點只負責一部分哈希槽, 每個Key通過公式(1)計算出屬于哪個節點.


圖1 Redis Cluster拓撲圖
CAP 即 Consistency(一致性)、Availability(可用性)、Partition tolerance(分區容錯性)[11,12], 在分布式系統中不可兼得.Redis Cluster的設計目標主要是高性能、高可用和高擴展, 但犧牲了部分的數據一致性.
(1) 一致性
是指對所有節點數據訪問一致性.Redis Cluster無法保證強一致性.由于集群使用異步復制, 在某些情況下, Redis Cluster可能會丟失系統向客戶確認的寫入.性能和一致性之間需要權衡, Redis集群在絕對需要時支持同步寫入的時候, 可以通過WAIT命令實現, 但是這使得丟失寫入的可能性大大降低.
(2) 可用性
可用性是對集群整體是否可用的衡量, 即當集群中一部分節點故障后, 集群整體是否還能響應客戶端的讀寫請求.Redis集群在分區的少數節點那邊不可用.一個由N個主節點組成的集群, 每個主節點都只有一個從節點.當有一個節點被分割出去后, 集群的多數節點這邊仍然是可訪問的.當有兩個節點被分割出去后集群仍可用的概率是 1–(1/(N×2–1)).如一個擁有6個節點的集群, 每個節點有一個從節點, 那么在兩個節點從多數節點這邊分割出去后集群不再可用的概率是1/(6×2–1) = 0.090909, 即有大約 9% 的概率.
(3) 高性能和線性擴展
當操作某個 Key 時, Redis Cluster節點不會像代理一樣直接找到負責這個Key的節點并執行命令, 而是將客戶端重定向到負責這個Key的節點.通過對Key的操作, 客戶端會記錄路由信息, 最終客戶端獲得每個節點負責的Key最新信息, 所以一般情況下, 對于給定的操作, 客戶端會直接連接正確的節點并執行命令.Redis Cluster不支持同時操作多個鍵值, 避免了數據在節點間來回移動.但普通操作是可以被處理得跟在單一Redis上一樣的, 這意味著在一個擁有N個主節點的Redis集群中, 由于Redis的設計是支持線性擴展的, 所以同樣的操作在集群上的表現會跟在單一Redis上的表現乘以N一樣.
節點定時向其他節點發送ping命令, 它會隨機選擇存儲的其他集群節點的其中三個進行信息“廣播”,廣播的信息包含一項是節點是否被標記為PFAIL/FAIL.如圖 2 所示, 超過一半的 Master節點也就是A和B節點同時判定C節點失效, 那么C節點將會被標為FAIL, 這個節點已失效的信息會被廣播至整個集群, 所有集群中的節點都會將失效的節點標志為FAIL, 即失效.又如圖中B節點收到A節點判定失效,此時被標為PFAIL狀態, 等待其主他節點共同判定后才能決定是否失效.

圖2 Redis Cluster容錯圖
如果集群任意Master掛掉, 且當前Master沒有Slave, 該 Master對應哈希槽無法使用, 集群不可用.如果有Slave節點, 將會進行 Slave選舉, 最終選擇Slave成為主節點.如果集群超過半數以上Master掛掉, 無論是否有Slave集群進入fail狀態.
Redis Cluster搭建于 4 臺虛擬機上, 虛擬機具體信息見2.2運行環境中的表2.由于Redis是單線程模型,一個Redis服務進程只會使用一個內核, 為了減少線程切換的開銷, 提升Redis的吞吐量和優化Redis性能,同時考慮到虛擬機內核數和內存大小, 因此集群設計為12個主節點, 每個節點對應Maxmemory為512 MB, 每個主節點對應兩個從節點, 集群共36個節點,任意節點之間通過 Cluster Bus, 即集群總線, 互相連接.集群主從節點詳細信息見表1, 圖3是集群架構縮略圖, 其中數字, 如:8000, 為虛擬機端口.

表1 Redis Cluster節點信息

圖3 Redis Cluster架構縮略圖
搭建 Redis Cluster是基于 Redis最新版本 4.0, 具體版本為 Redis 4.0.1, 由于 Redis Cluster搭建依賴Ruby環境, 所以使用了Ruby2.5.0版本.
系統運行環境通過4臺服務器模擬, 虛擬機信息如表2所示.
(1) Linux 系統性能配置
1) sysctlvm.overcommit_memory=1, 該設置用來防止內存申請不到發生卡死, 造成fork失敗的情況.

表2 服務器集群環境
2) sysctlvm.swappiness=20, 當 swappiness 為 20 時表示內存在使用到80%的時候, 就開始出現有交換分區的使用.Redis運行時根據此數據來選擇是否將內存中數據swap到硬盤.
3) Transparent Huge Pages (THP)[13]Linux kernel在2.6.38內核增加了THP特性, 支持大內存頁(2 MB)分配, 默認開啟.當開啟時可以降低 fork 子進程的速度, 但 fork操作之后, 每個內存頁從原來 4 KB變為2 MB, 會大幅增加重寫期間父進程內存消耗.操作命令如下:

4) sysctl -w net.ipv4.tcp_timestamps=1, 表示開啟對于TCP時間戳的支持, 若該項設置為0, 則下面一項net.ipv4.tcp_tw_recycle=1設置不起作用.
5) sysctl -w net.ipv4.tcp_tw_recycle=1, 表示開啟TCP 連接中 TIME-WAIT sockets的快速回收.對于Redis客戶端連接數比較多時, 釋放連接數.
6) sysctlnet.core.somaxconn=65535, 結合 Redis 配置參數tcp-backlog, 當系統并發量大并且客戶端速度緩慢的時候, 設置這二個參數.主要為了提高Redis的并發數.
(2) CPU 綁定
Redis是單線程模型, 一個Redis服務進程只會使用一個內核, 為了減少線程切換的開銷, 提升Redis的吞吐量和優化Redis性能, 因此, 選擇將Redis進程綁定到固定CPU上.以下是具體綁定步驟:
1) 從獲取 Redis 節點 PID:ps -ef | grep redis;
2) 查看 CPU 信息:cat/proc/cpuinfo;
3) 綁定 CPU:taskset-cp x PID
(3) Redis配置信息
以172.17.0.213:8000節點為例, 其他節點配置除了端口號、bind綁定的IP和PID、RDB、AOF、log等文件名稱不一樣外, 其他信息一致.

#考慮安全性問題
port 8000#節點監聽端口, 安全起見, 不使用默認6379
bind 172.17.0.213#綁定內網 IP, 防止外網訪問, 防攻擊
protected-mode yes#當開啟后, 禁止公網訪問Redis
tcp-backlog 65535#確定TCP連接中已完成隊列長度
tcp-keepalive 300#周期性檢測客戶端是否還處于健康狀###態, 避免服務器一直阻塞
maxclients 1000000#最大客戶端連接數, 提高并發量
cluster-node-timeout 15000
##集群節點的不可用時間(超時時間), 超過這個時間就會##被認為下線, 單位是毫秒
cluster-slave-validity-factor 1
#集群主從切換的控制因素之一, 若大于0, 主從間的最大#斷線時間通過node timeout*cluster-nodetimeout來計算
cluster-migration-barrier 1
##與同一主節點連接的從節點最少個數
cluster-require-full-coverage no
#默認值為yes, 全部hash slots都正常集群才可用,若為##no, 那么可以允許部分哈希槽下線的情況下繼續使用
maxmemory512M#節點最大使用內存
maxmemory-policy volatile-lru#超出最大內存后替換策略
requirepass, ./z#2kknsl;aksKm-0q8%^提升安全性
##########################################
CacheCloud是用來監控和管理Redis Cluster的一個開源工具.在172.17.0.212虛擬機上搭建CacheCloud,并將Redis Cluster36個節點作為一個應用導入至CacheCloud, 開始收集 Redis Cluster節點數據, 進行實時監控Redis Cluster整個集群狀態.圖4、圖5和圖6展示了Redis Cluster接入CacheCloud之后的部分功能圖, 可以實時在圖形化界面查看Redis Cluster拓撲圖、節點流量監控、客戶端連接統計等信息, 還可以進行相關管理.

圖4 Redis Cluster – CacheCloud 應用節點拓撲圖

圖5 Redis Cluster – CacheCloud 應用網絡流量圖

圖6 Redis Cluster – CacheCloud 客戶端連接統計圖
本部分工作分別以Redis Cluster功能驗證和性能測試展開.功能測試主要針對系統可用性、故障自診斷和智能恢復、動態擴展、配置管理等管理功能進行驗證, 驗證工具采用管理工具CacheCloud.性能測試分兩個部分:基于 Redis-Bechmark 對 Redis Cluster本身進行不同命令請求數的QPS測試;和業界較成熟的Codis分布式緩存系統進行并發響應時間對比測試實驗對比.
CacheCloud可以查看Redis Cluster實時全局信息, 包括應用主從節點數, 運行狀態等, 如圖 7.在基準測試時, 整個集群的命令分布及命中率都實時反映在了頁面上, 說明了系統的可用性和高效性.圖8展示了CacheCloud對Redis Cluster具體節點的動態擴展、Master/Slave動態切換、添加和刪除節點、實例的啟動和下線、以及故障轉移相關操作等, 實驗表明, 各指標狀態穩定、功能正常.

圖7 Redis Clsuter全局信息圖

圖8 RedisCluster-CacheCloud 應用實例管理
Redis-benchmark是官方自帶的Redis性能測試工具, 可以有效的測試Redis服務的性能.圖9、圖10和圖11分別為使用redis-benchmark對Redis Cluster節點主要命令的QPS統計信息, 當請求數Requests數分別為 100、101、102、103、104、105、106、107和108時, 基準測試命令 PING_INLINE、PING_BULK、GET、SET、INCR、LPUSH、RPUSH、LPOP、RPOP、SADD、HSET、MSET等對應的QPS趨勢圖,表3是各命令 QPS具體值, 從而從實驗驗證了Redis讀和寫的高效響應速度.
為和業界較成熟的Codis作性能對比, 在4臺虛擬機上同時搭建了Codis集群.Codis成員詳細信息見表4, Codis-group中主從節點詳細信息見表5.

圖9 Redis Cluster各命令 QPS (a)

圖10 Redis Cluster各命令 QPS (b)

圖11 Redis Cluster各命令 QPS (c)
Codis集群成員為:36個redisserver(其中12個Master節點, 24 個 Slave節點, 分 12 組, 每組 1 主 2 從),3 個 codis-proxy, 3 個 Zookeeper, 1 個 codis-dashboard,1 個 codis-fe, 1 個 codis-ha.
對Redis Cluster和Codis分別以客戶端并發數為100、101、102、103、104、105、106和 107時, 測量Redis Cluster和 Codis集群響應時間.圖 12 為 Redis Cluster和Codis響應時間對比圖.實驗結果表明, 當并發請求數為 10 000 及以上時, Redis Cluster的響應時間明顯優于Codis, 這是因為Codis通過代理連接, 性能有所損耗.因此, 基于 Redis Cluster的分布式實現方式簡單且高效.

表3 Redis 各命令 QPS
本文主要做了三個方面工作:研究了分布式緩存系統并基于最新版Redis4.0構建了Redis Cluster分布式緩存系統, 對其可用性、水平擴展、數據一致性及故障轉移做了驗證;基于Redis自身特點, 從Linux層面、Redis Cluster本身做了配置優化;集成了開源工具 CacheCloud 對 Redis Cluster進行監控和管理, 彌補了其本身在可視化管理方面的欠缺.實驗表明, Redis Cluster在高并發時響應速度要明顯優于Codis, 這一定程度上犧牲了數據強一致性.同時, 其豐富的數據類型、數據持久化及備份、消息隊列、高擴展性等特性,使得Redis Cluster分布式緩存更加先進和完善.
Redis 4.0新增了模塊機制、部分復制(PSYNC2.0)、混合RDB-AOF持久化策略、更優的緩存驅逐策略,兼容NAT和Docker, 但在有些方面還有待驗證和提升.下一步將結合實際應用, 在數據一致性、消息隊列和Docker相結合方面做進一步研究和提升.

表4 Codis集群組成成員信息

表5 Codis-group 主從節點信息

圖12 并發請求響應時間對比圖