redis要點

Redis

1、簡介

企業緩存產品介紹

  • Tair
# 優勢:
    高性能讀寫、支持三種存儲引擎(ddb、rdb、ldb)、支持高可用、支持分佈式分片集羣、支撐了幾乎全部淘寶業務的緩存。

# 缺點:
    單機狀況下,讀寫性能較其餘兩種產品較慢
  • Memcached
# 優勢:
    高性能讀寫、單一數據類型、支持客戶端式分佈式集羣、一致性hash多核結構、多線程讀寫性能高。
    
# 缺點:
    無持久化、節點故障可能出現緩存穿透、分佈式須要客戶端實現、跨機房數據同步困難、架構擴容複雜度高
  • Redis
# 優勢:
    高性能讀寫、多數據類型支持、數據持久化、高可用架構、支持自定義虛擬內存、支持分佈式分片集羣、單線程讀寫性能極高
  
# 缺點:
    多線程讀寫較Memcached慢。通常是新浪、京東、直播類平臺、網頁遊戲使用
    
# memcache與redis在讀寫性能的對比:
memcached:多核的緩存服務,更加適合於多用戶併發訪問次數較少的應用場景
Redis:單核的緩存服務,單節點狀況下,更加適合於少許用戶,屢次訪問的應用場景。Redis通常是單機多實例架構,配合Redis集羣出現。

Redis功能介紹

  • 高速讀寫
  • 數據類型豐富 (筆試、面試)
  • 支持持久化 (筆試、面試)
  • 多種內存分配及回收策略
  • 支持事務 (面試)
  • 消息隊列、消息訂閱
  • 支持高可用
  • 支持分佈式分片集羣 (面試)
  • 緩存穿透\雪崩(筆試、面試)
  • Redis API

2、Redis安裝

# 下載:
wget http://download.redis.io/releases/redis-3.2.12.tar.gz
# 解壓:
上傳至 /data
tar xzf redis-3.2.12.tar.gz
mv redis-3.2.12 redis
# 安裝:
cd redis
# 編譯
make
# 啓動:
src/redis-server &


# 配置環境變量:
vim /etc/profile 
export PATH=/data/redis/src:$PATH
source /etc/profile 


# 開啓服務端
redis-server & 
# 開啓客戶端
redis-cli 

# 測試
127.0.0.1:6379> set num 10
OK
127.0.0.1:6379> get num
10

3、Redis基本管理操做

1.基礎配置文件介紹

# 配置
mkdir /data/6379

cat >>/data/6379/redis.conf <<EOF
daemonize yes                   # 是否後臺運行
port 6379                       # 默認端口
logfile /data/6379/redis.log    # 日誌文件位置
dir /data/6379                  # 持久化文件存儲位置
dbfilename dump.rdb             # RDB持久化數據文件
EOF

# 重啓redis
# 關閉客戶端鏈接
redis-cli shutdown 
# 以配置文件啓動
redis-server /data/6379/redis.conf 
# 檢測端口
netstat -lnp|grep 63


# 開啓客戶端
redis-cli 

# 測試
127.0.0.1:6379> set name zhangsan 
OK
127.0.0.1:6379> get name
"zhangsan"

2.客戶端命令經常使用參數說明

# redis-cli 剛裝完,能夠在redis服務器上直接登陸redis
-p 6379   指定端口號
-h        指定連接地址
-a        指定連接密碼

# 無交互執行redis命令,不用進入redis也可操做,可是若是由密碼便須要密碼
redis-cli  set num  10

3.遠程鏈接

# redis默認開啓了保護模式,只容許本地迴環地址登陸並訪問數據庫。
protected-mode yes/no (保護模式,是否只容許本地訪問)

# 1.Bind :指定IP進行監聽
echo "bind 10.0.0.200  127.0.0.1" >>/data/6379/redis.conf
# 2.增長requirepass  {password}
echo "requirepass 123" >>/data/6379/redis.conf

# 重啓redis
redis-cli shutdown 
redis-server /data/6379/redis.conf 


# 驗證密碼
# 方法一:
[root@db03 ~]# redis-cli -a 123
127.0.0.1:6379> set name zhangsan 
OK
127.0.0.1:6379> exit
    
# 方法二:
[root@db03 ~]# redis-cli
127.0.0.1:6379> auth 123
OK
127.0.0.1:6379> set a b

    
    
# 在線查看和修改配置
CONFIG GET *
CONFIG GET requirepass
CONFIG SET requirepass 123

四.redis持久化(內存數據保存到磁盤)

1.做用

​ 能夠有效防止,在redis宕機後,緩存失效的問題.python

2.持久化方式

  • RDB
能夠在指定的時間間隔內生成數據集的時間點快照(point-in-time snapshot)。
    
優勢:
    速度快,適合於用作備份,主從複製也是基於RDB持久化功能實現的。
缺點:
    會有數據丟失
  • AOF
記錄服務器執行的全部寫操做命令,並在服務器啓動時,經過從新執行這些命令來還原數據集。 
    AOF 文件中的命令所有以 Redis 協議的格式來保存,新命令會被追加到文件的末尾。
    
優勢:
    能夠最大程度保證數據不丟
缺點:
    日誌記錄量級比較大,會顯得結構臃腫

3.RDB

# rdb持久化核心配置參數:

vim /data/6379/redis.conf

dir /data/6379
dbfilename dump.rdb
save 900 1
save 300 10
save 60 10000

# 配置分別表示:
900秒(15分鐘)內有1個更改
300秒(5分鐘)內有10個更改
60秒內有10000個更改

# 也就是說一分鐘以內10000個變更
# 或者5分鐘10次
# 或15分鐘1次纔會持久化一次
# 這樣就會致使一個問題,數據落地會不及時,有延遲,數據丟失

4.AOF

# AOF持久化配置
vim /data/6379/redis.conf
appendonly yes
appendfsync everysec # evertsec/always/no


