1 redis概述
1.1 什么是redis
??Redis的全稱是REmote Dictionary Server。redis 是一種基于對(duì)(key-value)的NoSQL數(shù)據(jù)庫(kù)。redis所有數(shù)據(jù)都存放在內(nèi)存中,所以Redis的讀寫速度非常驚人,還可以將內(nèi)存中的數(shù)據(jù)以快走和日志的形式保存在硬盤上,這樣斷電等機(jī)器故障不會(huì)使內(nèi)存中的數(shù)據(jù)“丟失”。redis還提供鍵過(guò)期、發(fā)布訂閱、事務(wù)、流水線、Lua腳本等功能。
1.2 redis的作者是誰(shuí)
?? redis的作者Salvatore Sanfilippo 在開(kāi)發(fā)一個(gè)叫LLOOGG的網(wǎng)站時(shí)需要實(shí)現(xiàn)一個(gè)高性能的隊(duì)列功能,一開(kāi)始是使用MySql 來(lái)現(xiàn)實(shí),后來(lái)發(fā)現(xiàn)無(wú)論怎么優(yōu)化SQL語(yǔ)句都不能使網(wǎng)站的性能提高上去,于是他決定自己做一個(gè)專屬于LLOOGG的數(shù)據(jù)庫(kù),這個(gè)就是Redis的前身。后來(lái),Salvatore Sanfilippo將Redis1.0的源碼開(kāi)放到GitHub上,可能連他自己都沒(méi)想到,Redis后來(lái)如此受歡迎。
1.3 有誰(shuí)在用redis
??從Redis的官方公司統(tǒng)計(jì)來(lái)看,有很多重量級(jí)的公司都在使用Redis,如國(guó)外的Twitter、Instagram、Stack Overflow、GitHub等,國(guó)內(nèi)就更多了,如果單單從體量來(lái)統(tǒng)計(jì),新浪微博可以說(shuō)是全球最大的Redis使用者,除了新浪微博,還有像阿里巴巴、騰訊、百度、搜狐、優(yōu)酷土豆、美團(tuán)、小米、唯品會(huì)等公司都是Redis的使用者。
2 redis 和 mySQL的區(qū)別
2.1 什么是mySQL 數(shù)據(jù)庫(kù)
- mySQL是一個(gè)關(guān)系型數(shù)據(jù)庫(kù)管理系統(tǒng),由瑞典MySQL AB公司開(kāi)發(fā),目前屬于Oracle公司。
- mySQL是一種關(guān)聯(lián)數(shù)據(jù)庫(kù)管理系統(tǒng),關(guān)聯(lián)數(shù)據(jù)庫(kù)將數(shù)據(jù)保存在不同的表中,而不是將所有數(shù)據(jù)放在一個(gè)大倉(cāng)庫(kù)內(nèi),這樣就增加了速度并提高了靈活性。
- mySQL是開(kāi)源的,Mysql支持大型的數(shù)據(jù)庫(kù)。可以處理?yè)碛猩锨f(wàn)條記錄的大型數(shù)據(jù)庫(kù)。MySQL使用標(biāo)準(zhǔn)的SQL數(shù)據(jù)語(yǔ)言形式。
- mySQL可以允許于多個(gè)系統(tǒng)上,并且支持多種語(yǔ)言。這些編程語(yǔ)言包括C、C++、Python、Java、Perl、PHP、Eiffel、Ruby和Tcl等。
2.2 兩者的區(qū)別
??redis是內(nèi)存數(shù)據(jù)庫(kù),數(shù)據(jù)保存在內(nèi)存中,讀寫速度快。
??mySQL是關(guān)系型數(shù)據(jù)庫(kù),功能強(qiáng)大,數(shù)據(jù)訪問(wèn)也就慢。
3 redis特性
3.1 速度快
??正常情況下,Redis執(zhí)行命令的速度非常快,官方給出的數(shù)字是讀寫性能可以達(dá)到10萬(wàn)/秒。原因可以大致歸納為以下四點(diǎn):
??1. Redis的所有數(shù)據(jù)都是存放在內(nèi)存中的,所以把數(shù)據(jù)放在內(nèi)存中是Redis速度快的最主要原因。
??2. Redis是用C語(yǔ)言實(shí)現(xiàn)的,一般來(lái)說(shuō)C語(yǔ)言實(shí)現(xiàn)的程序“距離”操作系統(tǒng)更近,執(zhí)行速度相對(duì)會(huì)更快。
??3. Redis使用了單線程架構(gòu),預(yù)防了多線程可能產(chǎn)生的競(jìng)爭(zhēng)問(wèn)題。
3.2 基于鍵值對(duì)的數(shù)據(jù)結(jié)構(gòu)服務(wù)器
??Redis的全稱是REmote Dictionary Server,它主要提供了5種數(shù)據(jù)結(jié)構(gòu):字符串、哈希、列表、集合、有序集合,同時(shí)在字符串的基礎(chǔ)之上演變出了位字符串、哈希、列表、集合、有序集合,同時(shí)在字符串的基礎(chǔ)之上演變出了位圖(Bitmaps)和HyperLogLog兩種神奇的“數(shù)據(jù)結(jié)構(gòu)”,并且隨著LBS(Location Based Service,基于位置服務(wù))的不斷發(fā)展,Redis3.2版本中加入有關(guān)GEO(地理信息定位)的功能。
3.3 豐富的功能
??除了5種數(shù)據(jù)結(jié)構(gòu),Redis還提供了許多額外的功能:
????·提供了鍵過(guò)期功能,可以用來(lái)實(shí)現(xiàn)緩存。
????·提供了發(fā)布訂閱功能,可以用來(lái)實(shí)現(xiàn)消息系統(tǒng)。
????·支持Lua腳本功能,可以利用Lua創(chuàng)造出新的Redis命令。
????·提供了簡(jiǎn)單的事務(wù)功能,能在一定程度上保證事務(wù)特性。
????·提供了流水線(Pipeline)功能,這樣客戶端能將一批命令一次性傳到Redis,減少了網(wǎng)絡(luò)的開(kāi)銷。
3.4 簡(jiǎn)單穩(wěn)定
??Redis的簡(jiǎn)單主要表現(xiàn)在三個(gè)方面。首先,Redis的源碼很少,早期版本的代碼只有2萬(wàn)行左右,3.0版本以后由于添加了集群特性,代碼增至5萬(wàn)行左右。其次,Redis使用單線程模型,這樣不僅使得Redis服務(wù)端處理模型變得簡(jiǎn)單,而且也使得客戶端開(kāi)發(fā)變得簡(jiǎn)單。最后,Redis不需要依賴于操作系統(tǒng)中的類庫(kù),Redis自己實(shí)現(xiàn)了事件處理的相關(guān)功能。
??Redis雖然很簡(jiǎn)單,但是不代表它不穩(wěn)定。以筆者維護(hù)的上千個(gè)Redis為例,沒(méi)有出現(xiàn)過(guò)因?yàn)镽edis自身bug而宕掉的情況。
3.5 客戶端語(yǔ)言多
??Redis提供了簡(jiǎn)單的TCP通信協(xié)議,很多編程語(yǔ)言可以很方便地接入到Redis,并且由于Redis受到社區(qū)和各大公司的廣泛認(rèn)可,所以支持Redis的客戶端語(yǔ)言也非常多,幾乎涵蓋了主流的編程語(yǔ)言,例如Java、PHP、Python、C、C++、Nodejs等。
3.6 持久化
??通常看,將數(shù)據(jù)放在內(nèi)存中是不安全的,一旦發(fā)生斷電或者機(jī)器故障,重要的數(shù)據(jù)可能就會(huì)丟失,因此Redis提供了兩種持久化方式:RDB和AOF,即可以用兩種策略將內(nèi)存的數(shù)據(jù)保存到硬盤中(,這樣就保證了數(shù)據(jù)的可持久性。
3.7 主從復(fù)制
??Redis提供了復(fù)制功能,實(shí)現(xiàn)了多個(gè)相同數(shù)據(jù)的Redis副本,復(fù)制功能是分布式Redis的基礎(chǔ)。
3.8 高可用和分布式
??Redis從2.8版本正式提供了高可用實(shí)現(xiàn)Redis Sentinel,它能夠保證Redis節(jié)點(diǎn)的故障發(fā)現(xiàn)和故障自動(dòng)轉(zhuǎn)移。Redis從3.0版本正式提供了分布式實(shí)現(xiàn)Redis Cluster,它是Redis真正的分布式實(shí)現(xiàn),提供了高可用、讀寫和容量的擴(kuò)展性。
4 安裝Redis
??安裝參考 http://m.itdecent.cn/p/150307f7e43f
4.1 安裝依賴
yum install gcc gcc-c++ make
yum install wget
4.2 下載redis軟件安裝包
wget http://download.redis.io/redis-stable.tar.gz
4.3 解壓安裝
tar xvzf redis-stable.tar.gz
cd redis-stable
mv redis-stable /usr/local/redis
cd /usr/local/redis
make
make install
編譯過(guò)程中可能會(huì)有如下報(bào)錯(cuò):
在包含自 adlist.c:34 的文件中:
zmalloc.h:50:31: 錯(cuò)誤:jemalloc/jemalloc.h:沒(méi)有那個(gè)文件或目錄
zmalloc.h:55:2: 錯(cuò)誤:#error "Newer version of jemalloc required"
make[1]: *** [adlist.o] 錯(cuò)誤 1
make[1]: Leaving directory `/usr/local/redis/src'
需要執(zhí)行如下命令:
make MALLOC=libc && make install
2.4 修改配置文件
復(fù)制配置文件
cp redis.conf /etc/
修改配置文件
vim /etc/redis.conf
配置修改如下:
pidfile /var/run/redis.pid
bind 127.0.0.1 本機(jī)ip #bind ip,綁定本機(jī)ip即可
daemonize yes #后臺(tái)運(yùn)行
loglevel notice
logfile /data/logs/redis/redis.log
#設(shè)置log目錄,需手動(dòng)創(chuàng)建/data/logs/redis/目錄,mkdir /data/logs/redis/
dir /data/redis/
#設(shè)置文件目錄,需手動(dòng)創(chuàng)建mkdir /data/redis/
databases 16
save 900 1
save 300 10
save 60 10000
rdbcompression yes
dbfilename dump.rdb #設(shè)置db文件
appendonly no
創(chuàng)建數(shù)據(jù)目錄 并更改權(quán)限
mkdir /data/redis/
chmod -R 755 /data
4.5 驗(yàn)證是否安裝成功
使用 /etc/redis.conf 配置文件啟動(dòng)redis
redis-server /etc/redis.conf
查看端口:
netstat -ntlp |grep 6379

如果顯示端口正在使用則說(shuō)明安裝成功,沒(méi)有就沒(méi)安裝成功。
鏈接redis 測(cè)試:
/usr/local/redis/src/redis-cli
存值
set keyTest "hello"
取值
get keyTest

關(guān)閉redis:
/usr/local/redis/src/redis-cli shutdown
5 redis API
5.1 常用命令
- 查看所有鍵
keys pattern
注:keys 后面接查找表示式,在庫(kù)中有大量key時(shí),使用keys * 需要謹(jǐn)慎,可能會(huì)引發(fā)線程阻塞。 - 查看所有鍵
dbsize - 查看所有鍵
exists key - 刪除鍵
del key [key ...]
可同時(shí)刪除多個(gè)key,返回成功刪除鍵的個(gè)數(shù) - 鍵過(guò)期
expire key seconds
設(shè)置鍵的過(guò)期時(shí)間,當(dāng)鍵過(guò)期后,用 get key 取鍵值,會(huì)返回nil。 - 鍵過(guò)期查詢命令
ttl key
返回值為大于等于0的整數(shù):鍵過(guò)期的剩余時(shí)間。
返回值為-1: 鍵沒(méi)有過(guò)期時(shí)間。
返回值為-2: 鍵以過(guò)期或不存在。 - 鍵的結(jié)構(gòu)類型
type key - 設(shè)置鍵值
set key value [ex seconds] [px milliseconds] [nx|xx]
[ex seconds] 為秒級(jí)過(guò)期時(shí)間
[px milliseconds] 為毫秒級(jí)過(guò)期時(shí)間
[nx] 當(dāng)鍵不存在時(shí)才設(shè)置,用于添加
[xx] 當(dāng)鍵存在時(shí)才設(shè)置,用于更新 - 獲取鍵值
get key - incr 自增計(jì)數(shù)
incr key
注:僅對(duì)整數(shù)有效,否則返回錯(cuò)誤 - decr 自減計(jì)數(shù)
decr key
注:僅對(duì)整數(shù)有效,否則返回錯(cuò)誤 - 鍵重命名
rename oldkey newkey - 遷移鍵
move key db
把鍵移動(dòng)db庫(kù)
6 主從復(fù)制
6.1 建立主從方式
建立主從復(fù)制方式有三種:
- 在從redis配置文件中加入 slaveof {masterHost} {masterHost},redis啟動(dòng)生效。
- 啟動(dòng)從redis后,在命令中執(zhí)行 --slaveof {masterHost} {masterHost}。
- 直接使用 slaveof {masterHost} {masterHost}。
查看主從復(fù)制狀態(tài):info replication
6.2 斷開(kāi)復(fù)制
在從節(jié)點(diǎn)上執(zhí)行命令 :slaveof no one
切換主方式:slaveof {newMaster}
注:切換主節(jié)點(diǎn)后從節(jié)點(diǎn)上的數(shù)據(jù)會(huì)被清空,線上慎用 slaveof命令。
6.3 復(fù)制原理
復(fù)制流程大致分為一下步驟:
- 保存master信息
slave 上執(zhí)行slaveof 命令后,便把master 的信息保存起來(lái)。 master 的 master_link_status 變?yōu)橄戮€狀態(tài)。 - slave 內(nèi)部通過(guò)每秒運(yùn)行的定時(shí)任務(wù)維護(hù)復(fù)制相關(guān)邏輯任務(wù),發(fā)現(xiàn)新master 節(jié)點(diǎn)后,會(huì)嘗試與該節(jié)點(diǎn)建立網(wǎng)絡(luò)鏈接。
- 發(fā)送ping命令
發(fā)送ping命令,當(dāng)master 回復(fù) pong 時(shí),說(shuō)明建立成功。 - 權(quán)限驗(yàn)證
- 數(shù)據(jù)集同步
在和master 正常通信后,首次建立鏈接,master會(huì)把持有的所有數(shù)據(jù)發(fā)給slave,這次為全量同步。 - 命令持續(xù)復(fù)制
在首次與master 同步完成后,后續(xù)master會(huì)持續(xù)把寫命令發(fā)送給slave,保證數(shù)據(jù)一致性,為部分同步。
6.4 心跳
在主從建立復(fù)制后,他們之間維護(hù)著長(zhǎng)鏈接并彼此發(fā)送心跳命令。
- 主從彼此都有心跳檢測(cè)機(jī)制,可以通過(guò)client list命令查看信息,主節(jié)點(diǎn)鏈接狀態(tài)為 flags=N,從節(jié)點(diǎn)鏈接狀態(tài)為flags=S。
- 主節(jié)點(diǎn)默認(rèn)每 10s 對(duì)從節(jié)點(diǎn)發(fā)送ping命令??赏ㄟ^(guò) repl-ping-slave-period 控制發(fā)送頻率。
- 從節(jié)點(diǎn)每 1s 給主節(jié)點(diǎn)發(fā)送 replconf ack {offset} 命令,上報(bào)自身當(dāng)前的復(fù)制偏移量。
7 內(nèi)存管理與優(yōu)化
7.1 內(nèi)存管理
7.1.1 設(shè)置內(nèi)存上限
maxmemory 參數(shù)可限制最大可使用內(nèi)存,主要目的:
- 用于緩存場(chǎng)景,當(dāng)超出內(nèi)存上限時(shí),使用LRU等刪除策略釋放空間。
- 防止所用內(nèi)存超過(guò)服務(wù)器內(nèi)存。
注:由于內(nèi)存碎片率的存在,實(shí)際消耗的內(nèi)存可能會(huì)比maxmemor設(shè)置的大。
7.1.2 動(dòng)態(tài)調(diào)整內(nèi)存上限
config set maxmemory 1GB
該命令可動(dòng)態(tài)調(diào)整內(nèi)存上限
注:為了防止極端情況下導(dǎo)致系統(tǒng)內(nèi)存被耗盡,建議每個(gè)redis進(jìn)程都設(shè)置maxmemory參數(shù),默認(rèn)無(wú)限大。
7.1.3 內(nèi)存回收策略
內(nèi)存回收策略體現(xiàn)在以下兩個(gè)方面:
. 刪除到期鍵。
. 內(nèi)存使用達(dá)到maxmemory上限觸發(fā)控制策略。
- 刪除過(guò)期鍵
1)惰性刪除
??redis 讀取帶過(guò)期屬性的鍵時(shí),當(dāng)該鍵已過(guò)期,會(huì)執(zhí)行刪除命令并返回空。
2)定時(shí)任務(wù)刪除
?? redis 內(nèi)部維護(hù)一個(gè)定時(shí)任務(wù),默認(rèn)每秒運(yùn)行10次,可通過(guò) hz 參數(shù)配置。每次任務(wù)隨機(jī)檢查20個(gè)鍵,當(dāng)發(fā)現(xiàn)過(guò)期鍵時(shí)刪除對(duì)應(yīng)鍵;
?? 如果超過(guò)25%的鍵過(guò)期,循環(huán)執(zhí)行回收邏輯直到不足25%或超時(shí)為止,默認(rèn)超時(shí)時(shí)間為25ms;該次為慢模式。
??如果回收邏輯超時(shí),則在觸發(fā)內(nèi)部事件之前再次以快模式回收過(guò)期鍵。
??快慢兩種模式刪除邏輯相同,只是執(zhí)行超時(shí)時(shí)間不同。慢模式超時(shí)為25ms,快模式超時(shí)為 1ms 且每 2 s內(nèi)只能運(yùn)行一次。 - 內(nèi)存溢出控制策略
當(dāng)redis內(nèi)存使用達(dá)到maxmemory上限觸發(fā)控制策略。
redis 支持 6種控制策略,由maxmemory-policy參數(shù)控制。
1)noeviction:默認(rèn)策略。不刪除任何數(shù)據(jù),redis變?yōu)橹蛔x模式,寫入報(bào)錯(cuò)。
2)volatile-lru:根據(jù)LRU算法刪除設(shè)置超時(shí)屬性的鍵,直到空間足夠?yàn)橹?。沒(méi)有可刪除鍵時(shí),回退到noeviction策略。
3)allkeys-lru:根據(jù)LRU算法刪除鍵,不管鍵有沒(méi)有設(shè)置超時(shí)屬性,直到空間足夠?yàn)橹埂?br> 4)allkeys-random:隨機(jī)刪除所有鍵,直到空間足夠?yàn)橹埂?br> 5)volatile-random:隨機(jī)刪除過(guò)期鍵,直到空間足夠?yàn)橹埂?br> 6)volatile-ttl:根據(jù)值的ttl屬性,刪除將要過(guò)期的數(shù)據(jù)。沒(méi)有可刪除鍵,回退到noeviction策略。
內(nèi)存溢出控制策略 可采用 config set maxmemory-policy {policy} 動(dòng)態(tài)設(shè)置。
7.2 內(nèi)存優(yōu)化
redis 內(nèi)存優(yōu)化的常用的方法有:
- redisObject對(duì)象
- 縮減鍵值對(duì)象
- 共享對(duì)象池
- 字符串優(yōu)化
- 編碼優(yōu)化
- 控制鍵的數(shù)量
8 redis cluster 集群
8.1 cluster 數(shù)據(jù)分布
8.1.1 分布式數(shù)據(jù)分區(qū)規(guī)則
??分布式數(shù)據(jù)庫(kù)會(huì)把整個(gè)數(shù)據(jù)集按照分區(qū)規(guī)則將數(shù)據(jù)集劃分到多個(gè)節(jié)點(diǎn)上,每個(gè)節(jié)點(diǎn)負(fù)責(zé)一部分?jǐn)?shù)據(jù)集。
??常用的分區(qū)規(guī)則由哈希分區(qū)和順序分區(qū)。redis cluster 采用的是哈希分區(qū)規(guī)則。
常見(jiàn)的哈希分區(qū)規(guī)則有以下幾種:
- 節(jié)點(diǎn)取余分區(qū)
??將key(鍵數(shù)量)% N(節(jié)點(diǎn)數(shù))計(jì)算出哈希值,決定該數(shù)據(jù)映射到哪個(gè)節(jié)點(diǎn)上。
??優(yōu)點(diǎn):簡(jiǎn)單。
??缺點(diǎn):當(dāng)節(jié)點(diǎn)數(shù) N 發(fā)生變化時(shí),映射關(guān)系會(huì)重新計(jì)算,可能會(huì)導(dǎo)致數(shù)據(jù)遷移。
為了避免數(shù)據(jù)遷移,通常節(jié)點(diǎn)數(shù)擴(kuò)張或縮減會(huì)成倍數(shù)關(guān)系。 - 一致性哈希分區(qū)
??思路是為系統(tǒng)中每個(gè)節(jié)點(diǎn)分配一個(gè)token,范圍一般在0~,這寫token構(gòu)成一個(gè)哈希環(huán)。數(shù)據(jù)讀寫時(shí),先根據(jù)key計(jì)算hash值,然后順時(shí)針找到第一個(gè)大于等于該哈希值的token節(jié)點(diǎn)。
- 虛擬槽分區(qū)
??虛擬槽分區(qū)使用哈??臻g,用哈希函數(shù)把所有數(shù)據(jù)映射到一個(gè)固定的范圍整數(shù)集合中,整數(shù)定義為槽(slot)。這個(gè)范圍遠(yuǎn)大于節(jié)點(diǎn)數(shù)。槽是集群內(nèi)數(shù)據(jù)管理和遷移的基本單位。
??redis cluster 槽的范圍 0~16383。當(dāng)有五個(gè)節(jié)點(diǎn)時(shí),每個(gè)節(jié)點(diǎn)大約負(fù)責(zé)管理3276個(gè)槽。
8.1.2 redis cluster 數(shù)據(jù)分區(qū)
??redis cluster采用虛擬槽分區(qū)規(guī)則,所有鍵根據(jù)哈希函數(shù)映射到0~16383個(gè)槽上。計(jì)算公式:slot=CRC16(key)&16383。
redis 虛擬槽分區(qū)特點(diǎn):
- 數(shù)據(jù)和節(jié)點(diǎn)解藕,簡(jiǎn)化節(jié)點(diǎn)擴(kuò)容和縮減難度。
- 不需要客戶端或代理服務(wù)維護(hù)槽元數(shù)據(jù),自身就能完成。
- 支持節(jié)點(diǎn)、槽、鍵之間的映射查詢。
8.2 redis cluster 功能限制
相對(duì)單例,cluster上存在以下功能限制:
- 只支持相同slot值的key 執(zhí)行批量操作,如mset、mget。
- 只支持多key 在同一個(gè)節(jié)點(diǎn)上的事務(wù)操作。
- 不能將大鍵值對(duì)象如hash、list映射到不同節(jié)點(diǎn)上。
- 不支持多數(shù)據(jù)庫(kù)空間,只有一個(gè)db0 數(shù)據(jù)庫(kù)。單例有db0~db15 共16個(gè)數(shù)據(jù)空間。
- 復(fù)制結(jié)構(gòu)只支持一層。
8.3 redis cluster 創(chuàng)建
8.3.1 服務(wù)器配置說(shuō)明
以三主三從為例:
只有三臺(tái)服務(wù)器,所以每臺(tái)服務(wù)上創(chuàng)建兩個(gè)redis 實(shí)例,用作一主一從。
注:需每臺(tái)服務(wù)器上先安裝好redis服務(wù),可參考步驟4 安裝Redis
8.3.2 安裝依賴
安裝ruby
yum install ruby
安裝 gem
yum install rubygems
安裝 gem
gem install redis
注:redis 安裝需要ruby 版本大于 2.2.2
升級(jí)ruby方法可參考 http://m.itdecent.cn/p/a1a4d59490d7
8.3.3 創(chuàng)建實(shí)例
注:以一臺(tái)服務(wù)器為例,主從實(shí)例的端口分別為 6379、6479
復(fù)制配置文件
cp /usr/local/redis/redis.conf /etc/redis_6379.conf
cp /usr/local/redis/redis.conf /etc/redis_6479.conf
注:每臺(tái)服務(wù)器應(yīng)先裝好redis服務(wù),
修改配置文件,修改項(xiàng)如下:
port 6379 #對(duì)應(yīng)實(shí)例的端口
pidfile /var/run/redis.pid
bind 127.0.0.1 本機(jī)ip #bind ip,綁定本機(jī)ip即可
daemonize yes
loglevel notice
logfile /data/logs/redis/redis_6379.log
#設(shè)置log目錄,需手動(dòng)創(chuàng)建/data/logs/redis目錄,mkdir /data/logs/redis
dir /data/redis/redis_6379/
#設(shè)置文件目錄,需手動(dòng)創(chuàng)建mkdir /data/redis/redis_6379/
databases 16
save 900 1
save 300 10
save 60 10000
rdbcompression yes
dbfilename dump_6379.rdb
#設(shè)置db文件
appendonly no
8.3.4 啟動(dòng)實(shí)例
使用對(duì)應(yīng)的配置文件啟動(dòng)
redis-server /etc/redis_6379.conf
redis-server /etc/redis_6479.conf
查看線程,端口驗(yàn)證是否啟動(dòng)正好
ps -ef|grep redis
netstat -tnlp | grep redis
8.3.5 創(chuàng)建集群
/usr/local/redis/src/redis-trib.rb create --replicas 1 主機(jī)1:6379 主機(jī)2:6379 主機(jī)3:6379 主機(jī)1:6479 主機(jī)2:6479 主機(jī)3:6479
注:只需在一臺(tái)服務(wù)器上創(chuàng)建集群。redis-trib.rb 會(huì)盡可能的將主從節(jié)點(diǎn)分配在不同的機(jī)器上,所以順序會(huì)重新分配。
8.3.6 驗(yàn)證集群
鏈接集群,加 –c 參數(shù)意味鏈接集群
redis-cli -h 172.31.78.3 -c -p 6379
8.3.7 集群完整性檢查
redis-trib.rb check 172.31.78.3:6379
該命令可檢查集群的完整性,只需檢查任意一個(gè)節(jié)點(diǎn)。返回結(jié)果會(huì)列出集群節(jié)點(diǎn)信息
8.4 節(jié)點(diǎn)通信
8.4.1 通信流程
- 集群每個(gè)節(jié)點(diǎn)都會(huì)開(kāi)辟一個(gè)TCP通道,用于節(jié)點(diǎn)間通信。通信端口為基礎(chǔ)端口上加10000.
- 每個(gè)節(jié)點(diǎn)在固定周期內(nèi)通過(guò)特定規(guī)則選擇幾個(gè)節(jié)點(diǎn)發(fā)送ping消息。
- 收到ping消息的節(jié)點(diǎn)回復(fù)pong消息作為響應(yīng)。
注:因?yàn)橥ㄐ哦丝?= 基礎(chǔ)端口 + 10000。所以如果基礎(chǔ)端口為 6379,這防火墻需要放開(kāi) 6379 和 16379 這兩個(gè)端口才可讓集群正常通信。
8.4.2 節(jié)點(diǎn)選擇
??redis cluster 節(jié)點(diǎn)內(nèi)通信采用固定頻率,每秒執(zhí)行10次定時(shí)任務(wù)。ping/pong消息攜帶當(dāng)前節(jié)點(diǎn)和其他節(jié)點(diǎn)的狀態(tài)數(shù)據(jù),頻繁通信勢(shì)必會(huì)加重計(jì)算和帶寬的負(fù)擔(dān)。所以每次定時(shí)任務(wù)選擇通信的節(jié)點(diǎn)非常重要。
- 選擇發(fā)送信息的節(jié)點(diǎn)數(shù)量
??集群每秒會(huì)隨機(jī)選取5個(gè)節(jié)點(diǎn),找出最久沒(méi)通信的節(jié)點(diǎn)發(fā)送ping消息。每100毫秒會(huì)掃描本地節(jié)點(diǎn)列表,發(fā)現(xiàn)節(jié)點(diǎn)最近接受pong消息的消息大于cluster_node_timeout/2 時(shí)會(huì)立即發(fā)送ping消息。所以每個(gè)節(jié)點(diǎn)每秒發(fā)送的ping消息量 = 1 + 10 * num (node.pong_receives>cluster_node_timeout/2); - 消息數(shù)據(jù)量
??每個(gè)ping消息的數(shù)據(jù)體現(xiàn)在消息頭和消息體中,消息頭的大小為 2KB,消息體需看偽代碼。
8.5 伸縮
新 node 加入集群方法。
可使用redis-trib.rb 工具直接添加。
// 添加新節(jié)點(diǎn)
redis-trib.rb add-node newhost1:port newhost2:port
//添加新節(jié)點(diǎn)并設(shè)置為master-id的從節(jié)點(diǎn)
redis-trib.rb add-node newhost:port --slave --master-id
8.6 請(qǐng)求重定向
??redis 接受到key命令時(shí)先計(jì)算key對(duì)應(yīng)的槽,再找出槽對(duì)應(yīng)的節(jié)點(diǎn)。如果節(jié)點(diǎn)是本身就處理命令,否則回復(fù)MOVED重定向錯(cuò)誤,通知客戶端請(qǐng)求正確節(jié)點(diǎn)。

8.6 故障轉(zhuǎn)移
8.6.1 故障發(fā)現(xiàn)
??redis 通過(guò)ping/pong消息實(shí)現(xiàn)節(jié)點(diǎn)通信,同時(shí)攜帶節(jié)點(diǎn)的消息,如主從狀態(tài)、節(jié)點(diǎn)故障、槽信息等。
??每個(gè)節(jié)點(diǎn)定期會(huì)給其他節(jié)點(diǎn)發(fā)送ping消息,如果在 cluster-node-timeout時(shí)間內(nèi)收不到其他節(jié)點(diǎn)回復(fù)的pong消息,則會(huì)將該節(jié)點(diǎn)標(biāo)記為主觀下線狀態(tài)(pfail)。
??當(dāng)某個(gè)節(jié)點(diǎn)判斷另一個(gè)節(jié)點(diǎn)為主觀下線后,會(huì)將相應(yīng)節(jié)點(diǎn)狀態(tài)信息在集群內(nèi)傳播。當(dāng)超過(guò)半數(shù)的節(jié)點(diǎn)的任務(wù)該節(jié)點(diǎn)下線時(shí),會(huì)將該節(jié)點(diǎn)標(biāo)記為客觀下線狀態(tài)。這時(shí)該節(jié)點(diǎn)就會(huì)下線。
注:如果在cluster-node-time*2 時(shí)間內(nèi)無(wú)法收集到一半以上節(jié)點(diǎn)的下線報(bào)告,那么之前的下線報(bào)告將會(huì)過(guò)期。也就是說(shuō)主觀下線報(bào)告永遠(yuǎn)趕不上客觀下線報(bào)告,那么故障節(jié)點(diǎn)就不會(huì)被標(biāo)記客觀下線而導(dǎo)致故障轉(zhuǎn)移失敗。所以cluster-node-time不建議設(shè)置過(guò)小。
8.6.2 故障恢復(fù)
?? 當(dāng)故障節(jié)點(diǎn)變?yōu)榭陀^下線后,如果該節(jié)點(diǎn)為主節(jié)點(diǎn)則需要在從節(jié)點(diǎn)中選出一個(gè)節(jié)點(diǎn)替補(bǔ)上。
恢復(fù)流程如下:
- 資格檢查
如果從節(jié)點(diǎn)與主節(jié)點(diǎn)斷線的時(shí)間超過(guò) cluster-node-time*cluster-slave-validity-factor,則該節(jié)點(diǎn)沒(méi)有資格。cluster-slave-validity-factor 為從節(jié)點(diǎn)有效因子,默認(rèn)為10。 - 準(zhǔn)備選舉時(shí)間
當(dāng)從節(jié)點(diǎn)擁有資格后,更新觸發(fā)選舉時(shí)間(failover_auth_time),達(dá)到該時(shí)間繼續(xù)后續(xù)流程。 - 發(fā)起選舉
發(fā)起選舉流程:
1)更新配置紀(jì)元
2)廣播選舉消息 -
選舉投票
只有主節(jié)點(diǎn)才會(huì)處理故障選舉消息。
image.png - 替換主節(jié)點(diǎn)
1)從節(jié)點(diǎn)取消復(fù)制變?yōu)橹鞴?jié)點(diǎn)
2)執(zhí)行clusterDelSlot操作撤銷故障主節(jié)點(diǎn)負(fù)責(zé)的槽,并執(zhí)行clustrAddSlot把這些槽給自己
3)在集群中廣播自己pong消息,說(shuō)自己登基了,不是備胎了。
8.6.3 故障轉(zhuǎn)移時(shí)間
故障發(fā)現(xiàn)到故障恢復(fù)花銷的時(shí)間大致可以由以下估計(jì):
- 主觀下線識(shí)別時(shí)間 = cluster-node-timeout
- 主觀下線傳播時(shí)間 <= cluster-node-timeout/2
- 從節(jié)點(diǎn)轉(zhuǎn)移時(shí)間 <= 1000毫秒
注:cluster-node-timeout 的默認(rèn)時(shí)間為 15秒
8.7 集群運(yùn)維
8.7.1 集群完整性
集群上每個(gè)槽(slot)都需要有一個(gè)非故障的主節(jié)點(diǎn)管理負(fù)責(zé),否則該集群會(huì)變?yōu)椴豢捎谩?/p>
8.7.2 集群傾斜
集群傾斜是指不同節(jié)點(diǎn)間數(shù)據(jù)量和請(qǐng)求量出現(xiàn)明顯差異,這會(huì)增大負(fù)載均衡和開(kāi)發(fā)運(yùn)維難度。
- 數(shù)據(jù)傾斜
1)節(jié)點(diǎn)和槽分配嚴(yán)重不均。
2)槽對(duì)應(yīng)鍵的數(shù)量差異過(guò)大。
3)集合對(duì)象包含大量元素。
4)內(nèi)存相關(guān)配置不一致。
針對(duì) 1)可使用redis-trib.rb info host:ip 命令定位。
針對(duì) 2)可使用cluster countkeysinslot {slot} 先獲取對(duì)應(yīng)槽的鍵數(shù),在 使用 cluster getkeysinslot {slot} {count} 循環(huán)迭代出槽下所以鍵。
針對(duì) 3)可使用redis-cli --bigkeys 識(shí)別大集合對(duì)象,在進(jìn)行數(shù)據(jù)轉(zhuǎn)移。
8.7.3 數(shù)據(jù)遷移
將單機(jī)redis數(shù)據(jù)轉(zhuǎn)移到集群環(huán)境下:
redis-trib.rb import host:port --form <arg> --copy --replace
注:
- 只能從單機(jī)轉(zhuǎn)移值集群。
- 不支持在線轉(zhuǎn)移,數(shù)據(jù)提供方應(yīng)先停止讀寫。
- 不支持?jǐn)帱c(diǎn)續(xù)傳。
- 單線程進(jìn)行數(shù)據(jù)遷移,大數(shù)據(jù)量時(shí)遷移速度慢。
8.7 springboot + jedis + RedisTemplate java客戶端接入集群demo
配置
// 從配置文件中獲取集群配置信息
@Value("${spring.redis.cluster.nodes}")
private String clusterNodes; // 節(jié)點(diǎn)格式 “host1:port,host2:port,host3:port,host4:port”
@Value("${spring.redis.timeout}")
private String timeOut;
@Value("${spring.redis.password}") // 集群密碼
private String password;
@Value("${spring.redis.jedis.pool.max-active}")
private int maxActive;
@Value("${spring.redis.jedis.pool.max-wait}")
private long maxWait;
@Value("${spring.redis.jedis.pool.max-idle}")
private int maxIdle;
@Value("${spring.redis.jedis.pool.min-idle}")
private int minIdle;
@Bean
public RedisTemplate<String, Object> redisTemplate() {
Map<String, Object> source = new HashMap<String, Object>();
source.put("spring.redis.cluster.nodes", clusterNodes);
source.put("spring.redis.cluster.timeout", timeOut);
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(new MapPropertySource("RedisProperties", source));
redisClusterConfiguration.setPassword(RedisPassword.of(password));
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxIdle(maxIdle);
config.setMinIdle(minIdle);
config.setMaxWaitMillis(maxWait);
config.setMaxTotal(maxActive);
JedisConnectionFactory factory = new JedisConnectionFactory(redisClusterConfiguration,config);
factory.getClientConfiguration();
factory.afterPropertiesSet();
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericToStringSerializer<String>(String.class));
redisTemplate.setConnectionFactory(factory);
return redisTemplate;
}
使用方法
@Autowired
private RedisTemplate redisTemplate; // 自動(dòng)注入
/** 默認(rèn)過(guò)期時(shí)長(zhǎng),單位:秒 */
public final static long DEFAULT_EXPIRE = 60 * 60 * 24 * 20;
/** 當(dāng)天就過(guò)期,單位:秒 */
public final static long DATE_EXPIRE = 60 * 60 * 24;
/** 不設(shè)置過(guò)期時(shí)長(zhǎng) */
public final static long NOT_EXPIRE = -1;
public void set(String key, String value, long expire){
valueOperations.set(key, value);
if (expire != NOT_EXPIRE) {
redisTemplate.expire(key, expire, TimeUnit.SECONDS);
}
}
public void set(String key, String value){
set(key, value, NOT_EXPIRE);
}
public boolean del(String key){
return redisTemplate.delete(key);
}
public String get(String key, long expire) {
String value = valueOperations.get(key);
if(expire != NOT_EXPIRE){
redisTemplate.expire(key, expire, TimeUnit.SECONDS);
}
return value;
}
public <T> T get(String key, Class<T> clazz, long expire) {
String value = valueOperations.get(key);
if (expire != NOT_EXPIRE) {
redisTemplate.expire(key,expire,TimeUnit.SECONDS);
}
return value == null ? null : fromJson(value, clazz);
}
參考文獻(xiàn):
- 《Redis開(kāi)發(fā)與運(yùn)維》作者: 付磊 張益軍
