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還提供鍵過期、發(fā)布訂閱摇幻、事務(wù)、流水線挥萌、Lua腳本等功能。
1.2 redis的作者是誰(shuí)
?? redis的作者Salvatore Sanfilippo 在開發(fā)一個(gè)叫LLOOGG的網(wǎng)站時(shí)需要實(shí)現(xiàn)一個(gè)高性能的隊(duì)列功能枉侧,一開始是使用MySql 來現(xiàn)實(shí)引瀑,后來發(fā)現(xiàn)無論怎么優(yōu)化SQL語(yǔ)句都不能使網(wǎng)站的性能提高上去,于是他決定自己做一個(gè)專屬于LLOOGG的數(shù)據(jù)庫(kù)榨馁,這個(gè)就是Redis的前身憨栽。后來,Salvatore Sanfilippo將Redis1.0的源碼開放到GitHub上翼虫,可能連他自己都沒想到屑柔,Redis后來如此受歡迎。
1.3 有誰(shuí)在用redis
??從Redis的官方公司統(tǒng)計(jì)來看珍剑,有很多重量級(jí)的公司都在使用Redis掸宛,如國(guó)外的Twitter、Instagram招拙、Stack Overflow唧瘾、GitHub等措译,國(guó)內(nèi)就更多了,如果單單從體量來統(tǒng)計(jì)饰序,新浪微博可以說是全球最大的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公司開發(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是開源的檬寂,Mysql支持大型的數(shù)據(jù)庫(kù)终抽。可以處理?yè)碛猩锨f條記錄的大型數(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ù)訪問也就慢聊倔。
3 redis特性
3.1 速度快
??正常情況下,Redis執(zhí)行命令的速度非成玻快耙蔑,官方給出的數(shù)字是讀寫性能可以達(dá)到10萬/秒。原因可以大致歸納為以下四點(diǎn):
??1. Redis的所有數(shù)據(jù)都是存放在內(nèi)存中的孤荣,所以把數(shù)據(jù)放在內(nèi)存中是Redis速度快的最主要原因甸陌。
??2. Redis是用C語(yǔ)言實(shí)現(xiàn)的,一般來說C語(yǔ)言實(shí)現(xiàn)的程序“距離”操作系統(tǒng)更近盐股,執(zhí)行速度相對(duì)會(huì)更快钱豁。
??3. Redis使用了單線程架構(gòu),預(yù)防了多線程可能產(chǎn)生的競(jìng)爭(zhēng)問題疯汁。
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還提供了許多額外的功能:
????·提供了鍵過期功能,可以用來實(shí)現(xiàn)緩存磕谅。
????·提供了發(fā)布訂閱功能,可以用來實(shí)現(xiàn)消息系統(tǒng)雾棺。
????·支持Lua腳本功能膊夹,可以利用Lua創(chuàng)造出新的Redis命令。
????·提供了簡(jiǎn)單的事務(wù)功能捌浩,能在一定程度上保證事務(wù)特性放刨。
????·提供了流水線(Pipeline)功能,這樣客戶端能將一批命令一次性傳到Redis尸饺,減少了網(wǎng)絡(luò)的開銷进统。
3.4 簡(jiǎn)單穩(wěn)定
??Redis的簡(jiǎn)單主要表現(xiàn)在三個(gè)方面助币。首先,Redis的源碼很少螟碎,早期版本的代碼只有2萬行左右眉菱,3.0版本以后由于添加了集群特性,代碼增至5萬行左右掉分。其次俭缓,Redis使用單線程模型,這樣不僅使得Redis服務(wù)端處理模型變得簡(jiǎn)單酥郭,而且也使得客戶端開發(fā)變得簡(jiǎn)單华坦。最后,Redis不需要依賴于操作系統(tǒng)中的類庫(kù)不从,Redis自己實(shí)現(xiàn)了事件處理的相關(guān)功能惜姐。
??Redis雖然很簡(jiǎn)單,但是不代表它不穩(wěn)定椿息。以筆者維護(hù)的上千個(gè)Redis為例歹袁,沒有出現(xiàn)過因?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://www.reibang.com/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
編譯過程中可能會(huì)有如下報(bào)錯(cuò):
在包含自 adlist.c:34 的文件中:
zmalloc.h:50:31: 錯(cuò)誤:jemalloc/jemalloc.h:沒有那個(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
如果顯示端口正在使用則說明安裝成功掷漱,沒有就沒安裝成功粘室。
鏈接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ù) - 鍵過期
expire key seconds
設(shè)置鍵的過期時(shí)間锦爵,當(dāng)鍵過期后,用 get key 取鍵值奥裸,會(huì)返回nil险掀。 - 鍵過期查詢命令
ttl key
返回值為大于等于0的整數(shù):鍵過期的剩余時(shí)間。
返回值為-1: 鍵沒有過期時(shí)間湾宙。
返回值為-2: 鍵以過期或不存在樟氢。 - 鍵的結(jié)構(gòu)類型
type key - 設(shè)置鍵值
set key value [ex seconds] [px milliseconds] [nx|xx]
[ex seconds] 為秒級(jí)過期時(shí)間
[px milliseconds] 為毫秒級(jí)過期時(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 斷開復(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 的信息保存起來。 master 的 master_link_status 變?yōu)橄戮€狀態(tài)静盅。 - slave 內(nèi)部通過每秒運(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í),說明建立成功市咽。 - 權(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ī)制遇八,可以通過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命令货矮。可通過 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)存超過服務(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)無限大雀久。
7.1.3 內(nèi)存回收策略
內(nèi)存回收策略體現(xiàn)在以下兩個(gè)方面:
. 刪除到期鍵宿稀。
. 內(nèi)存使用達(dá)到maxmemory上限觸發(fā)控制策略。
- 刪除過期鍵
1)惰性刪除
??redis 讀取帶過期屬性的鍵時(shí)赖捌,當(dāng)該鍵已過期祝沸,會(huì)執(zhí)行刪除命令并返回空矮烹。
2)定時(shí)任務(wù)刪除
?? redis 內(nèi)部維護(hù)一個(gè)定時(shí)任務(wù),默認(rèn)每秒運(yùn)行10次罩锐,可通過 hz 參數(shù)配置奉狈。每次任務(wù)隨機(jī)檢查20個(gè)鍵,當(dāng)發(fā)現(xiàn)過期鍵時(shí)刪除對(duì)應(yīng)鍵涩惑;
?? 如果超過25%的鍵過期仁期,循環(huán)執(zhí)行回收邏輯直到不足25%或超時(shí)為止,默認(rèn)超時(shí)時(shí)間為25ms竭恬;該次為慢模式跛蛋。
??如果回收邏輯超時(shí),則在觸發(fā)內(nèi)部事件之前再次以快模式回收過期鍵痊硕。
??快慢兩種模式刪除邏輯相同赊级,只是執(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)橹骨滔亍]有可刪除鍵時(shí),回退到noeviction策略谴分。
3)allkeys-lru:根據(jù)LRU算法刪除鍵锈麸,不管鍵有沒有設(shè)置超時(shí)屬性,直到空間足夠?yàn)橹埂?br> 4)allkeys-random:隨機(jī)刪除所有鍵牺蹄,直到空間足夠?yàn)橹埂?br> 5)volatile-random:隨機(jī)刪除過期鍵忘伞,直到空間足夠?yàn)橹埂?br> 6)volatile-ttl:根據(jù)值的ttl屬性,刪除將要過期的數(shù)據(jù)沙兰。沒有可刪除鍵氓奈,回退到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ī)則育勺。
常見的哈希分區(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ū)使用哈嫌景穑空間,用哈希函數(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ù)器配置說明
以三主三從為例:
只有三臺(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://www.reibang.com/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ì)開辟一個(gè)TCP通道痰憎,用于節(jié)點(diǎn)間通信票髓。通信端口為基礎(chǔ)端口上加10000.
- 每個(gè)節(jié)點(diǎn)在固定周期內(nèi)通過特定規(guī)則選擇幾個(gè)節(jié)點(diǎn)發(fā)送ping消息。
- 收到ping消息的節(jié)點(diǎn)回復(fù)pong消息作為響應(yīng)铣耘。
注:因?yàn)橥ㄐ哦丝?= 基礎(chǔ)端口 + 10000洽沟。所以如果基礎(chǔ)端口為 6379,這防火墻需要放開 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)吊骤,找出最久沒通信的節(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 通過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)超過半數(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)無法收集到一半以上節(jié)點(diǎn)的下線報(bào)告闽晦,那么之前的下線報(bào)告將會(huì)過期。也就是說主觀下線報(bào)告永遠(yuǎn)趕不上客觀下線報(bào)告提岔,那么故障節(jié)點(diǎn)就不會(huì)被標(biāo)記客觀下線而導(dǎo)致故障轉(zhuǎn)移失敗仙蛉。所以cluster-node-time不建議設(shè)置過小。
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í)間超過 cluster-node-time*cluster-slave-validity-factor,則該節(jié)點(diǎn)沒有資格赛惩。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消息喷面,說自己登基了,不是備胎了走孽。
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ù)載均衡和開發(fā)運(yùn)維難度磕瓷。
- 數(shù)據(jù)傾斜
1)節(jié)點(diǎn)和槽分配嚴(yán)重不均盒齿。
2)槽對(duì)應(yīng)鍵的數(shù)量差異過大。
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)過期時(shí)長(zhǎng)某饰,單位:秒 */
public final static long DEFAULT_EXPIRE = 60 * 60 * 24 * 20;
/** 當(dāng)天就過期,單位:秒 */
public final static long DATE_EXPIRE = 60 * 60 * 24;
/** 不設(shè)置過期時(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開發(fā)與運(yùn)維》作者: 付磊 張益軍