# everysec:異步操做,每秒記錄,若是一秒鐘內宕機,有數據丟失
# always:同步持久化,每次發生數據變動會被當即記錄到磁盤,性能差但數據完整性比較好
# no:將緩存回寫的策略交給系統,linux 默認是30秒將緩衝區的數據回寫硬盤的


# AOF的Rewrite(重寫) : 
  定義:AOF採用文件追加的方式持久化數據,因此文件會愈來愈大,爲了不這種狀況發生,增長了重寫機制 
  當AOF文件的大小超過了配置所設置的闕值時,Redis就會啓動AOF文件壓縮,只保留能夠恢復數據的最小指令集,可使用命令bgrewriteaof 
觸發機制:Redis會記錄上次重寫時的AOF文件大小,默認配置時當AOF文件大小是上次rewrite後大小的一倍且文件大於64M時觸發

auto-aof-rewrite-percentage 100 (一倍) 
auto-aof-rewrite-min-size 64mb 





# 面試: 
redis 持久化方式有哪些?有什麼區別?

rdb:基於快照的持久化,速度更快,通常用做備份,主從複製也是依賴於rdb持久化功能
aof:以追加的方式記錄redis操做日誌的文件。能夠最大程度的保證redis數據安全,相似於mysql的binlog

5、Redis數據類型

1.介紹

# string:字符型
鍵      值
key     value

# hash:字典類型
key     col1 value1  col2 value2 

# list:列表
key     [a,b,c,d]  
         0 1 2 3
    
# set:集合
key1    (a,b,c,d) 

# sorted set:有序集合
          10     20   30  40 
key1     ( a,    b,   c,  d)  
           0     1    2   3

2.通用操做

# 查看
# 查全部,數據量大時通常不推薦使用
KEYS *   
# 查單個 
keys 健名  
# 模糊查詢
keys a* 

# 返回鍵所存儲值的類型 
TYPE

# 檢查是否存在   
EXISTS

# 以秒\毫秒設定生存時間  
EXPIRE\ PEXPIRE

# 以秒\毫秒爲單位返回生存時間
TTL\ PTTL

# 取消生存實現設置 
PERSIST

# 刪除一個key
DEL     

# 變動KEY名
RENAME

3.string

# 應用場景
常規計數:
微博數,粉絲數等。
訂閱、禮物、頁遊
key:value
----------
#(1)
set name zhangsan   
#(2)
MSET id 101 name zhangsan age 20 gender m
# 等價於如下操做:
SET id 101 
set name zhangsan 
set age 20 
set gender m
 
mget id name age gender

#(3)計數器
每點一次關注,都執行如下命令一次
127.0.0.1:6379> incr fans_count
(integer) 10003
127.0.0.1:6379> get fans_count
"10003"
127.0.0.1:6379> incrby fans_count 1000
(integer) 11003
127.0.0.1:6379> decr fans_count 
(integer) 11002
127.0.0.1:6379> decrby  fans_count 1000

# 詳細的例子
# 增
set    mykey "test"             # 爲鍵設置新值,並覆蓋原有值
getset mycounter 0              # 設置值,取值同時進行
setex  mykey 10 "hello"         # 設置指定 Key 的過時時間爲10秒,在存活時間能夠獲取value
setnx mykey "hello"             # 若該鍵不存在,則爲鍵設置新值
mset key3  "zyx"  key4 "xyz"    # 批量設置鍵
---------------------------


# 刪
del mykey                       # 刪除已有鍵
---------------------------


# 改
append mykey "hello"            # 若該鍵並不存在,返回當前 Value 的長度;該鍵已經存在,返回追加後 Value的長度
incr mykey                      # 值增長1,若該key不存在,建立key,初始值設爲0,增長後結果爲1
decrby  mykey  5                # 值減小5
setrange mykey 20 dd            # 把第21和22個字節,替換爲dd, 超過value長度,自動補0
---------------------------


# 查  
exists mykey                    # 判斷該鍵是否存在,存在返回 1,不然返回0
get mykey                       # 獲取Key對應的value
strlen mykey                    # 獲取指定 Key 的字符長度
ttl mykey                       # 查看一下指定 Key 的剩餘存活時間(秒數)
getrange mykey 1 20             # 獲取第2到第20個字節,若20超過value長度,則截取第2個和後面全部的
mget key3 key4                  # 批量獲取鍵

4.hash

# 應用場景:
存儲部分變動的數據,如用戶信息等。
最接近mysql表結構的一種類型

--------------
# 存數據:
hmset stu id 101 name zs age 18

id    name     age
101   zs      18
# 同等與MySQL的
insert into stu(id,name,age) values (101,'zs',18);

# 取數據:
HMGET stu id name age     -----> select id,name,age from stu;
hgetall stu               ------> select * from stu;

# 將mysql中world.city表前10行數據導入redis

select concat("hmset city_",id," id ",id," name ",name," countrycode ",countrycode," district ",district," population ",population) from world.city  into outfile '/tmp/hmset1.txt'

cat /tmp/hmset.txt |redis-cli -a 123

hmset city_1  id 1 name Kabul contrycode AFG district Kabol population  1780000 


# 例子
# 增
hset myhash field1 "s"     # 若字段field1不存在,建立該鍵及與其關聯的Hashes, Hashes中,key爲field1 ,並設value爲s ,若存在會覆蓋原value
hsetnx myhash field1 s     # 若字段field1不存在,建立該鍵及與其關聯的Hashes, Hashes中,key爲field1 ,並設value爲s, 若字段field1存在,則無效
hmset myhash field1 "hello" field2 "world"  # 一次性設置多個字段
---------------------------


# 刪
hdel myhash field1                      # 刪除 myhash 鍵中字段名爲 field1 的字段
del myhash                              # 刪除鍵
---------------------------


# 改  
hincrby myhash field 1                  # 給field的值加1
---------------------------


# 查
hget myhash field1              # 獲取鍵值爲 myhash,字段field1 的值
hlen myhash                     # 獲取myhash鍵的字段數量
hexists myhash field1           # 判斷 myhash 鍵中是否存在字段名爲 field1 的字段
hmget myhash field1 field2 field3       # 一次性獲取多個字段
hgetall myhash                  # 返回 myhash 鍵的全部字段及其值
hkeys myhash                    # 獲取myhash 鍵中全部字段的名字
hvals myhash                    # 獲取 myhash 鍵中全部字段的值

