redis 小結(jié)

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ù)

  1. mySQL是一個(gè)關(guān)系型數(shù)據(jù)庫(kù)管理系統(tǒng),由瑞典MySQL AB公司開發(fā)罩缴,目前屬于Oracle公司蚊逢。
  2. 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)烙荷,這樣就增加了速度并提高了靈活性。
  3. mySQL是開源的檬寂,Mysql支持大型的數(shù)據(jù)庫(kù)终抽。可以處理?yè)碛猩锨f條記錄的大型數(shù)據(jù)庫(kù)桶至。MySQL使用標(biāo)準(zhǔn)的SQL數(shù)據(jù)語(yǔ)言形式昼伴。
  4. 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
image.png

如果顯示端口正在使用則說明安裝成功掷漱,沒有就沒安裝成功粘室。
鏈接redis 測(cè)試:

/usr/local/redis/src/redis-cli

存值

set keyTest "hello"

取值

get keyTest
image.png

關(guān)閉redis:

/usr/local/redis/src/redis-cli shutdown

5 redis API

5.1 常用命令

  1. 查看所有鍵
    keys pattern
    注:keys 后面接查找表示式,在庫(kù)中有大量key時(shí)卜范,使用keys * 需要謹(jǐn)慎衔统,可能會(huì)引發(fā)線程阻塞。
  2. 查看所有鍵
    dbsize
  3. 查看所有鍵
    exists key
  4. 刪除鍵
    del key [key ...]
    可同時(shí)刪除多個(gè)key海雪,返回成功刪除鍵的個(gè)數(shù)
  5. 鍵過期
    expire key seconds
    設(shè)置鍵的過期時(shí)間锦爵,當(dāng)鍵過期后,用 get key 取鍵值奥裸,會(huì)返回nil险掀。
  6. 鍵過期查詢命令
    ttl key
    返回值為大于等于0的整數(shù):鍵過期的剩余時(shí)間。
    返回值為-1: 鍵沒有過期時(shí)間湾宙。
    返回值為-2: 鍵以過期或不存在樟氢。
  7. 鍵的結(jié)構(gòu)類型
    type key
  8. 設(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è)置侠鳄,用于更新
  9. 獲取鍵值
    get key
  10. incr 自增計(jì)數(shù)
    incr key
    注:僅對(duì)整數(shù)有效埠啃,否則返回錯(cuò)誤
  11. decr 自減計(jì)數(shù)
    decr key
    注:僅對(duì)整數(shù)有效,否則返回錯(cuò)誤
  12. 鍵重命名
    rename oldkey newkey
  13. 遷移鍵
    move key db
    把鍵移動(dòng)db庫(kù)

6 主從復(fù)制

6.1 建立主從方式

建立主從復(fù)制方式有三種:

  1. 在從redis配置文件中加入 slaveof {masterHost} {masterHost}伟恶,redis啟動(dòng)生效碴开。
  2. 啟動(dòng)從redis后,在命令中執(zhí)行 --slaveof {masterHost} {masterHost}博秫。
  3. 直接使用 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ù)制流程大致分為一下步驟:

  1. 保存master信息
    slave 上執(zhí)行slaveof 命令后罢绽,便把master 的信息保存起來。 master 的 master_link_status 變?yōu)橄戮€狀態(tài)静盅。
  2. 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ò)鏈接蒿叠。
  3. 發(fā)送ping命令
    發(fā)送ping命令明垢,當(dāng)master 回復(fù) pong 時(shí),說明建立成功市咽。
  4. 權(quán)限驗(yàn)證
  5. 數(shù)據(jù)集同步
    在和master 正常通信后痊银,首次建立鏈接,master會(huì)把持有的所有數(shù)據(jù)發(fā)給slave施绎,這次為全量同步溯革。
  6. 命令持續(xù)復(fù)制
    在首次與master 同步完成后谷醉,后續(xù)master會(huì)持續(xù)把寫命令發(fā)送給slave致稀,保證數(shù)據(jù)一致性,為部分同步俱尼。

