redis集群+twemproxy
參考:
https://www.zhihu.com/question/21419897
http://www.cnblogs.com/haoxinyue/p/redis.html
為什麽集群?
通常,為了提高網站響應速度,總是把熱點數據保存在內存中而不是直接從後端數據庫中讀取。Redis是一個很好的Cache工具。大型網站應用,熱點數據量往往巨大,幾十G上百G是很正常的事兒,在這種情況下,如何正確架構Redis呢?
首先,無論我們是使用自己的物理主機,還是使用雲服務主機,內存資源往往是有限制的,scale up不是一個好辦法,我們需要scale out橫向可伸縮擴展,這需要由多臺主機協同提供服務,即分布式多個Redis實例協同運行。
其次,目前硬件資源成本降低,多核CPU,幾十G內存的主機很普遍,對於主進程是單線程工作的Redis,只運行一個實例就顯得有些浪費。同時,管理一個巨大內存不如管理相對較小的內存高效。因此,實際使用中,通常一臺機器上同時跑多個Redis實例。
方案
1.Redis官方集群方案 Redis Cluster
Redis Cluster是一種服務器Sharding技術,3.0版本開始正式提供。
Redis Cluster中,Sharding采用slot(槽)的概念,一共分成16384個槽,這有點兒類pre sharding思路。對於每個進入Redis的鍵值對,根據key進行散列,分配到這16384個slot中的某一個中。使用的hash算法也比較簡單,就是CRC16後16384取模。
Redis集群中的每個node(節點)負責分攤這16384個slot中的一部分,也就是說,每個slot都對應一個node負責處理。當動態添加或減少node節點時,需要將16384個槽做個再分配,槽中的鍵值也要遷移。當然,這一過程,在目前實現中,還處於半自動狀態,需要人工介入。
Redis集群,要保證16384個槽對應的node都正常工作,如果某個node發生故障,那它負責的slots也就失效,整個集群將不能工作。
為了增加集群的可訪問性,官方推薦的方案是將node配置成主從結構,即一個master主節點,掛n個slave從節點。這時,如果主節點失效,Redis Cluster會根據選舉算法從slave節點中選擇一個上升為主節點,整個集群繼續對外提供服務。這非常類似前篇文章提到的Redis Sharding場景下服務器節點通過Sentinel監控架構成主從結構,只是Redis Cluster本身提供了故障轉移容錯的能力。
Redis Cluster的新節點識別能力、故障判斷及故障轉移能力是通過集群中的每個node都在和其它nodes進行通信,這被稱為集群總線(cluster bus)。它們使用特殊的端口號,即對外服務端口號加10000。例如如果某個node的端口號是6379,那麽它與其它nodes通信的端口號是16379。nodes之間的通信采用特殊的二進制協議。
對客戶端來說,整個cluster被看做是一個整體,客戶端可以連接任意一個node進行操作,就像操作單一Redis實例一樣,當客戶端操作的key沒有分配到該node上時,Redis會返回轉向指令,指向正確的node,這有點兒像瀏覽器頁面的302 redirect跳轉。
Redis Cluster是Redis 3.0以後才正式推出,時間較晚,目前能證明在大規模生產環境下成功的案例還不是很多,需要時間檢驗。
2.Redis Sharding集群
Redis 3正式推出了官方集群技術,解決了多Redis實例協同服務問題。Redis Cluster可以說是服務端Sharding分片技術的體現,即將鍵值按照一定算法合理分配到各個實例分片上,同時各個實例節點協調溝通,共同對外承擔一致服務。
多Redis實例服務,比單Redis實例要復雜的多,這涉及到定位、協同、容錯、擴容等技術難題。這裏,我們介紹一種輕量級的客戶端Redis Sharding技術。
Redis Sharding可以說是Redis Cluster出來之前,業界普遍使用的多Redis實例集群方法。其主要思想是采用哈希算法將Redis數據的key進行散列,通過hash函數,特定的key會映射到特定的Redis節點上。這樣,客戶端就知道該向哪個Redis節點操作數據。Sharding架構如圖:
慶幸的是,java redis客戶端驅動jedis,已支持Redis Sharding功能,即ShardedJedis以及結合緩存池的ShardedJedisPool。
Jedis的Redis Sharding實現具有如下特點:
-
采用一致性哈希算法(consistent hashing),將key和節點name同時hashing,然後進行映射匹配,采用的算法是MURMUR_HASH。采用一致性哈希而不是采用簡單類似哈希求模映射的主要原因是當增加或減少節點時,不會產生由於重新匹配造成的rehashing。一致性哈希只影響相鄰節點key分配,影響量小。
2.為了避免一致性哈希只影響相鄰節點造成節點分配壓力,ShardedJedis會對每個Redis節點根據名字(沒有,Jedis會賦予缺省名字)會虛擬化出160個虛擬節點進行散列。根據權重weight,也可虛擬化出160倍數的虛擬節點。用虛擬節點做映射匹配,可以在增加或減少Redis節點時,key在各Redis節點移動再分配更均勻,而不是只有相鄰節點受影響。
3.ShardedJedis支持keyTagPattern模式,即抽取key的一部分keyTag做sharding,這樣通過合理命名key,可以將一組相關聯的key放入同一個Redis節點,這在避免跨節點訪問相關數據時很重要。
Redis Sharding采用客戶端Sharding方式,服務端Redis還是一個個相對獨立的Redis實例節點,沒有做任何變動。同時,我們也不需要增加額外的中間處理組件,這是一種非常輕量、靈活的Redis多實例集群方法。
當然,Redis Sharding這種輕量靈活方式必然在集群其它能力方面做出妥協。比如擴容,當想要增加Redis節點時,盡管采用一致性哈希,畢竟還是會有key匹配不到而丟失,這時需要鍵值遷移。
作為輕量級客戶端sharding,處理Redis鍵值遷移是不現實的,這就要求應用層面允許Redis中數據丟失或從後端數據庫重新加載數據。但有些時候,擊穿緩存層,直接訪問數據庫層,會對系統訪問造成很大壓力。有沒有其它手段改善這種情況?
Redis作者給出了一個比較討巧的辦法--presharding,即預先根據系統規模盡量部署好多個Redis實例,這些實例占用系統資源很小,一臺物理機可部署多個,讓他們都參與sharding,當需要擴容時,選中一個實例作為主節點,新加入的Redis節點作為從節點進行數據復制。數據同步後,修改sharding配置,讓指向原實例的Shard指向新機器上擴容後的Redis節點,同時調整新Redis節點為主節點,原實例可不再使用。
presharding是預先分配好足夠的分片,擴容時只是將屬於某一分片的原Redis實例替換成新的容量更大的Redis實例。參與sharding的分片沒有改變,所以也就不存在key值從一個區轉移到另一個分片區的現象,只是將屬於同分片區的鍵值從原Redis實例同步到新Redis實例。並不是只有增刪Redis節點引起鍵值丟失問題,更大的障礙來自Redis節點突然宕機。在《Redis持久化》一文中已提到,為不影響Redis性能,盡量不開啟AOF和RDB文件保存功能,可架構Redis主備模式,主Redis宕機,數據不會丟失,備Redis留有備份。
這樣,我們的架構模式變成一個Redis節點切片包含一個主Redis和一個備Redis。在主Redis宕機時,備Redis接管過來,上升為主Redis,繼續提供服務。主備共同組成一個Redis節點,通過自動故障轉移,保證了節點的高可用性。則Sharding架構演變成: Redis Sentinel提供了主備模式下Redis監控、故障轉移功能達到系統的高可用性。
高訪問量下,即使采用Sharding分片,一個單獨節點還是承擔了很大的訪問壓力,這時我們還需要進一步分解。通常情況下,應用訪問Redis讀操作量和寫操作量差異很大,讀常常是寫的數倍,這時我們可以將讀寫分離,而且讀提供更多的實例數。
可以利用主從模式實現讀寫分離,主負責寫,從負責只讀,同時一主掛多個從。在Sentinel監控下,還可以保障節點故障的自動監測。
3.利用代理中間件實現大規模Redis集群
上面分別介紹了多Redis服務器集群的兩種方式,它們是基於客戶端sharding的Redis Sharding和基於服務端sharding的Redis Cluster。
客戶端sharding技術其優勢在於服務端的Redis實例彼此獨立,相互無關聯,每個Redis實例像單服務器一樣運行,非常容易線性擴展,系統的靈活性很強。其不足之處在於:
- 由於sharding處理放到客戶端,規模進步擴大時給運維帶來挑戰。
- 服務端Redis實例群拓撲結構有變化時,每個客戶端都需要更新調整。
-
連接不能共享,當應用規模增大時,資源浪費制約優化。
服務端sharding的Redis Cluster其優勢在於服務端Redis集群拓撲結構變化時,客戶端不需要感知,客戶端像使用單Redis服務器一樣使用Redis集群,運維管理也比較方便。
不過Redis Cluster正式版推出時間不長,系統穩定性、性能等都需要時間檢驗,尤其在大規模使用場合。
能不能結合二者優勢?即能使服務端各實例彼此獨立,支持線性可伸縮,同時sharding又能集中處理,方便統一管理?本篇介紹的Redis代理中間件twemproxy就是這樣一種利用中間件做sharding的技術。
twemproxy處於客戶端和服務器的中間,將客戶端發來的請求,進行一定的處理後(如sharding),再轉發給後端真正的Redis服務器。也就是說,客戶端不直接訪問Redis服務器,而是通過twemproxy代理中間件間接訪問。
參照Redis Sharding架構,增加代理中間件的Redis集群架構如下:
twemproxy中間件的內部處理是無狀態的,它本身可以很輕松地集群,這樣可避免單點壓力或故障。
twemproxy又叫nutcracker,起源於twitter系統中redis/memcached集群開發實踐,運行效果良好,後代碼奉獻給開源社區。其輕量高效,采用C語言開發,工程網址是:GitHub - twitter/twemproxy: A fast, light-weight proxy for memcached and redis
twemproxy後端不僅支持redis,同時也支持memcached,這是twitter系統具體環境造成的。
由於使用了中間件,twemproxy可以通過共享與後端系統的連接,降低客戶端直接連接後端服務器的連接數量。同時,它也提供sharding功能,支持後端服務器集群水平擴展。統一運維管理也帶來了方便。
當然,也是由於使用了中間件代理,相比客戶端直連服務器方式,性能上會有所損耗,實測結果大約降低了20%左右。
基於Twemproxy的Redis集群方案
概述
由於單臺redis服務器的內存管理能力有限,使用過大內存redis服務器的性能急劇下降,且服務器發生故障將直接影響大面積業務。為了獲取更好的緩存性能及擴展型,我們將需要搭建redis集群來滿足需求。因redis 3.0 beta支持的集群功能不適合生產環境的使用,所以我們采用twitter正在使用的twemproxy來搭建redis緩存服務器集群,目前用戶包括Pinterest、Tumblr、Twitter、Vine、Kiip、Wuaki.tv、Wanelo、Kontera、Wikimedia、Bright、56.com、Snapchat、Digg、Gawkermedia、3scale.net等。
Twemproxy是memcached和redis協議的代理服務器,並能有效減少大量連接對redis服務器的性能影響,它提供的主要特性如下:
集群架構
安裝Redis
有三臺服務器,一臺COS1安裝twemproxy,另外兩臺COS2,COS3安裝redis。
- 下載最新安裝包:redis-2.8.9.tar.gz , tcl-8.5.7-6.el6.x86_64.rpm ,nutcracker-0.3.0.tar.gz
- 安裝必要組件rpm:
-
[root@COS2 redis-2.8.9]# yum install gcc
[root@COS2 src]# rpm -ivh tcl-8.5.7-6.el6.x86_64.rpm
-
安裝Redis:
[root@COS2 src]# tar xvf redis-2.8.9.tar.gz
[root@COS2 src]# cd redis-2.8.9
[root@COS2 redis-2.8.9]# make
…
Hint: To run ‘make test‘ is a good idea ;)
make[1]: Leaving directory `/usr/local/src/redis-2.8.9/src‘
[root@COS2 redis-2.8.9]# make test
All tests passed without errors!
Cleanup: may take some time... OK
make[1]: Leaving directory `/usr/local/src/redis-2.8.9/src‘
[root@COS2 redis-2.8.9]# make install
[root@COS2 redis-2.8.9]# cd /usr/local/bin/
[root@COS2 bin]# ll
total 13908
-rwxr-xr-x. 1 root root 4170264 Apr 26 11:51 redis-benchmark
-rwxr-xr-x. 1 root root 22185 Apr 26 11:51 redis-check-aof
-rwxr-xr-x. 1 root root 45419 Apr 26 11:51 redis-check-dump
-rwxr-xr-x. 1 root root 4263471 Apr 26 11:51 redis-cli
-rwxr-xr-x. 1 root root 5726791 Apr 26 11:51 redis-server
-
編輯redis配置文件:
[root@COS2 redis-2.8.9]# cp redis.conf /etc/
[root@COS2 redis-2.8.9]# vim /etc/red
redhat-release redis.conf
[root@COS2 redis-2.8.9]# vim /etc/redis.conf
把裏面的
daemonize no 修改成 daemonize yes
-
啟動redis服務:
[root@COS2 redis-2.8.9]# redis-server /etc/redis.conf
- 測試redis服務:
-
[root@COS2 redis-2.8.9]# redis-cli
-
127.0.0.1:6379> set kin kin
-
OK
127.0.0.1:6379> get kin
- 同樣的步驟安裝其他redis服務器。
安裝twemproxy
- 安裝twemproxy:
-
[root@COS1 src]# tar xvf nutcracker-0.3.0.tar.gz
-
[root@COS1 nutcracker-0.3.0]# cd nutcracker-0.3.0
-
[root@COS1 src]#./configure
[root@COS1 nutcracker-0.3.0]# make && make install
-
編輯配置文件:
[root@COS1 conf]# cd /usr/local/src/nutcracker-0.3.0/conf
[root@COS1 conf]# cp nutcracker.yml /etc/
[root@COS1 conf]# vim /etc/nutcracker.yml
alpha:
listen: 0.0.0.0:22121
hash: fnv1a_64
distribution: ketama
auto_eject_hosts: true
redis: true
server_retry_timeout: 2000
server_failure_limit: 1
servers: --兩臺redis服務器的地址和端口
- 10.23.22.240:6379:1
- 10.23.22.241:6379:1
- 測試配置文件:
-
[root@COS1 nutcracker-0.3.0]# nutcracker -t /etc/nutcracker.yml
nutcracker: configuration file ‘conf/nutcracker.yml‘ syntax is ok
-
啟動twemproxy:
[root@COS1 nutcracker-0.3.0]# nutcracker --help
This is nutcracker-0.3.0
Usage: nutcracker [-?hVdDt] [-v verbosity level] [-o output file]
[-c conf file] [-s stats port] [-a stats addr]
[-i stats interval] [-p pid file] [-m mbuf size]
Options:
-h, --help : this help
-V, --version : show version and exit
-t, --test-conf : test configuration for syntax errors and exit
-d, --daemonize : run as a daemon
-D, --describe-stats : print stats description and exit
-v, --verbosity=N : set logging level (default: 5, min: 0, max: 11)
-o, --output=S : set logging file (default: stderr)
-c, --conf-file=S : set configuration file (default: conf/nutcracker.yml) #配置
-s, --stats-port=N : set stats monitoring port (default: 22222)
-a, --stats-addr=S : set stats monitoring ip (default: 0.0.0.0)
-i, --stats-interval=N : set stats aggregation interval in msec (default: 30000 msec)
-p, --pid-file=S : set pid file (default: off)
-m, --mbuf-size=N : set size of mbuf chunk in bytes (default: 16384 bytes)
[root@COS1 nutcracker-0.3.0]# nutcracker -d -c /etc/nutcracker.yml
[root@COS1 nutcracker-0.3.0]# ps -ef|grep nutcracker
root 15358 1 0 02:40 ? 00:00:00 nutcracker -d -c /etc/nutcracker.yml
-
測試twemproxy:
[root@COS1 ~]# redis-cli -p 22121
127.0.0.1:22121> get kin
"kin"
127.0.0.1:22121> set kin king
OK
127.0.0.1:22121> get kin
"king"
性能測試
這裏使用redis自帶的redis-benchmark進行簡單的性能測試,測試結果如下:
-
Set測試:
-
通過twemproxy測試:
[root@COS1 src]# redis-benchmark -h 10.23.22.240 -p 22121 -c 100 -t set -d 100 -l –q
SET: 38167.94 requests per second
-
直接對後端redis測試:
[root@COS2 ~]# redis-benchmark -h 10.23.22.241 -p 6379 -c 100 -t set -d 100 -l –q
SET: 53191.49 requests per second
-
通過twemproxy測試:
-
Get測試:
- 通過twemproxy測試:
-
[root@COS1 src]# redis-benchmark -h 10.23.22.240 -p 22121 -c 100 -t get -d 100 -l -q
GET: 37453.18 requests per second
- 直接對後端redis測試:
-
[root@COS2 ~]# redis-benchmark -h 10.23.22.241 -p 6379 -c 100 -t get -d 100 -l -q
GET: 62111.80 requests per second
- 查看鍵值分布:
-
[root@COS2 ~]# redis-cli info|grep db0
-
db0:keys=51483,expires=0,avg_ttl=0
-
[root@COS3 ~]# redis-cli info|grep db0
db0:keys=48525,expires=0,avg_ttl=0
測試結果:以基本的set get命令通過twemproxy性能有所下降;通過twemproxy分布基本平均。測試數據以業務測試為準。
Redis 代理服務Twemproxy
1、twemproxy explore
當我們有大量 Redis 或 Memcached 的時候,通常只能通過客戶端的一些數據分配算法(比如一致性哈希),來實現集群存儲的特性。雖然Redis 2.6版本已經發布Redis Cluster,但還不是很成熟適用正式生產環境。 Redis 的 Cluster 方案還沒有正式推出之前,我們通過 Proxy 的方式來實現集群存儲。
Twitter,世界最大的Redis集群之一部署在Twitter用於為用戶提供時間軸數據。Twitter Open Source部門提供了Twemproxy。
Twemproxy,也叫nutcraker。是一個twtter開源的一個redis和memcache代理服務器。 redis作為一個高效的緩存服務器,非常具有應用價值。但是當使用比較多的時候,就希望可以通過某種方式 統一進行管理。避免每個應用每個客戶端管理連接的松散性。同時在一定程度上變得可以控制。
Twemproxy是一個快速的單線程代理程序,支持Memcached ASCII協議和更新的Redis協議:
它全部用C寫成,使用Apache 2.0 License授權。項目在Linux上可以工作,而在OSX上無法編譯,因為它依賴了epoll API.
Twemproxy 通過引入一個代理層,可以將其後端的多臺 Redis 或 Memcached 實例進行統一管理與分配,使應用程序只需要在 Twemproxy 上進行操作,而不用關心後面具體有多少個真實的 Redis 或 Memcached 存儲。
2、twemproxy特性:
-
支持失敗節點自動刪除
- 可以設置重新連接該節點的時間
- 可以設置連接多少次之後刪除該節點
- 該方式適合作為cache存儲
-
支持設置HashTag
- 通過HashTag可以自己設定將兩個KEYhash到同一個實例上去。
-
減少與redis的直接連接數
- 保持與redis的長連接
- 可設置代理與後臺每個redis連接的數目
-
自動分片到後端多個redis實例上
- 多種hash算法:能夠使用不同的策略和散列函數支持一致性hash。
- 可以設置後端實例的權重
-
避免單點問題
- 可以平行部署多個代理層.client自動選擇可用的一個
- 支持redis pipelining request
支持請求的流式與批處理,降低來回的消耗
-
支持狀態監控
- 可設置狀態監控ip和端口,訪問ip和端口可以得到一個json格式的狀態信息串
- 可設置監控信息刷新間隔時間
-
高吞吐量
- 連接復用,內存復用。
- 將多個連接請求,組成reids pipelining統一向redis請求。
另外可以修改redis的源代碼,抽取出redis中的前半部分,作為一個中間代理層。最終都是通過linux下的epoll 事件機制提高並發效率,其中nutcraker本身也是使用epoll的事件機制。並且在性能測試上的表現非常出色。
3、twemproxy問題與不足
Twemproxy 由於其自身原理限制,有一些不足之處,如:
- 不支持針對多個值的操作,比如取sets的子交並補等(MGET 和 DEL 除外)
- 不支持Redis的事務操作
- 出錯提示還不夠完善
- 也不支持select操作
4、安裝與配置
具體的安裝步驟可用查看github: https://github.com/twitter/twemproxy
Twemproxy 的安裝,主要命令如下:
apt-get install automake
apt-get install libtool
git clone git://github.com/twitter/twemproxy.git
cd twemproxy
autoreconf -fvi
./configure --enable-debug=log
make
src/nutcracker -h
通過上面的命令就算安裝好了,然後是具體的配置,下面是一個典型的配置
redis1:
listen: 127.0.0.1:6379 #使用哪個端口啟動Twemproxy
redis: true #是否是Redis的proxy
hash: fnv1a_64 #指定具體的hash函數
distribution: ketama #具體的hash算法
auto_eject_hosts: true #是否在結點無法響應的時候臨時摘除結點
timeout: 400 #超時時間(毫秒)
server_retry_timeout: 2000 #重試的時間(毫秒)
server_failure_limit: 1 #結點故障多少次就算摘除掉
servers: #下面表示所有的Redis節點(IP:端口號:權重)
- 127.0.0.1:6380:1
- 127.0.0.1:6381:1
- 127.0.0.1:6382:1
redis2:
listen: 0.0.0.0:10000
redis: true
hash: fnv1a_64
distribution: ketama
auto_eject_hosts: false
timeout: 400
servers:
- 127.0.0.1:6379:1
- 127.0.0.1:6380:1
- 127.0.0.1:6381:1
- 127.0.0.1:6382:1
你可以同時開啟多個 Twemproxy 實例,它們都可以進行讀寫,這樣你的應用程序就可以完全避免所謂的單點故障。
Twemproxy是一種代理分片機制,由Twitter開源。Twemproxy作為代理,可接受來自多個程序的訪問,按照路由規則,轉發給後臺的各個Redis服務器,再原路返回。該方案很好的解決了單個Redis實例承載能力的問題。當然,Twemproxy本身也是單點,需要用Keepalived做高可用方案。通過Twemproxy可以使用多臺服務器來水平擴張redis服務,可以有效的避免單點故障問題。雖然使用Twemproxy需要更多的硬件資源和在redis性能有一定的損失(twitter測試約20%),但是能夠提高整個系統的HA也是相當劃算的。不熟悉twemproxy的同學,如果玩過nginx反向代理或者mysql proxy,那麽你肯定也懂twemproxy了。其實twemproxy不光實現了redis協議,還實現了memcached協議,什麽意思?換句話說,twemproxy不光可以代理redis,還可以代理memcached
twemproxy不會增加redis的性能指標數據,據業界測算,使用twemproxy相比直接使用redis會帶來~10%的性能下降。
但是單個redis進程的內存管理能力有限。據測算,單個redis進程內存超過20G之後,效率會急劇下降。目前,我們給出的建議值是單個redis最好配置在8G以內。8G以上的redis緩存需求,通過twemproxy來提供支持。
Twemproxy的強大之處在於可以通過配置的方式讓它禁用掉失敗的結點,同時還能在一段時間後進行重試,抑或使用指定的鍵->服務器映射。這意味著在將Redis用作數據存儲時,它可以對Redis數據集進行分片(禁用掉結點驅逐);在將Redis用作緩存時,它可以啟用結點驅逐以實現簡單的高可用性。
TwemProxy支持自動分片、失敗節點摘除、一致性hash,優化Redis請求(批處理)等,同時TwemProxy也有一些不足之處,例如無法全面覆蓋Redis命令(不支持事務,多值操作(如keys*))。擴展Redis實例時,無法自動將之前數據進行再分配(需要寫腳本手動分配)。
redis集群+twemproxy