5.list

# 應用場景

消息隊列系統
好比sina微博:在Redis中咱們的最新微博ID使用了常駐緩存,這是一直更新的。
可是作了限制不能超過5000個ID,所以獲取ID的函數會一直詢問Redis。
只有在start/count參數超出了這個範圍的時候,才須要去訪問數據庫。
系統不會像傳統方式那樣「刷新」緩存,Redis實例中的信息永遠是一致的。
SQL數據庫(或是硬盤上的其餘類型數據庫)只是在用戶須要獲取「很遠」的數據時纔會被觸發,
而主頁或第一個評論頁是不會麻煩到硬盤上的數據庫了。


# 微信朋友圈:

127.0.0.1:6379> LPUSH wechat "today is nice day !"

127.0.0.1:6379> LPUSH wechat "today is bad day !"

127.0.0.1:6379> LPUSH wechat "today is good  day !"

127.0.0.1:6379> LPUSH wechat "today is rainy  day !"

127.0.0.1:6379> LPUSH wechat "today is friday !"

[e,d,c,b,a]
 0 1 2 3  4

127.0.0.1:6379> lrange wechat  0 0
"today is friday !"
127.0.0.1:6379> lrange wechat  0 1
"today is friday !"
"today is rainy  day !"
127.0.0.1:6379> lrange wechat  0 2
"today is friday !"
"today is rainy  day !"
"today is good  day !"
127.0.0.1:6379> lrange wechat  0 3
127.0.0.1:6379> lrange wechat  -2 -1
"today is bad day !"
"today is nice day !"


-----------------
# 增 
lpush mykey a b             # 若key不存在,建立該鍵及與其關聯的List,依次插入a ,b, 若List類型的key存在,則插入value中
lpushx mykey2 e             # 若key不存在,此命令無效, 若key存在,則插入value中
linsert mykey before a a1   # 在 a 的前面插入新元素 a1
linsert mykey after e e2    # 在e 的後面插入新元素 e2
rpush mykey a b             # 在鏈表尾部先插入b,在插入a
rpushx mykey e              # 若key存在,在尾部插入e, 若key不存在,則無效
rpoplpush mykey mykey2      # 將mykey的尾部元素彈出,再插入到mykey2 的頭部(原子性的操做)
---------------------------


# 刪
del mykey                   # 刪除已有鍵 
lrem mykey 2 a              # 從頭部開始找,按前後順序,值爲a的元素,刪除數量爲2個,若存在第3個,則不刪除
ltrim mykey 0 2             # 從頭開始,索引爲0,1,2的3個元素,其他所有刪除
---------------------------


# 改
lset mykey 1 e              # 從頭開始, 將索引爲1的元素值,設置爲新值 e,若索引越界,則返回錯誤信息
rpoplpush mykey mykey       # 將 mykey 中的尾部元素移到其頭部
---------------------------


# 查
lrange mykey 0 -1           # 取鏈表中的所有元素,其中0表示第一個元素,-1表示最後一個元素。
lrange mykey 0 2            # 從頭開始,取索引爲0,1,2的元素
lrange mykey 0 0            # 從頭開始,取第一個元素,從第0個開始,到第0個結束
lpop mykey                  # 獲取頭部元素,而且彈出頭部元素,出棧
lindex mykey 6              # 從頭開始,獲取索引爲6的元素 若下標越界,則返回nil 

------------

6.set

# 應用場景:
案例:在微博應用中,能夠將一個用戶全部的關注人存在一個集合中,將其全部粉絲存在一個集合。
Redis還爲集合提供了求交集、並集、差集等操做,能夠很是方便的實現如共同關注、共同喜愛、二度好友等功能,
對上面的全部集合操做,你還可使用不一樣的命令選擇將結果返回給客戶端仍是存集到一個新的集合中。

-------------
# lxl的好友
127.0.0.1:6379> sadd lxl pg1 pg2 songlaoban oldnie oldchen marong
(integer) 6

# jnl的好友
127.0.0.1:6379> sadd jnl baoqiang yufan oldchen songzhe oldguo  alexdsb 
(integer) 2

# lxl jnl全部的好友
127.0.0.1:6379> SUNION lxl jnl
 1) "marong"
 2) "pg2"
 3) "pg1"
 4) "oldchen"
 5) "alexdsb"
 6) "yufan"
 7) "songlaoban"
 8) "baoqiang"
 9) "oldnie"
10) "songzhe"
11) "oldguo"

# 共同的好友
127.0.0.1:6379> SINTER lxl jnl
1) "oldchen"


#  lxl的但不是jnl的好友
127.0.0.1:6379> SDIFF lxl jnl
1) "songlaoban"
2) "oldnie"
3) "pg1"
4) "pg2"
5) "marong"
# jnl的但不是lxl的好友
127.0.0.1:6379> SDIFF  jnl lxl 
1) "alexdsb"
2) "yufan"
3) "songzhe"
4) "oldguo"
5) "baoqiang"
-----------------
# 增
sadd myset a b c            # 若key不存在,建立該鍵及與其關聯的set,依次插入a ,b,若key存在,則插入value中,若a 在myset中已經存在,則插入了 d 和 e 兩個新成員。
---------------------------


# 刪
spop myset                  # 尾部的b被移出,事實上b並非以前插入的第一個或最後一個成員
srem myset a d f            # 若f不存在, 移出 a、d ,並返回2
---------------------------


# 改
smove myset myset2 a        # 將a從 myset 移到 myset2,
---------------------------