6.4 心跳

在主從建立復(fù)制后抖单,他們之間維護(hù)著長(zhǎng)鏈接并彼此發(fā)送心跳命令。

  1. 主從彼此都有心跳檢測(cè)機(jī)制遇八,可以通過client list命令查看信息矛绘,主節(jié)點(diǎn)鏈接狀態(tài)為 flags=N,從節(jié)點(diǎn)鏈接狀態(tài)為flags=S刃永。
  2. 主節(jié)點(diǎn)默認(rèn)每 10s 對(duì)從節(jié)點(diǎn)發(fā)送ping命令货矮。可通過 repl-ping-slave-period 控制發(fā)送頻率斯够。
  3. 從節(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)存雳刺,主要目的:

  1. 用于緩存場(chǎng)景劫灶,當(dāng)超出內(nèi)存上限時(shí),使用LRU等刪除策略釋放空間掖桦。
  2. 防止所用內(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. 刪除過期鍵
    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)行一次此衅。
  2. 內(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)化的常用的方法有:

  1. redisObject對(duì)象
  2. 縮減鍵值對(duì)象
  3. 共享對(duì)象池
  4. 字符串優(yōu)化
  5. 編碼優(yōu)化
  6. 控制鍵的數(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ī)則有以下幾種:

  1. 節(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)系捡多。
  2. 一致性哈希分區(qū)
    ??思路是為系統(tǒng)中每個(gè)節(jié)點(diǎn)分配一個(gè)token蓖康,范圍一般在0~2^{32},這寫token構(gòu)成一個(gè)哈希環(huán)垒手。數(shù)據(jù)讀寫時(shí)蒜焊,先根據(jù)key計(jì)算hash值,然后順時(shí)針找到第一個(gè)大于等于該哈希值的token節(jié)點(diǎn)科贬。
  3. 虛擬槽分區(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):

  1. 數(shù)據(jù)和節(jié)點(diǎn)解藕雾鬼,簡(jiǎn)化節(jié)點(diǎn)擴(kuò)容和縮減難度。
  2. 不需要客戶端或代理服務(wù)維護(hù)槽元數(shù)據(jù)宴树,自身就能完成策菜。
  3. 支持節(jié)點(diǎn)、槽森渐、鍵之間的映射查詢做入。

8.2 redis cluster 功能限制

相對(duì)單例,cluster上存在以下功能限制:

  1. 只支持相同slot值的key 執(zhí)行批量操作同衣,如mset竟块、mget。
  2. 只支持多key 在同一個(gè)節(jié)點(diǎn)上的事務(wù)操作耐齐。
  3. 不能將大鍵值對(duì)象如hash浪秘、list映射到不同節(jié)點(diǎn)上蒋情。
  4. 不支持多數(shù)據(jù)庫(kù)空間,只有一個(gè)db0 數(shù)據(jù)庫(kù)耸携。單例有db0~db15 共16個(gè)數(shù)據(jù)空間棵癣。
  5. 復(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 通信流程

  1. 集群每個(gè)節(jié)點(diǎn)都會(huì)開辟一個(gè)TCP通道痰憎,用于節(jié)點(diǎn)間通信票髓。通信端口為基礎(chǔ)端口上加10000.
  2. 每個(gè)節(jié)點(diǎn)在固定周期內(nèi)通過特定規(guī)則選擇幾個(gè)節(jié)點(diǎn)發(fā)送ping消息。
  3. 收到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)非常重要。

  1. 選擇發(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)传泊;
  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)奔害。


