Redis數(shù)據(jù)分片方案實(shí)踐
Twemproxy的介紹
Twitter的Twemproxy (https://github.com/twitter/twemproxy)是目前市面上用的最廣的使用做多的用來做redis集群服務(wù)。由于redis是單線程,而且官方的cluster 還不是很穩(wěn)定和廣泛使用。Twemproxy是一種代理分片機(jī)制,Twemproxy作為代理,可接受來自多個(gè)程序的訪問,按照路由規(guī)則,轉(zhuǎn)發(fā)給后臺(tái)的各個(gè)Redis服務(wù)器,再原路返回。該方案很好的解決了單個(gè)Redis實(shí)例承載能力的問題。當(dāng)然,Twemproxy本身也是單點(diǎn),需要用Keepalived做高可用方案(或者LVS)。通過Twemproxy可以使用多臺(tái)服務(wù)器來水平擴(kuò)張redis服務(wù),可以有效的避免單點(diǎn)故障問題。雖然使用Twemproxy需要更多的硬件資源和在redis性能有一定的損失(twitter測(cè)試約20%),但是能夠提高整個(gè)系統(tǒng)的HA也是相當(dāng)劃算的。其實(shí)twemproxy不光實(shí)現(xiàn)了redis協(xié)議,還實(shí)現(xiàn)了memcached協(xié)議,什么意思?換句話說,twemproxy不光可以代理redis,還可以代理memcached。
Twemproxy的優(yōu)點(diǎn):
1)對(duì)外暴露一個(gè)訪問節(jié)點(diǎn),減少程序復(fù)雜度。
2)支持失敗節(jié)點(diǎn)自動(dòng)刪除,可以設(shè)置重新連接該節(jié)點(diǎn)的時(shí)間,可以設(shè)置連接多少次之后刪除該節(jié)點(diǎn),該方式適合作為cache存儲(chǔ),不然會(huì)丟失Key;
3)支持設(shè)置HashTag,通過HashTag可以自己設(shè)定將兩個(gè)KEYhash到同一個(gè)實(shí)例上去。
4)多種hash算法,并且可以設(shè)置后端實(shí)例的權(quán)重。
5)減少與redis的直接連接數(shù):保持與redis的長(zhǎng)連接,可設(shè)置代理與后臺(tái)每個(gè)redis連接的數(shù)目,自動(dòng)分片到后端多個(gè)redis實(shí)例上。
6)避免單點(diǎn)問題:可以平行部署多個(gè)代理層,客戶端自動(dòng)選擇可用的一個(gè)。
7)高吞吐量:連接復(fù)用,內(nèi)存復(fù)用,將多個(gè)連接請(qǐng)求,組成redis pipelining統(tǒng)一向redis請(qǐng)求。
Twemproxy的缺點(diǎn):
1)不支持針對(duì)多個(gè)值的操作,比如取sets的子交并補(bǔ)等。
2)不支持Redis的事務(wù)操作。
3)對(duì)于已申請(qǐng)的內(nèi)存不會(huì)釋放,所有機(jī)器內(nèi)存要大,且需要定期重啟,不然就會(huì)出現(xiàn)客戶端連接錯(cuò)誤。
4)不支持動(dòng)態(tài)增刪節(jié)點(diǎn),修改完配置需重啟。
5)改變節(jié)點(diǎn)時(shí),系統(tǒng)不會(huì)對(duì)已有數(shù)據(jù)重分配,不自己寫腳本做數(shù)據(jù)遷移的話,會(huì)造成部分key丟失(key本身存在某redis上,只是key被哈希到了其他節(jié)點(diǎn),造成“丟失”)。
6)權(quán)重直接影響key的哈希結(jié)果,改變節(jié)點(diǎn)權(quán)重會(huì)造成部分key丟失。
7)默認(rèn)Twemproxy是單線程運(yùn)行,但是大部分使用Twemproxy的公司都會(huì)自行進(jìn)行二次開發(fā)一下,改成多線程。
總體來說,twemproxy還是非常的靠譜,雖然性能有損失,但是相對(duì)來說還是很值得的,而且久經(jīng)考驗(yàn),使用非常廣泛。關(guān)于更多更加詳細(xì)的資料請(qǐng)參考官方文檔。另外twemproxy只適合靜態(tài)集群,不適合需要?jiǎng)討B(tài)增刪節(jié)點(diǎn),手動(dòng)調(diào)整負(fù)載的場(chǎng)景,如果我們直接來用,需要做開發(fā)改進(jìn)工作。https://github.com/wandoulabs/codis這個(gè)系統(tǒng)基于twemproxy,增加了動(dòng)態(tài)數(shù)據(jù)遷移等功能,具體使用方法需要進(jìn)一步測(cè)試。
Twemproxy使用架構(gòu)
***種:?jiǎn)喂?jié)點(diǎn)Twemproxy
ps:節(jié)省硬件資源,但容易有單點(diǎn)故障。
第二種:高可用Twemproxy
PS:浪費(fèi)二分之一的資源,但是節(jié)點(diǎn)高可用。
第三種:負(fù)載均衡Twemproxy
PS:如果你是大規(guī)模Redis或Memcached應(yīng)用場(chǎng)景,就可以做Twemproxy的負(fù)載軍和場(chǎng)景,也就是在高可用Twemproxy的基礎(chǔ)上加LVS節(jié)點(diǎn),利用LVS(Linux virtual server)做Twemproxy的負(fù)載均衡,LVS是四層負(fù)載均衡技術(shù),有很強(qiáng)大的代理能力,具體可以看本博客的LVS章節(jié)介紹。但是當(dāng)你使用LVS之后,又出現(xiàn)了Twemproxy的問題,單點(diǎn)故障故障問題,這個(gè)時(shí)候又要跟給LVS做高可用了。但是LVS也支持做負(fù)載均衡,利用OSPF路由技術(shù)就可以做負(fù)載均衡了。而這個(gè)架構(gòu)也就是我目前工作中正在使用的架構(gòu)方式。
另外不管使用以上哪種架構(gòu)方式,都無法避免Redis的單點(diǎn)故障問題,Redis持久化也無法避免硬件故障問題。如果必須要保證Redis數(shù)據(jù)訪問的不可中斷性,那你還是使用Redis集群模式吧,集群模式目前對(duì)JAVA支持還不錯(cuò),工作中也有大量的使用。
安裝Twemproxy
1、下載Twemproxy
git clone https://github.com/twitter/twemproxy.git
2、安裝Twemproxy
Twemproxy需要使用Autoconf進(jìn)行編譯配置。 GNU Autoconf是一個(gè)在Bourne shell下制作供編譯、安裝和打包軟件的配置腳本的工具。Autoconf并不受程序語言限制,常用于C、C++、Erlang和Objective-C。配置腳本控制了一個(gè)軟件包在特定系統(tǒng)上的安裝。在進(jìn)行一系列測(cè)試后,配置腳本從模板中生成makefile與頭文件進(jìn)而調(diào)整軟件包,使之適應(yīng)某一種系統(tǒng)。Autoconf與Automake、Libtool等軟件組成了GNU構(gòu)建系統(tǒng)。Autoconf由戴維·麥肯思于1991年夏天編寫用于支持他在自由軟件基金會(huì)的編程工作。此后,Autoconf包含了多人編寫的改進(jìn)代碼并成為了使用最廣泛的自由編譯配置軟件。
下面開始使用autoreconf對(duì)twemproxy編譯配置:
- [root@www twemproxy]# autoreconf
- configure.ac:8: error: Autoconf version 2.64 or higher is required
- configure.ac:8: the top level
- autom4te: /usr/bin/m4 failed with exit status: 63
- aclocal: autom4te failed with exit status: 63
- autoreconf: aclocal failed with exit status: 63
- [root@www twemproxy]# autoconf --version
- autoconf (GNU Autoconf) 2.63
提示autoreconf 的版本過低,上面使用的是autoconf 2.63版本的,所以下面下載autoconf 2.69版本進(jìn)行編譯安裝。注意如果你是CentOS6,那么你的默認(rèn)版本就是2.63,如果你是CentOS7,那么你的默認(rèn)版本應(yīng)該是2.69,如果你是Debian8或Ubuntu16,那么你的默認(rèn)版本應(yīng)該也是2.69。反正如果執(zhí)行autoreconf報(bào)錯(cuò)就說明版本低了,需要編譯安裝了。
編譯安裝Autoconf
- [root@www ~]# wget http://ftp.gnu.org/gnu/autoconf/autoconf-2.69.tar.gz
- [root@www ~]# tar xvf autoconf-2.69.tar.gz
- [root@www ~]# cd autoconf-2.69
- [root@www autoconf-2.69]# ./configure --prefix=/usr
- [root@www autoconf-2.69]# make && make install
- [root@www autoconf-2.69]# autoconf --version
- autoconf (GNU Autoconf) 2.69
編譯安裝Twemproxy
- [root@www ~]# cd /root/twemproxy/
- [root@www twemproxy]# autoreconf -fvi
- [root@www twemproxy]# ./configure --prefix=/etc/twemproxy CFLAGS="-DGRACEFUL -g -O2" --enable-debug=full
- [root@www twemproxy]# make && make install
如果autoreconf -fvi時(shí)報(bào)如下錯(cuò)誤,就是要安裝libtool工具,需要依賴libtool(如果是CentOS直接使用yum install libtool即可,如果是Debian直接使用apt-get install libtool即可)。
- autoreconf: Entering directory `.'
- autoreconf: configure.ac: not using Gettext
- autoreconf: running: aclocal --force -I m4
- autoreconf: configure.ac: tracing
- autoreconf: configure.ac: adding subdirectory contrib/yaml-0.1.4 to autoreconf
- autoreconf: Entering directory `contrib/yaml-0.1.4'
- autoreconf: configure.ac: not using Autoconf
- autoreconf: Leaving directory `contrib/yaml-0.1.4'
- autoreconf: configure.ac: not using Libtool
- autoreconf: running: /usr/bin/autoconf --force
- configure.ac:36: error: possibly undefined macro: AC_PROG_LIBTOOL
- If this token and others are legitimate, please use m4_pattern_allow.
- See the Autoconf documentation.
- autoreconf: /usr/bin/autoconf failed with exit status: 1
Twemproxy添加配置文件
- [root@www twemproxy]# mkdir /etc/twemproxy/conf
- [root@www twemproxy]# cat /etc/twemproxy/conf/nutcracker.yml
- redis-cluster:
- listen: 0.0.0.0:22122
- hash: fnv1a_64
- distribution: ketama
- timeout: 400
- backlog: 65535
- preconnect: true
- redis: true
- server_connections: 1
- auto_eject_hosts: true
- server_retry_timeout: 60000
- server_failure_limit: 3
- servers:
- - 172.16.0.172:6546:1 redis01
- - 172.16.0.172:6547:1 redis02
配置選項(xiàng)介紹:
redis-cluster:給這個(gè)配置段取一個(gè)名字,可以有多個(gè)配置段;
listen:設(shè)置監(jiān)控IP和端口端口;
hash:具體的hash函數(shù),支持md5,crc16,crc32,finv1a_32,fnv1a_64,hsieh,murmur,jenkins等十多種,一般選用fnv1a_64可以了,默認(rèn)也是fnv1a_64;
hash_tag:hash_tag允許根據(jù)key的一個(gè)部分來計(jì)算key的hash值。hash_tag由兩個(gè)字符組成,一個(gè)是hash_tag的開始,另外一個(gè)是hash_tag的結(jié)束,在hash_tag的開始和結(jié)束之間,是將用于計(jì)算key的hash值的部分,計(jì)算的結(jié)果會(huì)用于選擇服務(wù)器。例如:如果hash_tag被定義為”{}”,那么key值為”user:{user1}:ids”和”user:{user1}:tweets”的hash值都是基于”user1”,最終會(huì)被映射到相同的服務(wù)器。而”user:user1:ids”將會(huì)使用整個(gè)key來計(jì)算hash,可能會(huì)被映射到不同的服務(wù)器。
distribution:指定哈希算法,這個(gè)哈希算法決定通過上面hash后的key如何分布在多個(gè)server上,默認(rèn)是”ketama“一致性哈希。ketama:ketama一致性hash算法,會(huì)根據(jù)服務(wù)器構(gòu)造出一個(gè)hash ring,并為ring上的節(jié)點(diǎn)分配hash范圍。ketama的優(yōu)勢(shì)在于單個(gè)節(jié)點(diǎn)添加、刪除之后,會(huì)***程度上保持整個(gè)群集中緩存的key值可以被重用。modula:modula非常簡(jiǎn)單,就是根據(jù)key值的hash值取模,根據(jù)取模的結(jié)果選擇對(duì)應(yīng)的服務(wù)器。random:random是無論key值的hash是什么,都隨機(jī)的選擇一個(gè)服務(wù)器作為key值操作的目標(biāo)。
timeout:設(shè)置twemproxy的超時(shí)時(shí)間,當(dāng)timeout被設(shè)置后,如果在timeout的時(shí)間過后還沒有從服務(wù)端得到回應(yīng),這時(shí)會(huì)將超時(shí)錯(cuò)誤信息SERVER_ERROR Connection time out發(fā)送給客戶端
backlog:監(jiān)聽TCP的backlog(連接等待隊(duì)列)的長(zhǎng)度,默認(rèn)是512。
preconnect:指定是否在系統(tǒng)啟動(dòng)時(shí),twemproxy就建立和所有redis的連接,默認(rèn)是false,一個(gè)布爾值;
redis:指定此配置段否作為Redis做代理,如果不加redis為true的話,就可以為memcached集群做代理(這就是Twemproxy作為redis或memcached集群代理的唯一區(qū)別);
redis_auth: 如果你的后端Redis開啟了認(rèn)證,那么就需要redis_auth指定認(rèn)證的密碼了;
server_connections:twemproxy與每臺(tái)redis服務(wù)器的連接數(shù),默認(rèn)就是1,如果大于1,用戶命令可能發(fā)到不同的連接上,可能造成命令的實(shí)際執(zhí)行順序和用戶指定的不一致(類似并發(fā));
auto_eject_hosts:是否在節(jié)點(diǎn)無法響應(yīng)的時(shí)候剔除,默認(rèn)為true,但是需要注意,節(jié)點(diǎn)剔除后,因?yàn)闄C(jī)器數(shù)減少,機(jī)器哈希位置變化,會(huì)造成部分key無法***,但是不剔除程序連接就會(huì)報(bào)錯(cuò);
server_retry_timeout:控制服務(wù)器連接的時(shí)間間隔,單位是毫秒,在auto_eject_host被設(shè)置為true的時(shí)候產(chǎn)生作用,默認(rèn)是30000毫秒;
server_failure_limit:Redis連續(xù)超時(shí)的次數(shù),超過這個(gè)次數(shù)就視其為無法連接,如果auto_eject_hosts設(shè)置為true,那么此Redis會(huì)被移除;
servers:一個(gè)pool中的服務(wù)器的地址、端口和權(quán)重的列表,包括一個(gè)可選的服務(wù)器的名字,如果提供服務(wù)器的名字,將會(huì)使用它決定server的次序,從而提供對(duì)應(yīng)的一致性hash的hash ring。否則,將使用server被定義的次序,可以通過兩種字符串格式指定’host:port:weight’或者’host:port:weight name’。一般都是使用第二種別名的方式,這樣當(dāng)其中某個(gè)Redis節(jié)點(diǎn)出現(xiàn)問題時(shí),可以直接添加一個(gè)新的Redis節(jié)點(diǎn)但服務(wù)器名字不要改變,這樣twemproxy還是使用相同的服務(wù)器名稱進(jìn)行hash ring,所以其他數(shù)據(jù)節(jié)點(diǎn)的數(shù)據(jù)不會(huì)出現(xiàn)問題(只有掛點(diǎn)的機(jī)器數(shù)據(jù)丟失)。
PS:要嚴(yán)格按照Twemproxy配置文件的格式來,不然就會(huì)有語法錯(cuò)誤;另外,在Twemproxy的配置文件中可以同時(shí)設(shè)置代理Redis集群或Memcached集群,只需要定義不同的配置段即可。
啟動(dòng)twemproxy (nutcracker)
剛已經(jīng)加好了配置文件,現(xiàn)在測(cè)試下配置文件:
- [root@www twemproxy]# /etc/twemproxy/sbin/nutcracker -t
- nutcracker: configuration file 'conf/nutcracker.yml' syntax is ok
說明配置文件已經(jīng)成功,現(xiàn)在開始運(yùn)行nutcracker:
- [root@www ~]# /etc/twemproxy/sbin/nutcracker -c /etc/twemproxy/conf/nutcracker.yml -p /var/run/nutcracker.pid -o /var/log/nutcracker.log -d
- 選項(xiàng)說明:
- -h, –help #查看幫助文檔,顯示命令選項(xiàng);
- -V, –version #查看nutcracker版本;
- -c, –conf-file=S #指定配置文件路徑 (default: conf/nutcracker.yml);
- -p, –pid-file=S #指定進(jìn)程pid文件路徑,默認(rèn)關(guān)閉 (default: off);
- -o, –output=S #設(shè)置日志輸出路徑,默認(rèn)為標(biāo)準(zhǔn)錯(cuò)誤輸出 (default: stderr);
- -d, –daemonize #以守護(hù)進(jìn)程運(yùn)行;
- -t, –test-conf #測(cè)試配置腳本的正確性;
- -D, –describe-stats #打印狀態(tài)描述;
- -v, –verbosity=N #設(shè)置日志級(jí)別 (default: 5, min: 0, max: 11);
- -s, –stats-port=N #設(shè)置狀態(tài)監(jiān)控端口,默認(rèn)22222 (default: 22222);
- -a, –stats-addr=S #設(shè)置狀態(tài)監(jiān)控IP,默認(rèn)0.0.0.0 (default: 0.0.0.0);
- -i, –stats-interval=N #設(shè)置狀態(tài)聚合間隔 (default: 30000 msec);
- -m, –mbuf-size=N #設(shè)置mbuf塊大小,以bytes單位 (default: 16384 bytes);
PS:一般在生產(chǎn)環(huán)境中,都是使用進(jìn)程管理工具來進(jìn)行twemproxy的啟動(dòng)管理,如supervisor或pm2工具,避免當(dāng)進(jìn)程掛掉的時(shí)候能夠自動(dòng)拉起進(jìn)程。
驗(yàn)證是否正常啟動(dòng)
- [root@www ~]# ps aux | grep nutcracker
- root 20002 0.0 0.0 19312 916 ? Sl 18:48 0:00 /etc/twemproxy/sbin/nutcracker -c /etc/twemproxy/conf/nutcracker.yml -p /var/run/nutcracker.pid -o /var/log/nutcracker.log -d
- root 20006 0.0 0.0 103252 832 pts/0 S+ 18:48 0:00 grep nutcracker
- [root@www ~]# netstat -nplt | grep 22122
- tcp 0 0 0.0.0.0:22122 0.0.0.0:* LISTEN 20002/nutcracker
Twemproxy代理Redis集群
這里我們使用***種方案在同一臺(tái)主機(jī)上測(cè)試Twemproxy代理Redis集群,一個(gè)twemproxy和兩個(gè)Redis節(jié)點(diǎn)(想添加更多的也可以)。Twemproxy就是用上面的配置了,下面只需要增加兩個(gè)Redis節(jié)點(diǎn)。
安裝配置Redis
在安裝Redis之前,需要安裝Redis的依賴程序tcl,如果不安裝tcl在Redis執(zhí)行make test的時(shí)候就會(huì)報(bào)錯(cuò)的哦。
- [root@www ~]# yum install -y tcl
- [root@www ~]# wget https://github.com/antirez/redis/archive/3.2.0.tar.gz
- [root@www ~]# tar xvf 3.2.0.tar.gz -C /usr/local
- [root@www ~]# cd /usr/local/
- [root@www local]# mv redis-3.2.0 redis
- [root@www local]# cd redis
- [root@www redis]# make
- [root@www redis]# make test
- [root@www redis]# make install
配置兩個(gè)Redis節(jié)點(diǎn)
- [root@www ~]# mkdir /data/redis-6546
- [root@www ~]# mkdir /data/redis-6547
- [root@www ~]# cat /data/redis-6546/redis.conf
- daemonize yes
- pidfile /var/run/redis/redis-server.pid
- port 6546
- bind 0.0.0.0
- loglevel notice
- logfile /var/log/redis/redis-6546.log
- [root@www ~]# cat /data/redis-6547/redis.conf
- daemonize yes
- pidfile /var/run/redis/redis-server.pid
- port 6547
- bind 0.0.0.0
- loglevel notice
- logfile /var/log/redis/redis-6547.log
PS:簡(jiǎn)單提供兩個(gè)Redis配置文件,如果開啟了Redis認(rèn)證,那么在twemproxy中也需要填寫Redis密碼。
啟動(dòng)兩個(gè)Redis節(jié)點(diǎn)
- [root@www ~]# /usr/local/redis/src/redis-server /data/redis-6546/redis.conf
- [root@www ~]# /usr/local/redis/src/redis-server /data/redis-6547/redis.conf
- [root@www ~]# ps aux | grep redis
- root 23656 0.0 0.0 40204 3332 ? Ssl 20:14 0:00 redis-server 0.0.0.0:6546
- root 24263 0.0 0.0 40204 3332 ? Ssl 20:16 0:00 redis-server 0.0.0.0:6547
驗(yàn)證Twemproxy讀寫數(shù)據(jù)
首先twemproxy配置項(xiàng)中servers的主機(jī)要配置正確,然后連接Twemproxy的22122端口即可測(cè)試。
- [root@www ~]# redis-cli -p 22122
- 127.0.0.1:22122> set key vlaue
- OK
- 127.0.0.1:22122> get key
- "vlaue"
- 127.0.0.1:22122> FLUSHALL
- Error: Server closed the connection
- 127.0.0.1:22122> quit
上面我們set一個(gè)key,然后通過twemproxy也可以獲取到數(shù)據(jù),一切正常。但是在twemproxy中使用flushall命令就不行了,不支持。
然后我們?nèi)フ曳謩e連接兩個(gè)redis節(jié)點(diǎn),看看數(shù)據(jù)是否出現(xiàn)在某一個(gè)節(jié)點(diǎn)上了,如果有,就說明twemproxy正常運(yùn)行了。
- [root@www ~]# redis-cli -p 6546
- 127.0.0.1:6546> get key
- (nil)
- 127.0.0.1:6546>
由上面的結(jié)果我們可以看到,數(shù)據(jù)存儲(chǔ)到6547節(jié)點(diǎn)上了。目前沒有很好的辦法明確知道某個(gè)key存儲(chǔ)到某個(gè)后端節(jié)點(diǎn)了。
如何Reload twemproxy?
由于twemproxy沒有提供啟動(dòng)腳本,都是命令行參數(shù)啟動(dòng)的。所以,無法使用對(duì)twemproxy進(jìn)行reload的操作,在生產(chǎn)環(huán)境中,一個(gè)應(yīng)用無法reload(重載配置文件)是一個(gè)災(zāi)難。當(dāng)你對(duì)twemproxy進(jìn)行增刪節(jié)點(diǎn)時(shí)如果直接使用restart的話勢(shì)必會(huì)影響線上的業(yè)務(wù)。所以***的辦法還是reload,既然twemproxy沒有提供,那么可以使用kill命令帶一個(gè)信號(hào),然后跟上twemproxy主進(jìn)程的進(jìn)行號(hào)即可。
kill -SIGHUP PID
注意,PID就是twemproxy master進(jìn)程。