# 查
sismember myset a           # 判斷 a 是否已經存在,返回值爲 1 表示存在。
smembers myset              # 查看set中的內容
scard myset                 # 獲取Set 集合中元素的數量
srandmember myset           # 隨機的返回某一成員
sdiff myset1 myset2 myset3  # 1和2獲得一個結果,拿這個集合和3比較,得到每一個獨有的值
sdiffstore diffkey myset myset2 myset3    # 3個集和比較,獲取獨有的元素,並存入diffkey 關聯的Set中
sinter myset myset2 myset3  # 得到3個集合中都有的元素
sinterstore interkey myset myset2 myset3  # 把交集存入interkey 關聯的Set中
sunion myset myset2 myset3  # 獲取3個集合中的成員的並集
sunionstore unionkey myset myset2 myset3  # 把並集存入unionkey 關聯的Set中

7.SortedSet

# 應用場景:
排行榜應用,取TOP N操做

這個需求與上面需求的不一樣之處在於,前面操做以時間爲權重,這個是以某個條件爲權重,好比按頂的次數排序,
這時候就須要咱們的sorted set出馬了,將你要排序的值設置成sorted set的score,將具體的數據設置成相應的value,
每次只須要執行一條ZADD命令便可。

--------------
127.0.0.1:6379> zadd topN 0 smlt 0 fskl 0 fshkl 0 lzlsfs 0 wdhbx 0 wxg 
(integer) 6
127.0.0.1:6379> ZINCRBY topN 100000 smlt
"100000"
127.0.0.1:6379> ZINCRBY topN 10000 fskl
"10000"
127.0.0.1:6379> ZINCRBY topN 1000000 fshkl
"1000000"
127.0.0.1:6379> ZINCRBY topN 100 lzlsfs
"100"
127.0.0.1:6379> ZINCRBY topN 10 wdhbx
"10"
127.0.0.1:6379> ZINCRBY topN 100000000 wxg
"100000000"

127.0.0.1:6379> ZREVRANGE topN 0 2 
1) "wxg"
2) "fshkl"
3) "smlt"
127.0.0.1:6379> ZREVRANGE topN 0 2 withscores
1) "wxg"
2) "100000000"
3) "fshkl"
4) "1000000"
5) "smlt"
6) "100000"
127.0.0.1:6379> 

--------------
# 增
zadd myzset 2 "two" 3 "three"       # 添加兩個分數分別是 2 和 3 的兩個成員
---------------------------


# 刪
zrem myzset one two                 # 刪除多個成員變量,返回刪除的數量
---------------------------


# 改
zincrby myzset 2 one                # 將成員 one 的分數增長 2,並返回該成員更新後的分數
---------------------------


# 查 
zrange myzset 0 -1 WITHSCORES       # 返回全部成員和分數,不加WITHSCORES,只返回成員
zrank myzset one                    # 獲取成員one在Sorted-Set中的位置索引值。0表示第一個位置
zcard myzset                        # 獲取 myzset 鍵中成員的數量
zcount myzset 1 2                   # 獲取分數知足表達式 1 <= score <= 2 的成員的數量
zscore myzset three                 # 獲取成員 three 的分數
zrangebyscore myzset  1 2           # 獲取分數知足表達式 1 < score <= 2 的成員


#-inf 表示第一個成員,+inf最後一個成員
#limit限制關鍵字
#2  3  是索引號
zrangebyscore myzset -inf +inf limit 2 3  # 返回索引是2和3的成員
zremrangebyscore myzset 1 2         # 刪除分數 1<= score <= 2 的成員,並返回實際刪除的數量
zremrangebyrank myzset 0 1              # 刪除位置索引知足表達式 0 <= rank <= 1 的成員
zrevrange myzset 0 -1 WITHSCORES        # 按位置索引從高到低,獲取全部成員和分數
#原始成員:位置索引從小到大
      one  0  
      two  1
#執行順序:把索引反轉
      位置索引:從大到小
      one 1
      two 0
#輸出結果: two  
      one
zrevrange myzset 1 3                # 獲取位置索引,爲1,2,3的成員
#相反的順序:從高到低的順序
zrevrangebyscore myzset 3 0             # 獲取分數 3>=score>=0的成員並以相反的順序輸出
zrevrangebyscore myzset 4 0 limit 1 2   # 獲取索引是1和2的成員,並反轉位置索引

6、redis發佈消息

1.介紹

Redis發佈消息一般有兩種模式:mysql

  • 隊列模式(queuing)linux

    任務隊列的好處:面試

    ​ 鬆耦合。redis

    ​ 易於擴展。算法

  • 發佈-訂閱模式(publish-subscribe)sql

其實從Pub/Sub的機制來看,它更像是一個廣播系統,多個Subscriber能夠訂閱多個Channel,多個Publisher能夠往多個Channel中發佈消息。能夠這麼簡單的理解:

    Subscriber:收音機,能夠收到多個頻道,並以隊列方式顯示

    Publisher:電臺,能夠往不一樣的FM頻道中發消息

    Channel:不一樣頻率的FM頻道
    
    
客戶端在執行訂閱命令以後進入了訂閱狀態,只能接收 SUBSCRIBE 、PSUBSCRIBE、 UNSUBSCRIBE 、PUNSUBSCRIBE 四個命令。 開啓的訂閱客戶端,沒法收到該頻道以前的消息,由於 Redis 不會對發佈的消息進行持久化。 和不少專業的消息隊列系統(例如Kafka、RocketMQ)相比,Redis的發佈訂閱略顯粗糙,例如沒法實現消息堆積和回溯。但勝在足夠簡單,若是當前場景能夠容忍的這些缺點,也不失爲一個不錯的選擇。

2.發佈訂閱

# 將信息 message 發送到指定的頻道 channel
PUBLISH channel msg

# 訂閱頻道,能夠同時訂閱多個頻道
SUBSCRIBE channel [channel ...]
    
# 取消訂閱指定的頻道, 若是不指定頻道,則會取消訂閱全部頻道
UNSUBSCRIBE [channel ...]
    
# 訂閱一個或多個符合給定模式的頻道,每一個模式以 * 做爲匹配符,好比 it* 匹配所 有以 it 開頭的頻道( it.news 、 it.blog 、 it.tweets 等等), news.* 匹配全部 以 news. 開頭的頻道( news.it 、 news.global.today 等等),諸如此類
PSUBSCRIBE pattern [pattern ...]
    