image.png

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ù)流程如下:

  1. 資格檢查
    如果從節(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。
  2. 準(zhǔn)備選舉時(shí)間
    當(dāng)從節(jié)點(diǎn)擁有資格后喷兼,更新觸發(fā)選舉時(shí)間(failover_auth_time)篮绰,達(dá)到該時(shí)間繼續(xù)后續(xù)流程。
  3. 發(fā)起選舉
    發(fā)起選舉流程:
    1)更新配置紀(jì)元
    2)廣播選舉消息
  4. 選舉投票
    只有主節(jié)點(diǎn)才會(huì)處理故障選舉消息褒搔。


    image.png
  5. 替換主節(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ì):

  1. 主觀下線識(shí)別時(shí)間 = cluster-node-timeout
  2. 主觀下線傳播時(shí)間 <= cluster-node-timeout/2
  3. 從節(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)維難度磕瓷。

  1. 數(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

注:

  1. 只能從單機(jī)轉(zhuǎn)移值集群。
  2. 不支持在線轉(zhuǎn)移肃拜,數(shù)據(jù)提供方應(yīng)先停止讀寫。
  3. 不支持?jǐn)帱c(diǎn)續(xù)傳焰坪。
  4. 單線程進(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):

  1. 《Redis開發(fā)與運(yùn)維》作者: 付磊 張益軍
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末善绎,一起剝皮案震驚了整個(gè)濱河市黔漂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌禀酱,老刑警劉巖瘟仿,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異比勉,居然都是意外死亡劳较,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門浩聋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來观蜗,“玉大人,你說我怎么就攤上這事衣洁∧鼓恚” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)砖第。 經(jīng)常有香客問我撤卢,道長(zhǎng),這世上最難降的妖魔是什么梧兼? 我笑而不...
    開封第一講書人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任放吩,我火速辦了婚禮,結(jié)果婚禮上羽杰,老公的妹妹穿的比我還像新娘渡紫。我一直安慰自己,他們只是感情好考赛,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開白布惕澎。 她就那樣靜靜地躺著,像睡著了一般颜骤。 火紅的嫁衣襯著肌膚如雪唧喉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,727評(píng)論 1 305
  • 那天忍抽,我揣著相機(jī)與錄音欣喧,去河邊找鬼。 笑死梯找,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的益涧。 我是一名探鬼主播锈锤,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼闲询!你這毒婦竟也來了久免?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤扭弧,失蹤者是張志新(化名)和其女友劉穎阎姥,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鸽捻,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡呼巴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了御蒲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片衣赶。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖厚满,靈堂內(nèi)的尸體忽然破棺而出府瞄,到底是詐尸還是另有隱情,我是刑警寧澤碘箍,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布遵馆,位于F島的核電站鲸郊,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏货邓。R本人自食惡果不足惜秆撮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望逻恐。 院中可真熱鬧像吻,春花似錦、人聲如沸复隆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)挽拂。三九已至惭每,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間亏栈,已是汗流浹背台腥。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留绒北,地道東北人黎侈。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像闷游,于是被迫代替她去往敵國(guó)和親峻汉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Redis Cluster Specification 1 設(shè)計(jì)目標(biāo)和理由 1.1 Redis Cluster g...
    近路閱讀 4,250評(píng)論 0 12
  • NOSQL類型簡(jiǎn)介鍵值對(duì):會(huì)使用到一個(gè)哈希表脐往,表中有一個(gè)特定的鍵和一個(gè)指針指向特定的數(shù)據(jù)休吠,如redis,volde...
    MicoCube閱讀 3,985評(píng)論 2 27
  • 關(guān)于Mongodb的全面總結(jié) MongoDB的內(nèi)部構(gòu)造《MongoDB The Definitive Guide》...
    中v中閱讀 31,938評(píng)論 2 89
  • 郭相麟 思想是什么业簿? 它靠物質(zhì)來沉淀 靠精神來升華 只有物質(zhì)顯得浮華 只有精神顯現(xiàn)蒼白 唯有物質(zhì)和精神互為共生 融...
    郭相麟閱讀 218評(píng)論 0 0
  • 春天一到百花開瘤礁, 燕子飛躍來, 敢問枝頭誰(shuí)最美梅尤? 人間善良笑容顏柜思!
    小文化人閱讀 116評(píng)論 0 0