# 退訂指定的規則, 若是沒有參數則會退訂全部規則
PUNSUBSCRIBE [pattern [pattern ...]]

# 查看訂閱與發佈系統狀態
PUBSUB subcommand [argument [argument ...]]
    
# 注意:使用發佈訂閱模式實現的消息隊列,當有客戶端訂閱channel後只能收到後續發佈到該頻道的消息,以前發送的不會緩存,必須Provider和Consumer同時在線。
# 發佈訂閱例子:

窗口1:
# 先要訂閱頻道baodi
127.0.0.1:6379> SUBSCRIBE baodi 

窗口2:
# 頻道發佈信息,訂閱者會立馬收到
127.0.0.1:6379> PUBLISH baodi "jin tian zhen kaixin!"

訂閱多頻道:
窗口1:
127.0.0.1:6379> PSUBSCRIBE wang*
窗口2:
127.0.0.1:6379> PUBLISH wangbaoqiang "jintian zhennanshou "

7、redis事務

redis的事務是基於隊列實現的。
mysql的事務是基於事務日誌實現的。

----------
# 開啓事務功能時(multi)
multi 
command1      
command2
command3
command4

# 4條語句做爲一個組,並無真正執行,而是被放入同一隊列中。
若是,這是執行discard,會直接丟棄隊列中全部的命令,而不是作回滾。

exec
# 當執行exec時,對列中全部操做,要麼全成功要麼全失敗


----------
127.0.0.1:6379> set a b
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set a b
QUEUED
127.0.0.1:6379> set c d
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK

8、redis主從複製

1.簡介

持久化保證了即便redis服務重啓也不會丟失數據,由於redis服務重啓後會將硬盤上持久化的數據恢復到內存中,可是當redis服務器的硬盤損壞了可能會致使數據丟失,若是經過redis的主從複製機制就能夠避免這種單點故障數據庫

2.原理

  1. 從服務器向主服務器發送 SYNC 命令。
  2. 接到 SYNC 命令的主服務器會調用BGSAVE 命令,建立一個 RDB 文件,並使用緩衝區記錄接下來執行的全部寫命令。
  3. 當主服務器執行完 BGSAVE 命令時,它會向從服務器發送 RDB 文件,而從服務器則會接收並載入這個文件。
  4. 主服務器將緩衝區儲存的全部寫命令(廣播形式)發送給從服務器執行。

主服務器會一直持續第四步,只要有新的命令,主服務器會監視vim

3.主從的持久化

當配置Redis複製功能時,強烈建議打開主服務器的持久化功能。 不然的話,因爲延遲等問題,部署的服務應該要避免自動拉起。

爲了幫助理解主服務器關閉持久化時自動拉起的危險性,參考一下如下會致使主從服務器數據所有丟失的例子:

  1. 假設節點A爲主服務器,而且關閉了持久化。 而且節點B和節點C從節點A複製數據
  2. 節點A崩潰,而後由自動拉起服務重啓了節點A. 因爲節點A的持久化被關閉了,因此重啓以後沒有任何數據
  3. 節點B和節點C將從節點A複製數據,可是A的數據是空的, 因而就把自身保存的數據副本刪除。

在關閉主服務器上的持久化,並同時開啓自動拉起進程的狀況下,即使使用Sentinel來實現Redis的高可用性,也是很是危險的。 由於主服務器可能拉起得很是快,以致於Sentinel在配置的心跳時間間隔內沒有檢測到主服務器已被重啓,而後仍是會執行上面的數據丟失的流程。

不管什麼時候,數據安全都是極其重要的,因此應該禁止主服務器關閉持久化的同時自動拉起。

# 給主服務器加上持久化
appendonly yes
appendfsync everysec

4.複製的一致性

  1. 在讀寫分離環境下,客戶端向主服務器發送寫命令 SET n 10086,主服務器在執行這個寫命令以後,向客戶端返回回覆,並將這個寫命令傳播給從服務器。
  2. 接到回覆的客戶端繼續向從服務器發送讀命令 GET n ,而且由於網絡狀態的緣由,客戶端的 GET命令比主服務器傳播的 SET 命令更快到達了從服務器。
  3. 由於從服務器鍵 n 的值還未被更新,因此客戶端在從服務器讀取到的將是一個錯誤(過時)的 n值。

4.1解決方案

# 主從數據一致性保證:
# 在配置文件中配置
min-slaves-to-write 1   # 從服務器的數量
min-slaves-max-lag  2   # 網絡延遲的最大秒數


這個特性的運做原理:
從服務器以每秒一次的頻率 PING 主服務器一次, 並報告複製流的處理狀況。
主服務器會記錄各個從服務器最後一次向它發送 PING 的時間。

用戶能夠經過配置, 指定網絡延遲的最大值 min-slaves-max-lag ,

以及執行寫操做所需的至少從服務器數量 min-slaves-to-write 。

若是至少有 min-slaves-to-write 個從服務器, 而且這些服務器的延遲值都少於 min-slaves-max-lag秒,

那麼主服務器就會執行客戶端請求的寫操做。

你能夠將這個特性看做 CAP 理論中的 C 的條件放寬版本: 儘管不能保證寫操做的持久性, 
但起碼丟失數據的窗口會被嚴格限制在指定的秒數中。

另外一方面, 若是條件達不到 min-slaves-to-write 和 min-slaves-max-lag 所指定的條件, 那麼寫操做就不會被執行
主服務器會向請求執行寫操做的客戶端返回一個錯誤。

5.主從複製實現

# 一、環境:
# 準備兩個或兩個以上redis實例

mkdir /data/638{0..2}

# 配置文件示例:
cat >> /data/6380/redis.conf << EOF 
port 6380
daemonize yes
pidfile /data/6380/redis.pid
loglevel notice
logfile "/data/6380/redis.log"
dbfilename dump.rdb
dir /data/6380
requirepass 123
masterauth 123
appendonly yes
appendfsync everysec
EOF

cp /data/6380/redis.conf /data/6381/redis.conf
cp /data/6380/redis.conf /data/6382/redis.conf

sed -i 's#6380#6381#g' /data/6381/redis.conf
sed -i 's#6380#6382#g' /data/6382/redis.conf


# 啓動:
redis-server /data/6380/redis.conf
redis-server /data/6381/redis.conf
redis-server /data/6382/redis.conf

# 查看端口
netstat -lnp|grep 638
 
 
主節點:6380
從節點:638一、6382

# 二、開啓主從:
6381/6382命令行:

redis-cli -p 6381 -a 123 SLAVEOF 127.0.0.1 6380
redis-cli -p 6382 -a 123 SLAVEOF 127.0.0.1 6380


# 三、查詢主從狀態
redis-cli -p 6380 -a 123 info replication
redis-cli -p 6381 -a 123 info replication
redis-cli -p 6382 -a 123 info replication


# 模擬主庫故障
redis-cli -p 6380 -a 123 shutdown

# 1.從庫切爲主庫
redis-cli -p 6381 -a 123
info replication
slaveof no one

# 2.6382鏈接到6381:
redis-cli -p 6382 -a 123
127.0.0.1:6382> SLAVEOF no one
127.0.0.1:6382> SLAVEOF 127.0.0.1 6381

6.redis-sentinel(哨兵)

6.1 功能

  1. 監控
  2. 自動選主,切換(6381 slaveof no one)
  3. 2號從庫(6382)指向新主庫(6381)
  4. 應用透明

當主服務器掛掉時,能夠快速切換主從,儘快恢復服務

6.2 sentinel搭建過程

# 1.建立哨兵文件夾
mkdir /data/26380
# 2.配置哨兵
cat >> /data/26380/sentinel.conf << EOF
port 26380
dir "/data/26380"
sentinel monitor mymaster 127.0.0.1 6380 1
sentinel down-after-milliseconds mymaster 5000
sentinel auth-pass mymaster 123 
EOF
# 3.啓動:
redis-sentinel /data/26380/sentinel.conf &


# 配置詳解

# 格式:sentinel <option_name> <master_name> <option_value>#該行的意思是:監控的master的名字叫作mymaster(自定義),地址爲127.0.0.1:6380,行尾最後的一個1表明在sentinel集羣中,多少個sentinel認爲masters死了,才能真正認爲該master不可用了。
sentinel monitor mymaster 127.0.0.1 6380 1


#sentinel會向master發送心跳PING來確認master是否存活,若是master在「必定時間範圍」內不迴應PONG 或者是回覆了一個錯誤消息,那麼這個sentinel會主觀地(單方面地)認爲這個master已經不可用了(subjectively down, 也簡稱爲SDOWN)。而這個down-after-milliseconds就是用來指定這個「必定時間範圍」的,單位是毫秒,默認30秒。
sentinel down-after-milliseconds mymaster 5000

:此時一個哨兵就正式開啓了,每次就經過哨兵訪問便可

​ redis-cli -p 26380

​ 哨兵會自動指向主庫,就算主庫掛了,哨兵也會在內部根據實際狀況選擇一個主庫,讓剩下的從庫指向新的主庫,並且掛了的主庫也會從新啓動變成從庫

6.3 Sentinel管理命令

PING :返回 PONG 。
SENTINEL masters :# 列出全部被監視的主服務器
SENTINEL slaves <master name>: # 列出全部被監視的從服務器
SENTINEL get-master-addr-by-name <master name> : # 返回給定名字的主服務器的 IP 地址和端口號。 
SENTINEL reset <pattern> : # 重置全部名字和給定模式 pattern 相匹配的主服務器。 
SENTINEL failover <master name> : # 當主服務器失效時, 在不詢問其餘 Sentinel 意見的狀況下, 強制開始一次自動故障遷移。

9、redis集羣

1.基本介紹

Redis集羣是一個能夠在多個 Redis 節點之間進行數據共享的設施installation

Redis 集羣不支持那些須要同時處理多個鍵的 Redis 命令, 由於執行這些命令須要在多個 Redis 節點之間移動數據, 而且在高負載的狀況下, 這些命令將下降Redis集羣的性能, 並致使不可預測的行爲。

Redis 集羣經過分區partition來提供必定程度的可用性availability: 即便集羣中有一部分節點失效或者沒法進行通信, 集羣也能夠繼續處理命令請求。

Redis集羣提供瞭如下兩個好處:

  • 將數據自動切分split到多個節點的能力。
  • 當集羣中的一部分節點失效或者沒法進行通信時, 仍然能夠繼續處理命令請求的能力。

2.Redis Cluster集羣

Redis集羣搭建的方式有多種,例如使用zookeeper等,但從redis 3.0以後版本支持redis-cluster集羣,Redis-Cluster採用無中心結構,每一個節點保存數據和整個集羣狀態,每一個節點都和其餘全部 節點鏈接。

特色:

  1. 全部的redis節點彼此互聯(PING-PONG機制),內部使用二進制協議優化傳輸速度和帶寬。
  2. 節點的fail是經過集羣中超過半數的節點檢測失效時才生效。
  3. 客戶端與redis節點直連,不須要中間proxy層.客戶端不須要鏈接集羣全部節點,鏈接集羣中任何一個可用節點便可。
  4. redis-cluster把全部的物理節點映射到[0-16383]slot上(不必定是平均分配),cluster 負責維護node<->slot<->value。
  5. Redis集羣預分好16384個桶,當須要在 Redis 集羣中放置一個 key-value 時,根據 CRC16(key) mod 16384的值,決定將一個key放到哪一個桶中。

3. Redis Cluster節點分配

如今咱們是三個主節點分別是:A, B, C 三個節點,它們能夠是一臺機器上的三個端口,也能夠是三臺不一樣的服務器。那麼,採用哈希槽 (hash slot)的方式來分配16384個slot 的話,它們三個節點分別承擔的slot 區間是:

節點A覆蓋0-5460;
節點B覆蓋5461-10922;
節點C覆蓋10923-16383.

獲取數據:

​ 若是存入一個值'data'='1024-4-12',按照redis cluster哈希槽的算法: CRC16('data')%16384 = 2022。 那麼就會把這個key 的存儲分配到 A 上了。一樣,當我鏈接(A,B,C)任何一個節點想獲取'key'這個key時,也會這樣的算法,而後內部跳轉到A節點上獲取數據

新增一個主節點:

新增一個節點D,redis cluster的這種作法是從各個節點的前面各拿取一部分slot到D上,我會在接下來的實踐中實驗。大體就會變成這樣:
  

節點A覆蓋1365-5460
節點B覆蓋6827-10922
節點C覆蓋12288-16383
節點D覆蓋0-1364,5461-6826,10923-12287

4.Redis Cluster主從模式

​ redis cluster 爲了保證數據的高可用性,加入了主從模式,一個主節點對應一個或多個從節點,主節點提供數據存取,從節點則是從主節點拉取數據備份,當這個主節點掛掉後,就會有這個從節點選取一個來充當主節點,從而保證集羣不會掛掉。redis集羣主從

​ 上面那個例子裏, 集羣有ABC三個主節點, 若是這3個節點都沒有加入從節點,若是B掛掉了,咱們就沒法訪問整個集羣了。A和C的slot也沒法訪問。

​ 因此咱們在集羣創建的時候,必定要爲每一個主節點都添加了從節點, 好比像這樣, 集羣包含主節點A、B、C, 以及從節點A一、B一、C1, 那麼即便B掛掉系統也能夠繼續正確工做。

​ B1節點替代了B節點,因此Redis集羣將會選擇B1節點做爲新的主節點,集羣將會繼續正確地提供服務。 當B從新開啓後,它就會變成B1的從節點。

​ 不過須要注意,若是節點B和B1同時掛了,Redis集羣就沒法繼續正確地提供服務了。

5.redis集羣的搭建

# 6個redis實例,通常會放到3臺硬件服務器
# 注:在企業規劃中,一個分片的兩個分到不一樣的物理機,防止硬件主機宕機形成的整個分片數據丟失。

# 端口號:7000-7005

# 一、安裝集羣插件
EPEL源安裝ruby支持
yum install ruby rubygems -y

# 可使用國內源
gem sources -l
gem sources -a http://mirrors.aliyun.com/rubygems/ 
gem sources  --remove https://rubygems.org/
gem sources -l
gem install redis -v 3.3.3

# 2.集羣節點準備

mkdir /data/700{0..5}

cat >> /data/7000/redis.conf << EOF
port 7000
daemonize yes
pidfile /data/7000/redis.pid
loglevel notice
logfile "/data/7000/redis.log"
dbfilename dump.rdb
dir /data/7000
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOF

cp /data/7000/redis.conf /data/7001/redis.conf
cp /data/7000/redis.conf /data/7002/redis.conf
cp /data/7000/redis.conf /data/7003/redis.conf
cp /data/7000/redis.conf /data/7004/redis.conf
cp /data/7000/redis.conf /data/7005/redis.conf

sed -i 's#7000#7001#g' /data/7001/redis.conf
sed -i 's#7000#7002#g' /data/7002/redis.conf
sed -i 's#7000#7003#g' /data/7003/redis.conf
sed -i 's#7000#7004#g' /data/7004/redis.conf
sed -i 's#7000#7005#g' /data/7005/redis.conf

# 啓動節點:

redis-server /data/7000/redis.conf 
redis-server /data/7001/redis.conf 
redis-server /data/7002/redis.conf 
redis-server /data/7003/redis.conf 
redis-server /data/7004/redis.conf 
redis-server /data/7005/redis.conf 


# 查看端口是否啓動
ps -ef |grep redis
root       8854      1  0 03:56 ?        00:00:00 redis-server *:7000 [cluster]     
root       8858      1  0 03:56 ?        00:00:00 redis-server *:7001 [cluster]     
root       8860      1  0 03:56 ?        00:00:00 redis-server *:7002 [cluster]     
root       8864      1  0 03:56 ?        00:00:00 redis-server *:7003 [cluster]     
root       8866      1  0 03:56 ?        00:00:00 redis-server *:7004 [cluster]     
root       8874      1  0 03:56 ?        00:00:00 redis-server *:7005 [cluster]  
                
                
                
# 3.將節點加入集羣管理

redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005
                        
                        
# 4.集羣狀態查看

# 集羣主節點狀態
redis-cli -p 7000 cluster nodes | grep master
# 集羣從節點狀態
redis-cli -p 7000 cluster nodes | grep slave




# 五、集羣節點管理
# 增長新的節點7006/7007
# 1.添加主節點:
# add-node是加入集羣節點
# 127.0.0.1:7006爲要加入的節點
# 127.0.0.1:7000 表示加入的集羣的一個節點,用來辨識是哪一個集羣,理論上那個集羣的節點均可以。
redis-trib.rb add-node 127.0.0.1:7006 127.0.0.1:7000
        
# 2.轉移slot(從新分片) 
# redis-cluster在新增節點時並未分配卡槽,須要咱們手動對集羣進行從新分片遷移數據
redis-trib.rb reshard 127.0.0.1:7000
How many slots do you want to move (from 1 to 16384)? 4096
# 它提示咱們須要遷移多少slot到7006上,咱們平分16384個哈希槽給4個節點:16384/4 = 4096,咱們須要移動4096個槽點到7006上。
What is the receiving node ID? 1c98b2b2ce18f88c76821cdb82dba4defaa5eb48
# 須要輸入7006的節點id
Source node #1:all  
#  若是咱們不打算從特定的節點上取出指定數量的哈希槽,那麼能夠向redis-trib輸入 all,這樣的話, 集羣中的全部主節點都會成爲源節點,redis-trib從各個源節點中各取出一部分哈希槽,湊夠4096個,而後移動到7006節點上


# 添加一個從節點
# 1c98b2b2ce18f88c76821cdb82dba4defaa5eb48爲要加到master主節點的node id,127.0.0.1:7007爲新增的從節點,127.0.0.1:7000爲集羣的一個節點(集羣的任意節點都行),用來辨識是哪一個集羣;若是沒有給定那個主節點--master-id的話,redis-trib將會將新增的從節點隨機到從節點較少的主節點上。
redis-trib.rb add-node --slave --master-id 1c98b2b2ce18f88c76821cdb82dba4defaa5eb48 127.0.0.1:7007 127.0.0.1:7000


# 6.刪除節點

# 將須要刪除節點slot移動走
redis-trib.rb reshard 127.0.0.1:7000


# 刪除一個節點
# 刪除master節點以前首先要使用reshard移除master的所有slot,而後再刪除當前節點
redis-trib.rb del-node 127.0.0.1:7006 1c98b2b2ce18f88c76821cdb82dba4defaa5eb48
redis-trib.rb del-node 127.0.0.1:7007 00185d1cf069b23468d5863202ac651f0d02a9f8
---------------------
# 設置redis最大內存
config set maxmemory 102400000
---------------------

10、python實現redis多種API

一、對redis的單實例進行鏈接操做

# redis單例提供了一種數據緩存方式和豐富的數據操做api,可是將數據徹底存儲在單個redis中主要存在兩個問題:數據備份和數據體量較大形成的性能下降。這裏redis的主從模式爲這兩個問題提供了一個較好的解決方案。

# 安裝
unzip redis-py-master.zip
cd redis-py-master
python3 setup.py install

# 使用
>>>import redis
>>>r = redis.StrictRedis(host='10.0.0.200', port=6379, db=0,password='123')
>>>r.set('foo', 'bar')
True
>>>r.get('foo')
'bar'

2.python使用redis的哨兵sentinel

# redis主從模式解決了數據備份和單例可能存在的性能問題,可是其也引入了新的問題。
# 因爲主從模式配置了三個redis實例,而且每一個實例都使用不一樣的ip(若是在不一樣的機器上)和端口號。
# 根據前面所述,主從模式下能夠將讀寫操做分配給不一樣的實例進行從而達到提升系統吞吐量的目的,但也正是由於這種方式形成了使用上的不便,由於每一個客戶端鏈接redis實例的時候都是指定了ip和端口號的,若是所鏈接的redis實例由於故障下線了,而主從模式也沒有提供必定的手段通知客戶端另外可鏈接的客戶端地址,於是須要手動更改客戶端配置從新鏈接。
# 另外,主從模式下,若是主節點因爲故障下線了,那麼從節點由於沒有主節點而同步中斷,於是須要人工進行故障轉移工做。
# 爲了解決這兩個問題,在2.8版本以後redis正式提供了sentinel(哨兵)架構。

# 1.啓動redis的實例以及sentinel
redis-server /data/6380/redis.conf
redis-server /data/6381/redis.conf
redis-server /data/6382/redis.conf 
redis-sentinel /data/26380/sentinel.conf &


# 使用
# 2.導入redis sentinel包
>>>from redis.sentinel import Sentinel  
# 指定sentinel的地址和端口號
>>> sentinel = Sentinel([('localhost', 26380)], socket_timeout=0.1)  
# 測試,獲取如下主庫和從庫的信息
>>> sentinel.discover_master('mymaster')  
>>> sentinel.discover_slaves('mymaster')  

# 配置讀寫分離
# 寫節點
>>> master = sentinel.master_for('mymaster', socket_timeout=0.1,password="123")  
# 讀節點
>>> slave = sentinel.slave_for('mymaster', socket_timeout=0.1,password="123")  
# 讀寫分離測試   key     
>>> master.set('oldboy', '123')  
>>> slave.get('oldboy')  
'123'

2.redis cluster集羣的鏈接並操做

# redis集羣是在redis 3.0版本推出的一個功能,其有效的解決了redis在分佈式方面的需求。
# 當遇到單機內存,併發和流量瓶頸等問題時,可採用Cluster方案達到負載均衡的目的。
# 而且從另外一方面講,redis中sentinel有效的解決了故障轉移的問題,也解決了主節點下線客戶端沒法識別新的可用節點的問題,可是若是是從節點下線了,sentinel是不會對其進行故障轉移的,而且鏈接從節點的客戶端也沒法獲取到新的可用從節點,而這些問題在Cluster中都獲得了有效的解決。


# 安裝redis-cluser的客戶端程序
# 下載壓縮包
cd redis-py-cluster-unstable
python3 setup.py install
# 或pip
pip install redis-py-cluster

# 使用
python3
>>> from rediscluster import StrictRedisCluster  
>>> startup_nodes = [{"host": "127.0.0.1", "port": "7000"},{"host": "127.0.0.1", "port": "7001"},{"host": "127.0.0.1", "port": "7002"}]   
>>> rc = StrictRedisCluster(startup_nodes=startup_nodes, decode_responses=True)  
>>> rc.set("name", "zs")  
True  
>>> print(rc.get("name"))  
'zs'

11、redis常見問題以及解決方案

# 緩存穿透
概念
訪問一個不存在的key,緩存不起做用,請求會穿透到DB,流量大時DB會掛掉。

# 解決方案
採用布隆過濾器,使用一個足夠大的bitmap,用於存儲可能訪問的key,不存在的key直接被過濾;
訪問key未在DB查詢到值,也將空值寫進緩存,但能夠設置較短過時時間。

# 緩存雪崩
概念
大量的key設置了相同的過時時間,致使在緩存在同一時刻所有失效,形成瞬時DB請求量大、壓力驟增,引發雪崩。

# 解決方案
能夠給緩存設置過時時間時加上一個隨機值時間,使得每一個key的過時時間分佈開來,不會集中在同一時刻失效。

# 緩存擊穿
概念
一個存在的key,在緩存過時的一刻,同時有大量的請求,這些請求都會擊穿到DB,形成瞬時DB請求量大、壓力驟增。

# 解決方案
在訪問key以前,採用SETNX(set if not exists)來設置另外一個短時間key來鎖住當前key的訪問,訪問結束再刪除該短時間key。
相關文章
相關標籤/搜索