Redis從安裝到實踐

2020-08-12 21:03:11

Redis

宣告:本人已儘量將參照的地方註明來源,如有侵權,請聯繫,如有部分圖片無法顯示,請存取我的個人主頁

Linux下安裝步驟

常規安裝

  • wget http://download.redis.io/releases/redis-6.0.6.tar.gz
  • tar xzf redis-6.0.6.tar.gz
  • cd redis-6.0.6
  • make
    • 問題一
      • make: *** [all] 錯誤 2
        [外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-3nXEaLaz-1597237183686)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/202008031023533wxwrjsphds.png)]
    • 問題一原因
      • Linux系統gcc版本過低,yum安裝的gcc是4.8.5的。需要升級gcc
        [外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-qycIyu1r-1597237183689)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/202008031038034rdvremy31y.png)]
    • 問題一解決
      • yum -y install centos-release-scl
      • yum -y install devtoolset-7-gcc devtoolset-7-gcc-c++ devtoolset-7-binutils
      • scl enable devtoolset-7 bash
      • 注意:scl只是臨時啓用這個gcc版本,重新啓動就會失效,需要長期使用此版本GCC,需要執行如下命令
        • echo "source /opt/rh/devtoolset-7/enable" >>/etc/profile
      • 重新執行make

Docker安裝

暫無

執行

  • 確保自己在redis目錄下
    [外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-wndcaLww-1597237183690)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/20200803105349fugbud4nbhz.png)]
  • 前臺執行src/redis-server
    [外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-aIESQJu4-1597237183692)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/20200803114217zeq3543l2pt.png)]

目錄結構

[外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-sN8nG7iy-1597237183693)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/202008031113294fayaqifdy3.png)]

更改設定

  • 組態檔位置/root/redis-6.0.6/redis.conf

    • 注意:注意看自己是在哪個目錄下進行解壓操作的
  • 開啓組態檔vim redis.conf

    • 注意:vim命令未找到的話,需要安裝一下yum install -y vim(未測試)
  • 更改redis系結的IP地址

    • 跳轉到69行shift + :,輸入69,按enter鍵,這裏建議修改爲0.0.0.0,即是允許所有IP地址連線的意思
      [外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-Dk1VuaHb-1597237183694)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/20200803122001sgdxs34qmdo.png)]
    • 注意:輸入命令set nu可以顯示檔案行號
      [外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-lJRG4oax-1597237183695)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/20200803130403gr40zytrudr.png)]
  • 更改密碼

    • 同上一樣,所在行號如下表格所示
      [外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-0U3AMQ9R-1597237183696)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/20200803133409fjhchqdo1pv.png)]
    • 注意:在登錄時如果不設定密碼,將出現許可權不足的問題(也可以將protected-mode設爲no解決)

重要幾個參數所在行號

參數名 參數解釋 所在行號 預設值
bind 系結的主機IP地址 69 127.0.0.1
protected-mode 是否可以直接存取,開啓需設定bind ip或者設定存取密碼 88 yes
port 指定Redis監聽埠,預設埠爲6379 92 6379
pidfile 守護行程方式執行時,pid的值 244 /var/run/redis_6379.pid
loglevel 日誌級別,有debug、verbose、notice、warning 252 notice
logfile 日誌檔案位置,預設爲標準輸出, 257 「」
databases 邏輯數據庫的數量,預設連線數據庫爲0號數據庫,可以使用SELECT 命令在連線上指定數據庫id 272 16
save save ,多長時間內,有多少次更新操作,就將數據同步到數據檔案,可以多個條件配合 304 多個
requirepass 密碼 786 foobared

參考鏈接

  • 重新啓動Redis服務
    • 找到已執行redis進程號ps -ef|grep redis
      • 如果只顯示一行,則說明redis沒有執行,則不用進行重新啓動
        [外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-tpymSU0d-1597237183698)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/20200803134357ioctwgejfo5.png)]
    • 殺死已啓動的進程kill -9 pid號
    • 重新執行./redis-server /root/redis-6.0.6/redis.conf &,注意在src的目錄下!!!
      [外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-M1QSD1Wb-1597237183699)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/202008031405281ark50eosic.png)]
      • 三種方式
        • 方式一:直接啓動,進入redis根目錄,執行./redis-server & ,&代表後臺執行
          • 注意:此方式不會重新載入組態檔
        • 方式二:指定組態檔,進入根目錄,執行./redis-server 組態檔名,例如:./redis-server /etc/redis/6379.conf
        • 方式三:後臺自啓(略),參考鏈接
    • 檢查後臺執行是否成功
      • ps aux|grep redis
        [外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-w1msnm5s-1597237183701)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/20200803140934jrwtqv250tl.png)]

遠端連線(需要開放IP及埠)

  • 使用自己的redis連線工具,這裏使用的是RedisView

  • [外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-somLO2PR-1597237183701)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/20200803143522hxgu1ad5g4k.png)]

  • 注意:阿裡雲或者騰訊雲等需要開放安全組,也就是防火牆開放遠端連線ip和埠

  • 使用redisbin,在windows命令列連線

    • 下載地址
    • [外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-QIhwS6Cw-1597237183703)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/20200803155235l4frawo5kxp.png)]

阿裡雲開放IP及埠

  • [外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-UseSaIsx-1597237183703)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/202008031438462sj5kaqjefw.png)]

  • [外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-Nz3dLVxq-1597237183705)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/20200803144415pk5cujmpope.png)]

  • [外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-a2wyUqVB-1597237183707)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/20200803144618ykmn5xakuqn.png)]

  • [外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-vldv4kbE-1597237183708)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/202008031450131jhcajg5ljy.png)]

登錄成功

[外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-Dp0u21Ck-1597237183709)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/20200803145856mzq0aa12qkt.png)]

數據型別

  • String儲存簡單字串、複雜字串(xml、json)、數位(整數、浮點數)、二進制(圖片、音訊、視訊),最大不能超過512M
  • Hash儲存結構化物件
  • List儲存字串型別的集合數據,List中的數據是有序可重複的,因此可以通過下標獲取一個或者一段數據
  • Set儲存字串型別的集合數據,Set中的數據是無序的且不可重複
  • Sorted Set儲存不可重複的字串數據,但是區別於Set的是,Sorted Set可以在儲存數據時手動設定一個排序值,以此作爲排序依據

數據庫選擇

有16個庫,從0–15,預設是0號庫,庫的選擇:select 庫號;不同庫的數據不影響

  • select 0-15 選擇庫

  • flushall 清空整個redis伺服器數據,所有的數據庫全部清空

  • flushdb清除當前庫

數據操作

參考苟哥資料

String

命令 語法 用途 範例
set set 鍵 值 儲存一個鍵值對,鍵存在則覆蓋值 set test 10
get get 鍵 通過鍵取出對應的值,如果鍵不存在則返回null get test
getset getset 鍵 值 先取出鍵對應的值,然後修改值 getset test 10 20
incr incr 鍵 將鍵對應值自增1 ncr test
decr decr 鍵 將鍵對應值自減1 decr test
incrby incrby 鍵 自增值 將該鍵對應的值自增指定數值 incrby test 20
decrby decrby 鍵 自減值 將該鍵對應的值自減指定數值 decrby test 10
append append 鍵 值 如果該鍵存在,則在該值得基礎上追加一段字串,如果不存在該鍵則新增一個鍵值對 append test 66
127.0.0.1:6379> expire key  60 # 數據在 60s 後過期
(integer) 1
127.0.0.1:6379> setex key 60 value # 數據在 60s 後過期 (setex:[set] + [ex]pire)
OK
127.0.0.1:6379> ttl key # 檢視數據還有多久過期
(integer) 56

Hash

命令 語法 用途 範例
hset hset 鍵 屬性名 值 爲指定的鍵所對應的hash數據中的某個屬性賦值 hset student name jack
hmset hset 鍵 屬性名1 值1 屬性名2 值2 。。。 向指定的鍵所對應的hash數據中一次儲存一個或者多個鍵值對數據
hget hget 鍵 屬性名 查詢指定鍵對應的hash數據中某個屬性的值 hget student name 結果:jack
hmget hmget 鍵 屬性名1 屬性名2 。。。 查詢指定鍵對應的hash數據中一個或多個鍵值對的值
hdel hdel 鍵 屬性名1 屬性名2 。。。 刪除指定鍵對應的hash數據中的一個或者多個屬性 lrange names 0 10 結果是names對應的集閤中0-10之間的數據,如果數據長度不足,則返回已有的數據,不會出現異常
hexists hexists 鍵 屬性 判斷指定鍵中是否存在某個屬性,返回1表示存在,0表示不存在 hexists student name
hlen hlen 鍵 返回指定鍵對應的hash數據的屬性個數
hkeys hkeys 鍵 返回指定鍵對應的hash數據中的所有屬性名稱
hvals hvals 鍵 返回指定鍵對應的hash數據中的所有屬性值
hincrby hincrby 鍵 屬性名稱 增量 將指定鍵對應的屬性值自增增量的值 hincrby student age 10 age的值在原本的基礎上+10

List

命令 語法 用途 範例
lpush lpush 鍵 值1 值2… 在指定的鍵所對應的list的頭部插入所有的values,鍵不存在則新增 lpush names jack rose 集閤中最前的兩個數據是rose jack
rpush rpush 鍵 值1 值2… 在指定的鍵所對應的list的尾部插入所有的values,鍵不存在則新增 rpush names jack rose 集閤中最後的兩個數據是jack rose
lrange lrange 鍵 下標1 下標2 查詢從下標1到下標2之間的數據,第一個數據下標爲0,下標可以爲負數,-1表示倒數第一個數據,-2表示倒數第二個數據,以此類推 lrange names 0 10 結果是names對應的集閤中0-10之間的數據,如果數據長度不足,則返回已有的數據,不會出現異常
lpushx lpushx鍵 值1 在指定的鍵存在的情況下,將所有的數據插入到集合的頭部 lpushx names jack rose 集閤中最前的兩個數據是rose jack
rpushx rpushx鍵 值1 在指定的鍵存在的情況下,將所有的數據插入到集合的尾部 rpush names jack rose 集閤中最後的兩個數據是jack rose
lpop lpop 鍵 將該鍵對應的集閤中的第一個數據取出,取出之後第一個數據就從集閤中移除 lpop names 結果:取出並移除第一個數據
rpop rpop鍵 將該鍵對應的集閤中的最後一個個數據取出,取出之後數據就從集閤中移除 rpop names 結果:取出並移除最後一個數據

通過 rpush/lpop 實現佇列

127.0.0.1:6379> rpush myList value1 # 向 list 的頭部(右邊)新增元素
(integer) 1
127.0.0.1:6379> rpush myList value2 value3 # 向list的頭部(最右邊)新增多個元素
(integer) 3
127.0.0.1:6379> lpop myList # 將 list的尾部(最左邊)元素取出
"value1"
127.0.0.1:6379> lrange myList 0 1 # 檢視對應下標的list列表, 0 爲 start,1爲 end
1) "value2"
2) "value3"
127.0.0.1:6379> lrange myList 0 -1 # 檢視列表中的所有元素,-1表示倒數第一
1) "value2"
2) "value3"

通過 rpush/rpop 實現棧

127.0.0.1:6379> rpush myList2 value1 value2 value3
(integer) 3
127.0.0.1:6379> rpop myList2 # 將 list的頭部(最右邊)元素取出
"value3"

[外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-05MyqCrX-1597237183711)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/20200803183839vpelopqdzbh.png)]

通過 lrange 檢視對應下標範圍的列表元素

127.0.0.1:6379> rpush myList value1 value2 value3
(integer) 3
127.0.0.1:6379> lrange myList 0 1 # 檢視對應下標的list列表, 0 爲 start,1爲 end
1) "value1"
2) "value2"
127.0.0.1:6379> lrange myList 0 -1 # 檢視列表中的所有元素,-1表示倒數第一
1) "value1"
2) "value2"
3) "value3"

通過 lrange 命令,你可以基於 list 實現分頁查詢,效能非常高!

通過 llen 檢視鏈表長度

127.0.0.1:6379> llen myList
(integer) 3

Set

命令 語法 用途 範例
sadd add 鍵 值1 值2… 在指定的鍵對應的set集閤中儲存一個或多個數據 sadd names jack tom 集閤中最前的兩個數據是rose jack
smembers smembers鍵 查詢指定鍵的Set集閤中儲存的所有數據 smembers names
scard scard鍵 查詢指定鍵的Set集閤中儲存的數據個數 scard names
sismember sismembe 鍵 值 判斷值是否是指定鍵對應的Set集閤中的成員,返回1表示是,0表示否 sismember names jack
srem srem鍵 值1 值2… 刪除指定鍵對應的Set集閤中的一個或多個數據 srem names jack
spop spop鍵 count 隨機刪除並返回指定鍵對應的Set集閤中的1個或多個數據,數量由count決定,但是在某些版本中count不支援,不提供count時預設count爲1 spop numbers
srandmember srandmember鍵 count 隨機返回指定鍵對應的Set集閤中的1個或多個數據,數量由count決定,不提供count時預設count爲1 srandmember names 2 隨機返回names集閤中的2個數據
smove smove 鍵1 鍵2 值 將鍵1對應集閤中的指定數據移動到鍵2的集閤中 smove names1 names2 jack
sdiff sdiff 鍵1 鍵2 返回鍵1的集閤中在鍵2的集閤中不存在的數據,也就是求鍵1集合在鍵2集閤中的差集 sdiff names1 names2
sdiffstore sdiffstore 鍵1 鍵2 鍵3 將鍵2集合在鍵3中的差集儲存到鍵1的集閤中 sdiffstore names names1 names2
sinter sinter 鍵1 鍵2 返回鍵1集閤中在鍵2集閤中也存在的數據,也就是求鍵1集合和鍵2集合的交集 sinter names1 names2
sinterstore sinterstore 鍵1 鍵2 鍵3 將鍵2集合在鍵3中的交集儲存到鍵1的集閤中 sinterstore names names1 names2
sunion sunion 鍵1 鍵2 返回鍵1集合和鍵2集合的並集 sunion names1 names2
sunionstore sunion 鍵1 鍵2 鍵3 將鍵2集合和鍵3集合的並集儲存到鍵1集閤中 sunionstore names names1 names2

SortSet

命令 語法 用途 範例
zadd zadd 鍵 score value score value … 向指定鍵對應的集閤中新增數據,score爲排序值,可以是整數或小數。value爲值 zadd names 1 zhangsan 2 lisi
zcard zcard鍵 返回指定鍵對應的集閤中的數據總數 zcard names
zcount zcount 鍵 min max 查詢指定鍵的集閤中排序值在min和max之間的數據個數 zcount names 1 5
zrange zrange鍵 start stop 返回指定鍵對應的集閤中排序序號(非排序值)在start和stop之間的數據,序號從0開始,數據會升序排列 zrange names jack
zrank zrank 鍵 值 返回指定鍵對應的集閤中某個值得排序序號 zrank names jack
zrem zrem 鍵1 值1 值2 移除指定鍵對應的Set集閤中的1個或多個值 zrem names1 jack
zremrangebyrank zremrangebyrank 鍵 start stop 按照排序序號移除多個成員 zremrangebyrank names 0 2
zremrangebyscore zremrangebyscore鍵 min max 按照排序值移除多個成員 zremrangebyscore names 1 2

事務

雖然redis是單執行緒,但是可以同時有多個用戶端存取,每個用戶端會有一個執行緒。用戶端存取之間存在競爭,redis裏面只有單個命令是執行的。比如set,get。每執行一個命令都需要用戶端來競爭,所以可能出現併發問題,如果你希望把一組命令執行的結果作爲整體,要麼全部成功,要麼失敗,就必須用鎖,或者事務

參考資料

開啓:multi

  127.0.0.1:6379> multi
  OK

提交:exec

  127.0.0.1:6379> exec
  OK

清除待執行佇列:discard

監聽:watch類似樂觀鎖,

  • 如果在watch命令觀測一個key之後,開啓事務後修改該key.這個時候如果有其它連線修改了key,則會導致事務執行失敗,在這個事務的其他操作也是失敗
    exec之後,watch命令監控取消

    watch命令可以說是redis的事務功能最關鍵的運用了,在使用了watch之後可以保證一定的原子性和數據安全

特點

沒有隔離級別的概念

開啓事務之後的操作全部是在待執行佇列中快取,並沒有真正執行,也就不存在事務內部的查詢要看到事務即將的更新,事務外部也不知道

不保證原子性

Redis對單條命令是保證原子性(比如批次msetnx命令),但是如果事務不保證原子性(一致性),就沒有回滾的概唸了.事務中任何命令的失敗,其餘命令任會執行

可以這麼說.Redis的以上兩個事務特徵幾乎可以認爲,redis沒有事務功能.更應該稱之爲命令的打包執行.
那麼爲什麼redis中要有事務?舉個例子:假設登錄的時候記錄登錄的ip(一條命令操作),接下再執行儲存使用者的登錄訊息操作(一條命令),假設獲取使用者的訊息需要在另外一個系統中獲取,我們無法保證100%獲取到.但是這兩步操作在我們程式業務功能設計中應該被認爲是登錄操作的單個功能起的影響.因爲獲取使用者的登錄訊息可能會失敗.那麼就沒必要記錄ip了.redis恰恰提供事務名佇列的清空功能

可以怎麼理解,如果你的(命令)操作步驟非常多,每一個命令都需要大量的其他非redis的操作才能 纔能保證接下來的業務進行,那麼就需要redis的事務佇列功能一步一步記錄命令,在整個工程中如果發生了意外則清空佇列,正常則提交

所以我們應該在我們的程式程式碼中保證一致性和隔離級別的功能而不是交給redis,方正redis的事務就是打包執行,任何利用是查詢設計的問題

在redis開啓事務的魅力multi就可以知道,redis自己本身也沒有把這個功能稱之爲事務.正確的叫法是多命令

若在待執行佇列中存在語法性錯誤,exec提交之後,其他正確命令也會被執行,這是單單的錯誤命令拋出異常
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name li si #錯誤的命令
QUEUED
127.0.0.1:6379> set money 100
QUEUED
127.0.0.1:6379> exec
1) (error) ERR syntax error
2) OK

​ 如果是錯誤的命令名(比如setnx寫成setnn,則不能稱之爲語法錯誤),如果在佇列中出現了類似錯誤,則整個佇列不成功

參考資料

持久化

概念

解決計算機關閉造成的記憶體數據丟失問題

RDB(Redis DataBase)

redis預設採用此快照方式對數據進行持久化操作

Redis 快照 是最簡單的 Redis 永續性模式。當滿足特定條件時,它將生成數據集的時間點快照,例如,如果先前的快照是在2分鐘前建立的,並且現在已經至少有 100 次新寫入,則將建立一個新的快照。此條件可以由使用者設定 Redis 範例來控制,也可以在執行時修改而無需重新啓動伺服器。快照作爲包含整個數據集的單個 .rdb 檔案生成。

AOF(append-only file)

快照不是很持久。如果執行 Redis 的計算機停止執行,電源線出現故障或者您 kill -9 的範例意外發生,則寫入 Redis 的最新數據將丟失。儘管這對於某些應用程式可能不是什麼大問題,但有些使用案例具有充分的耐用性,在這些情況下,快照並不是可行的選擇。

AOF(Append Only File - 僅追加檔案) 它的工作方式非常簡單:每次執行 修改記憶體 中數據集的寫操作時,都會 記錄 該操作。假設 AOF 日誌記錄了自 Redis 範例建立以來 所有的修改性指令序列,那麼就可以通過對一個空的 Redis 範例 順序執行所有的指令,也就是 「重放」,來恢復 Redis 當前範例的記憶體數據結構的狀態

AOF 重寫

Redis 在長期執行的過程中,AOF 的日誌會越變越長。如果範例宕機重新啓動,重放整個 AOF 日誌會非常耗時,導致長時間 Redis 無法對外提供服務。所以需要對 AOF 日誌 「瘦身」

Redis 提供了 bgrewriteaof 指令用於對 AOF 日誌進行瘦身。其 原理 就是 開闢一個子進程 對記憶體進行 遍歷 轉換成一系列 Redis 的操作指令,序列化到一個新的 AOF 日誌檔案 中。序列化完畢後再將操作期間發生的 增量 AOF 日誌 追加到這個新的 AOF 日誌檔案中,追加完畢後就立即替代舊的 AOF 日誌檔案了,瘦身工作就完成了。

fsync

AOF 日誌是以檔案的形式存在的,當程式對 AOF 日誌檔案進行寫操作時,實際上是將內容寫到了內核爲檔案描述符分配的一個記憶體快取中,然後內核會非同步將髒數據刷回到磁碟的。

就像我們 上方第四步 描述的那樣,我們需要藉助 glibc 提供的 fsync(int fd) 函數來講指定的檔案內容 強制從內核快取刷到磁碟。但 「強制開車」 仍然是一個很消耗資源的一個過程,需要 「節制」!通常來說,生產環境的伺服器,Redis 每隔 1s 左右執行一次 fsync 操作就可以了。

Redis 同樣也提供了另外兩種策略,一個是 永不 fsync,來讓操作系統來決定合適同步磁碟,很不安全,另一個是 來一個指令就 fsync 一次,非常慢。但是在生產環境基本不會使用,瞭解一下即可。

設定

  • 新增組態檔
    • 使用cat redis.conf | grep -v "#" | grep -v "^$" > redis7379.conf命令將組態檔的註釋去掉並放入另外一個組態檔
  • 修改組態檔參數
    • bind 0.0.00
      • 允許所有IP連線
    • port7379
      • 埠,自定義,根據需要自定義
    • pidfile/var/run/redis_7379.pid
      • 進程號資訊,自定義
    • logfile「/root/redis-6.0.6/redis_7379.log」
      • 日誌檔案,自定義
    • 將三個save註釋掉,因爲不打算採用RDB方式進行持久化
    • appendonlyyes
      • 開啓AOF持久化
    • appendfilename「appendonly7379.aof」
      • 持久化檔名
    • appendfsynceverysec
      • 主要有三個參數可以選擇:always、everysec和no。設定爲always時,會極大消弱Redis的效能,因爲這種模式下每次write後都會呼叫fsync(Linux爲呼叫fdatasync;如果設定爲no,則write後不會有fsync呼叫,由操作系統自動排程刷磁碟,效能是最好的;everysec爲最多每秒呼叫一次fsync,這種模式效能並不是很糟糕,一般也不會產生毛刺,這歸功於Redis引入了BIO執行緒,所有fsync操作都非同步交給了BIO執行緒。

叢集

主從複製

  • 啓動多個redis,啓動預設都是爲主節點

    • 通過啓動不同的redis組態檔達到此目的,可以通過bash命令批次執行這些命令
      #!/bin/bash
      ./../src/redis-server /root/redis-6.0.6/config/redis7379.conf &
      ./../src/redis-server /root/redis-6.0.6/config/redis7380.conf &
      ./../src/redis-server /root/redis-6.0.6/config/redis7381.conf &
    
    
  • 將主節點變成從節點,使用slaveof命令

    • 進入 Redis 範例中使用控制檯
      • PS D:\soft_green_store\redisbin64> ./redis-cli.exe -p 7380 -h 120.26.173.108 -a jungle123456
    • slaveof 120.26.173.108 7380
      • 注意:在主庫沒有設定requirepass要求密碼驗證的情況下,主庫已設定則從庫需額外在組態檔中加上masterauth 主庫密碼參數纔可以複製主庫的數據[外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-BipfMlQQ-1597237183713)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/20200805142920acfad2ccmfu.png)]
      • 通過info replication可以檢視redis相關狀態資訊
        [外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-x4s5ZyBb-1597237183715)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/20200805143940lzr24qr4mf0.png)]
  • 搭建完成

    [外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-PyhzxUjp-1597237183716)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/20200805144824jwhpd4equfv.png)]

Redis Sentinel哨兵

  • 複製幾分哨兵的組態檔出來

    port 26379 #哨兵埠
    daemonize yes #後臺守護
    logfile "/root/redis-6.0.6/config/sentinel_log/26379.log" #日誌檔案
     #mymaster這個名字是自己給的別名,還是系統預設?? 名字監聽ip+埠 選舉次數要到達2次
    sentinel monitor mymaster 120.26.173.108 7379 2
    sentinel auth-pass mymaster jungle123456 #監聽密碼
    sentinel down-after-milliseconds mymaster 10000 #監聽服務GG等待時間
    #應該還應該設定哨兵的密碼!!!生產環境
    
  • 增加bash命令

    #!/bin/bash
    #啓動redis主從
    ./../src/redis-server /root/redis-6.0.6/config/redis7379.conf &
    ./../src/redis-server /root/redis-6.0.6/config/redis7380.conf &
    ./../src/redis-server /root/redis-6.0.6/config/redis7381.conf &
    #啓動哨兵,哨兵啓動後,哨兵組態檔,還會發現出現了一些變化
    ./../src/redis-server /root/redis-6.0.6/config/sentin1.conf --sentinel
    ./../src/redis-server /root/redis-6.0.6/config/sentin2.conf --sentinel
    ./../src/redis-server /root/redis-6.0.6/config/sentin3.conf --sentinel
    
  • 測試

    • 使用 redis-cil 工具連線哨兵節點,並執行 info Sentinel 命令來檢視是否已經在監視主節點了:

      PS D:\soft_green_store\redisbin64> ./redis-cli.exe -p 26379 -h 120.26.173.108
      redis 120.26.173.108:26379> info Sentinel
      # Sentinel
      sentinel_masters:1
      sentinel_tilt:0
      sentinel_running_scripts:0
      sentinel_scripts_queue_length:0
      sentinel_simulate_failure_flags:0
      master0:name=mymaster,status=ok,address=120.26.173.108:7379,slaves=0,sentinels=3
      

      上面列印的資訊顯示出,一個主節點下並沒有子slaves節點,說明啓動的三個redis服務,另外兩個並沒有成爲子服務,因爲之前是通過啓動後再通過命令來將主節點變成子節點,這裏覺得太麻煩,果斷修改組態檔,加上slaveof 120.26.173.108 7379,再重新啓動啓動,就一切ok了,全部設定如下:

      hash-max-ziplist-value 64
      list-max-ziplist-size -2
      list-compress-depth 0
      set-max-intset-entries 512
      zset-max-ziplist-entries 128
      zset-max-ziplist-value 64
      hll-sparse-max-bytes 3000
      stream-node-max-bytes 4096
      stream-node-max-entries 100
      activerehashing yes
      client-output-buffer-limit normal 0 0 0
      client-output-buffer-limit replica 256mb 64mb 60
      client-output-buffer-limit pubsub 32mb 8mb 60
      hz 10
      dynamic-hz yes
      aof-rewrite-incremental-fsync yes
      rdb-save-incremental-fsync yes
      jemalloc-bg-thread yes
      masterauth jungle123456
      slaveof 120.26.173.108 7379
      
    • 再測試kill掉主節點後的變化

    # Sentinel
    sentinel_masters:1
    sentinel_tilt:0
    sentinel_running_scripts:0
    sentinel_scripts_queue_length:0
    sentinel_simulate_failure_flags:0
    master0:name=mymaster,status=ok,address=120.26.173.108:7380,slaves=2,sentinels=3
    redis 120.26.173.108:26379>
    

    但同時還可以發現,哨兵節點認爲新的主節點仍然有兩個從節點 (上方 slaves=2),這是因爲哨兵在將 6381 切換成主節點的同時,將 6379 節點置爲其從節點。雖然 6379 從節點已經掛掉,但是由於 哨兵並不會對從節點進行客觀下線,因此認爲該從節點一直存在。當 6379 節點重新啓動後,會自動變成 6381 節點的從節點。

    另外,在故障轉移的階段,哨兵和主從節點的組態檔都會被改寫:

    • 對於主從節點: 主要是 slaveof 設定的變化,新的主節點沒有了 slaveof 設定,其從節點則 slaveof 新的主節點。
    • 對於哨兵節點: 除了主從節點資訊的變化,紀元(epoch) (記錄當前叢集狀態的參數) 也會變化,紀元相關的參數都 +1 了。

那麼,問題就來了,3個哨兵,不小心掛了兩個,選舉又要求有2個,這樣的情況又如何解決呢?

搜尋了一下,沒人提這個東西,是需要哨兵之間搭一個叢集,還是這個方案沒人用,所以沒人care。Q。。。AQ。。。

Redis叢集

注:本文很多理論內容都是來自這裏,特別是下面 下麪的內容,幾乎一模一樣,主要目的是防止原文消失QAQ

上圖 展示了 Redis Cluster 典型的架構圖,叢集中的每一個 Redis 節點都 互相兩兩相連,用戶端任意 直連 到叢集中的 任意一臺,就可以對其他 Redis 節點進行 讀寫 的操作。

基本原理

[外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-ApjFrNbY-1597237183717)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/202008051750570iomfselny4.png)]

Redis 叢集中內建了 16384 個雜湊槽。當用戶端連線到 Redis 叢集之後,會同時得到一份關於這個 叢集的設定資訊,當用戶端具體對某一個 key 值進行操作時,會計算出它的一個 Hash 值,然後把結果對 16384 求餘數,這樣每個 key 都會對應一個編號在 0-16383 之間的雜湊槽,Redis 會根據節點數量 大致均等 的將雜湊槽對映到不同的節點。

再結合叢集的設定資訊就能夠知道這個 key 值應該儲存在哪一個具體的 Redis 節點中,如果不屬於自己管,那麼就會使用一個特殊的 MOVED 命令來進行一個跳轉,告訴用戶端去連線這個節點以獲取數據:

GET x
-MOVED 3999 127.0.0.1:6381

MOVED 指令第一個參數 3999key 對應的槽位編號,後面是目標節點地址,MOVED 命令前面有一個減號,表示這是一個錯誤的訊息。用戶端在收到 MOVED 指令後,就立即糾正原生的 槽位對映表,那麼下一次再存取 key 時就能夠到正確的地方去獲取了。

叢集的主要作用

  1. 數據分割區: 數據分割區 (或稱數據分片) 是叢集最核心的功能。叢集將數據分散到多個節點,一方面 突破了 Redis 單機記憶體大小的限制,儲存容量大大增加另一方面 每個主節點都可以對外提供讀服務和寫服務,大大提高了叢集的響應能力。Redis 單機記憶體大小受限問題,在介紹持久化和主從複製時都有提及,例如,如果單機記憶體太大,bgsavebgrewriteaoffork 操作可能導致主進程阻塞,主從環境下主機切換時可能導致從節點長時間無法提供服務,全量複製階段主節點的複製緩衝區可能溢位……
  2. 高可用: 叢集支援主從複製和主節點的 自動故障轉移 (與哨兵類似),當任一節點發生故障時,叢集仍然可以對外提供服務。

快速體驗

第一步:建立叢集節點組態檔

首先我們找一個地方建立一個名爲 redis-cluster 的目錄:

mkdir -p ~/Desktop/redis-cluster

然後按照上面的方法,建立六個組態檔,分別命名爲:redis_7000.conf/redis_7001.confredis_7005.conf,然後根據不同的埠號修改對應的埠值就好了:

# 後臺執行
daemonize yes
# 埠號
port 7000
# 爲每一個叢集節點指定一個 pid_file
pidfile ~/Desktop/redis-cluster/redis_7000.pid
# 啓動叢集模式
cluster-enabled yes
# 每一個叢集節點都有一個組態檔,這個檔案是不能手動編輯的。確保每一個叢集節點的組態檔不通
cluster-config-file nodes-7000.conf
# 叢集節點的超時時間,單位:ms,超時後叢集會認爲該節點失敗
cluster-node-timeout 5000
# 最後將 appendonly 改成 yes(AOF 持久化)
appendonly yes
protected-mode  false

記得把對應上述組態檔中根埠對應的設定都修改掉 (port/ pidfile/ cluster-config-file)

Jungle:

  • sed -i "s/7001/7006/g"grep 「7001」 -rl ./redis_7006.conf``可以批次將檔案內的內容替換
第二步:分別啓動 6 個 Redis 範例
redis-server ~/Desktop/redis-cluster/redis_7000.conf
redis-server ~/Desktop/redis-cluster/redis_7001.conf
redis-server ~/Desktop/redis-cluster/redis_7002.conf
redis-server ~/Desktop/redis-cluster/redis_7003.conf
redis-server ~/Desktop/redis-cluster/redis_7004.conf
redis-server ~/Desktop/redis-cluster/redis_7005.conf

然後執行 ps -ef | grep redis 檢視是否啓動成功:

[外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-8Ux1FPLk-1597237183718)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/20200805174624m2ykqlzafny.png)]

可以看到 6 個 Redis 節點都以叢集的方式成功啓動了,但是現在每個節點還處於獨立的狀態,也就是說它們每一個都各自成了一個叢集,還沒有互相聯繫起來,我們需要手動地把他們之間建立起聯繫。

第三步:建立叢集

執行下列命令:

redis-cli --cluster create --cluster-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
  • Jungle:

    • 執行了

      ./../../src/redis-cli --cluster create --cluster-replicas 1 120.26.173.108:7000 120.26.173.108:7001 120.26.173.108:7002 120.26.173.108:7003 120.26.173.108:7004 120.26.173.108:7005
      

      出現[ERR] Node 120.26.173.108:7000 DENIED Redis is running in protected mode because protected mode is enabled, no bind address was specified, no authentication password is requested to clients一大串紅色。

      我估計就是因爲組態檔沒有設定protected-mode false,系統預設給了true,這時候可以選擇給個false,或者給個密碼

    • 再次執行,一直出現Waiting for the cluster to join...............................................

      原因是埠沒有放行,這裏使用的是7001到7005,那麼7001-7005及17001-17005都要放行

  • 這裏稍微解釋一下這個 --replicas 1 的意思是:我們希望爲叢集中的每個主節點建立一個從節點。

觀察控制檯輸出:

[外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-60tiIHbZ-1597237183719)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/20200805174704wnixsrzjumr.png)]

看到 [OK] 的資訊之後,就表示叢集已經搭建成功了,可以看到,這裏我們正確地建立了三主三從的叢集。

第四步:驗證叢集

我們先使用 redic-cli 任意連線一個節點:

redis-cli -c -h 127.0.0.1 -p 7000
127.0.0.1:7000>
  • -c表示叢集模式;-h 指定 ip 地址;-p 指定埠。

然後隨便 set 一些值觀察控制檯輸入:

127.0.0.1:7000> SET name wmyskxz
-> Redirected to slot [5798] located at 127.0.0.1:7001
OK
127.0.0.1:7001>

可以看到這裏 Redis 自動幫我們進行了 Redirected 操作跳轉到了 7001 這個範例上。

我們再使用 cluster info (檢視叢集資訊)cluster nodes (檢視節點列表) 來分別看看:(任意節點輸入均可)

127.0.0.1:7001> CLUSTER INFO
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:2
cluster_stats_messages_ping_sent:1365
cluster_stats_messages_pong_sent:1358
cluster_stats_messages_meet_sent:4
cluster_stats_messages_sent:2727
cluster_stats_messages_ping_received:1357
cluster_stats_messages_pong_received:1369
cluster_stats_messages_meet_received:1
cluster_stats_messages_received:2727

127.0.0.1:7001> CLUSTER NODES
56a04742f36c6e84968cae871cd438935081e86f 127.0.0.1:7003@17003 slave 4ec8c022e9d546c9b51deb9d85f6cf867bf73db6 0 1584428884000 4 connected
4ec8c022e9d546c9b51deb9d85f6cf867bf73db6 127.0.0.1:7000@17000 master - 0 1584428884000 1 connected 0-5460
e2539c4398b8258d3f9ffa714bd778da107cb2cd 127.0.0.1:7005@17005 slave a3406db9ae7144d17eb7df5bffe8b70bb5dd06b8 0 1584428885222 6 connected
d31cd1f423ab1e1849cac01ae927e4b6950f55d9 127.0.0.1:7004@17004 slave 236cefaa9cdc295bc60a5bd1aed6a7152d4f384d 0 1584428884209 5 connected
236cefaa9cdc295bc60a5bd1aed6a7152d4f384d 127.0.0.1:7001@17001 myself,master - 0 1584428882000 2 connected 5461-10922
a3406db9ae7144d17eb7df5bffe8b70bb5dd06b8 127.0.0.1:7002@17002 master - 0 1584428884000 3 connected 10923-16383
127.0.0.1:7001>

數據分割區方案簡析

方案一:雜湊值 % 節點數

雜湊取餘分割區思路非常簡單:計算 key 的 hash 值,然後對節點數量進行取餘,從而決定數據對映到哪個節點上。

不過該方案最大的問題是,當新增或刪減節點時,節點數量發生變化,系統中所有的數據都需要 重新計算對映關係,引發大規模數據遷移。

方案二:一致性雜湊分割區

一致性雜湊演算法將 整個雜湊值空間 組織成一個虛擬的圓環,範圍是 [0 , 232-1],對於每一個數據,根據 key 計算 hash 值,確數據在環上的位置,然後從此位置沿順時針行走,找到的第一臺伺服器就是其應該對映到的伺服器:

[外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-LRoMwy5m-1597237183719)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/20200805174731njzsdwdzequ.png)]

與雜湊取餘分割區相比,一致性雜湊分割區將 增減節點的影響限制在相鄰節點。以上圖爲例,如果在 node1node2 之間增加 node5,則只有 node2 中的一部分數據會遷移到 node5;如果去掉 node2,則原 node2 中的數據只會遷移到 node4 中,只有 node4 會受影響。

一致性雜湊分割區的主要問題在於,當 節點數量較少 時,增加或刪減節點,對單個節點的影響可能很大,造成數據的嚴重不平衡。還是以上圖爲例,如果去掉 node2node4 中的數據由總數據的 1/4 左右變爲 1/2 左右,與其他節點相比負載過高。

方案三:帶有虛擬節點的一致性雜湊分割區

該方案在 一致性雜湊分割區的基礎上,引入了 虛擬節點 的概念。Redis 叢集使用的便是該方案,其中的虛擬節點稱爲 槽(slot)。槽是介於數據和實際節點之間的虛擬概念,每個實際節點包含一定數量的槽,每個槽包含雜湊值在一定範圍內的數據。

在使用了槽的一致性雜湊分割區中,槽是數據管理和遷移的基本單位。槽 解耦數據和實際節點 之間的關係,增加或刪除節點對系統的影響很小。仍以上圖爲例,系統中有 4 個實際節點,假設爲其分配 16 個槽(0-15);

  • 槽 0-3 位於 node1;4-7 位於 node2;以此類推…

如果此時刪除 node2,只需要將槽 4-7 重新分配即可,例如槽 4-5 分配給 node1,槽 6 分配給 node3,槽 7 分配給 node4;可以看出刪除 node2 後,數據在其他節點的分佈仍然較爲均衡。

節點通訊機制 機製簡析

叢集的建立離不開節點之間的通訊,例如我們上訪在 快速體驗 中剛啓動六個叢集節點之後通過 redis-cli 命令幫助我們搭建起來了叢集,實際上背後每個叢集之間的兩兩連線是通過了 CLUSTER MEET <ip> <port> 命令發送 MEET 訊息完成的,下面 下麪我們展開詳細說說。

兩個埠

哨兵系統 中,節點分爲 數據節點哨兵節點:前者儲存數據,後者實現額外的控制功能。在 叢集 中,沒有數據節點與非數據節點之分:所有的節點都儲存數據,也都參與叢集狀態的維護。爲此,叢集中的每個節點,都提供了兩個 TCP 埠:

  • 普通埠: 即我們在前面指定的埠 (7000等)。普通埠主要用於爲用戶端提供服務 (與單機節點類似);但在節點間數據遷移時也會使用。
  • 叢集埠: 埠號是普通埠 + 10000 (10000是固定值,無法改變),如 7000 節點的叢集埠爲 17000叢集埠只用於節點之間的通訊,如搭建叢集、增減節點、故障轉移等操作時節點間的通訊;不要使用用戶端連線叢集介面。爲了保證叢集可以正常工作,在設定防火牆時,要同時開啓普通埠和叢集埠。
Gossip 協定

節點間通訊,按照通訊協定可以分爲幾種型別:單對單、廣播、Gossip 協定等。重點是廣播和 Gossip 的對比。

  • 廣播是指向叢集內所有節點發送訊息。優點 是叢集的收斂速度快(叢集收斂是指叢集內所有節點獲得的叢集資訊是一致的),缺點 是每條訊息都要發送給所有節點,CPU、頻寬等消耗較大。
  • Gossip 協定的特點是:在節點數量有限的網路中,每個節點都 「隨機」 的與部分節點通訊 (並不是真正的隨機,而是根據特定的規則選擇通訊的節點)*,經過一番雜亂無章的通訊,每個節點的狀態很快會達到一致。Gossip 協定的 優點 有負載 *(比廣播) 低、去中心化、容錯性高 (因爲通訊有冗餘) 等;缺點 主要是叢集的收斂速度慢。

訊息型別

叢集中的節點採用 固定頻率(每秒10次)定時任務 進行通訊相關的工作:判斷是否需要發送訊息及訊息型別、確定接收節點、發送訊息等。如果叢集狀態發生了變化,如增減節點、槽狀態變更,通過節點間的通訊,所有節點會很快得知整個叢集的狀態,使叢集收斂。

節點間發送的訊息主要分爲 5 種:meet 訊息ping 訊息pong 訊息fail 訊息publish 訊息。不同的訊息型別,通訊協定、發送的頻率和時機、接收節點的選擇等是不同的:

  • MEET 訊息: 在節點握手階段,當節點收到用戶端的 CLUSTER MEET 命令時,會向新加入的節點發送 MEET 訊息,請求新節點加入到當前叢集;新節點收到 MEET 訊息後會回覆 回復一個 PONG 訊息。
  • PING 訊息: 叢集裡每個節點每秒鐘會選擇部分節點發送 PING 訊息,接收者收到訊息後會回覆 回復一個 PONG 訊息。PING 訊息的內容是自身節點和部分其他節點的狀態資訊,作用是彼此交換資訊,以及檢測節點是否線上。PING 訊息使用 Gossip 協定發送,接收節點的選擇兼顧了收斂速度和頻寬成本,具體規則如下:(1)隨機找 5 個節點,在其中選擇最久沒有通訊的 1 個節點;(2)掃描節點列表,選擇最近一次收到 PONG 訊息時間大於 cluster_node_timeout / 2 的所有節點,防止這些節點長時間未更新。
  • PONG訊息: PONG 訊息封裝了自身狀態數據。可以分爲兩種:第一種 是在接到 MEET/PING 訊息後回覆 回復的 PONG 訊息;第二種 是指節點向叢集廣播 PONG 訊息,這樣其他節點可以獲知該節點的最新資訊,例如故障恢復後新的主節點會廣播 PONG 訊息。
  • FAIL 訊息: 當一個主節點判斷另一個主節點進入 FAIL 狀態時,會向叢集廣播這一 FAIL 訊息;接收節點會將這一 FAIL 訊息儲存起來,便於後續的判斷。
  • PUBLISH 訊息: 節點收到 PUBLISH 命令後,會先執行該命令,然後向叢集廣播這一訊息,接收節點也會執行該 PUBLISH 命令。

數據結構簡析

節點需要專門的數據結構來儲存叢集的狀態。所謂叢集的狀態,是一個比較大的概念,包括:叢集是否處於上線狀態、叢集中有哪些節點、節點是否可達、節點的主從狀態、槽的分佈……

節點爲了儲存叢集狀態而提供的數據結構中,最關鍵的是 clusterNodeclusterState 結構:前者記錄了一個節點的狀態,後者記錄了叢集作爲一個整體的狀態。

clusterNode 結構

clusterNode 結構儲存了 一個節點的當前狀態,包括建立時間、節點 id、ip 和埠號等。每個節點都會用一個 clusterNode 結構記錄自己的狀態,併爲叢集內所有其他節點都建立一個 clusterNode 結構來記錄節點狀態。

下面 下麪列舉了 clusterNode 的部分欄位,並說明了欄位的含義和作用:

typedef struct clusterNode {
    //節點建立時間
    mstime_t ctime;
    //節點id
    char name[REDIS_CLUSTER_NAMELEN];
    //節點的ip和埠號
    char ip[REDIS_IP_STR_LEN];
    int port;
    //節點標識:整型,每個bit都代表了不同狀態,如節點的主從狀態、是否線上、是否在握手等
    int flags;
    //設定紀元:故障轉移時起作用,類似於哨兵的設定紀元
    uint64_t configEpoch;
    //槽在該節點中的分佈:佔用16384/8個位元組,16384個位元;每個位元對應一個槽:位元值爲1,則該位元對應的槽在節點中;位元值爲0,則該位元對應的槽不在節點中
    unsigned char slots[16384/8];
    //節點中槽的數量
    int numslots;
    …………
} clusterNode;

除了上述欄位,clusterNode 還包含節點連線、主從複製、故障發現和轉移需要的資訊等。

clusterState 結構

clusterState 結構儲存了在當前節點視角下,叢集所處的狀態。主要欄位包括:

typedef struct clusterState {
    //自身節點
    clusterNode *myself;
    //設定紀元
    uint64_t currentEpoch;
    //叢集狀態:線上還是下線
    int state;
    //叢集中至少包含一個槽的節點數量
    int size;
    //雜湊表,節點名稱->clusterNode節點指針
    dict *nodes;
    //槽分佈資訊:陣列的每個元素都是一個指向clusterNode結構的指針;如果槽還沒有分配給任何節點,則爲NULL
    clusterNode *slots[16384];
    …………
} clusterState;

除此之外,clusterState 還包括故障轉移、槽遷移等需要的資訊。

建議閱讀

設定

閱讀文章

概念

https://blog.csdn.net/nsrainbow/article/details/49032337

文章這裏設定不錯

    bind:0.0.0.0(指定存取的網絡卡,預設爲127.0.01只能本機存取,可以將此項改爲0.0.0.0或本機IP,或者直接註釋掉此項。)
    protected-mode:no (是否開啓保護模式,開啓後只能本機存取。此項預設爲yes,需要設定爲no。)
    port:7000(服務執行埠,如果6個節點執行在同一臺機器上,每個節點的埠應不相同。)
    requirepass:123456(存取密碼,設定爲需要的密碼。建議每個節點密碼相同。)
    masterauth:123456(主節點的密碼,當從節點從主節點同步數據時,需要提供主節點的密碼。一般與存取密碼相同。)
    cluster-enabled :yes(是否開啓cluster模式,預設爲no,需要設定爲yes。)
    cluster-config-file:nodes.conf (儲存cluster中每個節點資訊的檔案,該檔案會自動生成,這裏設定的是檔案儲存的位置。)
    cluster-node-timeout:5000(超時多長時間後判斷節點掛掉。)
    appendonly:yes(開啓AOF持久化)

快取

快取穿透

概念

大量請求的key不存在快取中,導致請求全部落在數據庫

解決方式

  • 參數校驗

    • ID校驗

    • 郵箱格式不對
      參考文章

    • 參考文章
          Validated代表去實體類校驗
      <1> 參數 Foo 前需要加上 @Validated 註解,表明需要 spring 對其進行校驗,而校驗的資訊會存放到其後的 BindingResult 中。注意,必須相鄰,如果有多個參數需要校驗,形式可以如下。foo(@Validated Foo foo, BindingResult fooBindingResult ,@Validated Bar bar, BindingResult barBindingResult); 即一個校驗類對應一個校驗結果。
      <2> 校驗結果會被自動填充,在 controller 中可以根據業務邏輯來決定具體的操作,如跳轉到錯誤頁面。
          
      #JSR 提供的校驗註解
      @Null   被註釋的元素必須爲 null    
      @NotNull    被註釋的元素必須不爲 null    
      @AssertTrue     被註釋的元素必須爲 true    
      @AssertFalse    被註釋的元素必須爲 false    
      @Min(value)     被註釋的元素必須是一個數字,其值必須大於等於指定的最小值    
      @Max(value)     被註釋的元素必須是一個數字,其值必須小於等於指定的最大值    
      @DecimalMin(value)  被註釋的元素必須是一個數字,其值必須大於等於指定的最小值    
      @DecimalMax(value)  被註釋的元素必須是一個數字,其值必須小於等於指定的最大值    
      @Size(max=, min=)   被註釋的元素的大小必須在指定的範圍內    
      @Digits (integer, fraction)     被註釋的元素必須是一個數字,其值必須在可接受的範圍內    
      @Past   被註釋的元素必須是一個過去的日期    
      @Future     被註釋的元素必須是一個將來的日期    
      @Pattern(regex=,flag=)  被註釋的元素必須符合指定的正則表達式
      
      #Hibernate Validator
      @NotBlank(message =)   驗證字串非 null,且長度必須大於 0    
      @Email  被註釋的元素必須是電子郵箱地址    
      @Length(min=,max=)  被註釋的字串的大小必須在指定的範圍內    
      @NotEmpty   被註釋的字串的必須非空    
      @Range(min=,max=,message=)  被註釋的元素必須在合適的範圍內
      
      

      總結:

      在springboot中,對於@RequestParam的非實體類參數進行校驗,需要在類上加上@Validated註解,BindingRequest參數必須僅跟着實體類參數校驗,不能離太遠,@validate註解後如果不跟BindingRequest參數,則會拋出異常,不會進入方法內部,其實我也知道,我以後可能不會看這個,但是就是想寫一寫QAQ…

  • 快取和數據庫都沒有可以設定過期時間

    set key value EX 10086但是隻能解決key變化不頻繁的情況,如要使用此方法,儘量將key的過期時間設定短一點,比如1分鐘

    注:key的命名:表名:主鍵名:主鍵值

    public Object getObjectInclNullById(Integer id) {
        // 從快取中獲取數據
        Object cacheValue = cache.get(id);
        // 快取爲空
        if (cacheValue == null) {
            // 從數據庫中獲取
            Object storageValue = storage.get(key);
            // 快取空物件
            cache.set(key, storageValue);
            // 如果儲存數據爲空,需要設定一個過期時間(300秒)
            if (storageValue == null) {
                // 必須設定過期時間,否則有被攻擊的風險
                cache.expire(key, 60 * 5);
            }
            return storageValue;
        }
        return cacheValue;
    }
    
  • 布隆過濾器

    • 布隆過濾器是一個非常神奇的數據結構,通過它我們可以非常方便地判斷一個給定數據是否存在於海量數據中。我們需要的就是判斷 key 是否合法,有沒有感覺布隆過濾器就是我們想要找的那個「人」。

      具體是這樣做的:把所有可能存在的請求的值都存放在布隆過濾器中,當使用者請求過來,先判斷使用者發來的請求的值是否存在於布隆過濾器中。不存在的話,直接返回請求參數錯誤資訊給用戶端,存在的話纔會走下面 下麪的流程。

    • 具體參考另外一篇詳細文章

快取雪崩

概念

實際上,快取雪崩描述的就是這樣一個簡單的場景:快取在同一時間大面積的失效,後面的請求都直接落到了數據庫上,造成數據庫短時間內承受大量請求。 這就好比雪崩一樣,摧枯拉朽之勢,數據庫的壓力可想而知,可能直接就被這麼多請求弄宕機了。

舉個例子:系統的快取模組出了問題比如宕機導致不可用。造成系統的所有存取,都要走數據庫。

還有一種快取雪崩的場景是:有一些被大量存取數據(熱點快取)在某一時刻大面積失效,導致對應的請求直接落到了數據庫上。 這樣的情況,有下面 下麪幾種解決辦法:

舉個例子 :秒殺開始 12 個小時之前,我們統一存放了一批商品到 Redis 中,設定的快取過期時間也是 12 個小時,那麼秒殺開始的時候,這些秒殺的商品的存取直接就失效了。導致的情況就是,相應的請求直接就落到了數據庫上,就像雪崩一樣可怕。

解決方案

  • 針對 Redis 服務不可用的情況:

    • 採用 Redis 叢集,避免單機出現問題整個快取服務都沒辦法使用。
    • 限流,避免同時處理大量的請求。
    • 建議閱讀:文章1
  • 針對熱點快取失效的情況:

    • 設定不同的失效時間比如隨機設定快取的失效時間。
    • 快取永不失效。

JAVA整合

SpringBoot

設定

  • 導包

    •     <!--Redis-->
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-data-redis</artifactId>
          </dependency>
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-cache</artifactId>
          </dependency>
          <dependency>
              <groupId>com.fasterxml.jackson.core</groupId>
              <artifactId>jackson-databind</artifactId>
          </dependency>
      
  • 啓動器開啓快取,新增@EnableCaching註解

  • 新增組態檔

    • spring:
        redis:
          database: 0
          host: 120.26.173.108
          port: 7000
          #password: jungle123456
          #連線超時時間(毫秒)
          timeout: 20000
          jedis:
            pool:
              #連線池最大連線數(使用負值表示沒有限制)
              max-active: 8
              #連線池最大阻塞等待時間(使用負值表示沒有限制)
              max-wait: 1
              #連線池中的最大空閒連線
              max-idle: 8
              #連線池中的最小空閒連線
              min-idle: 0
          cluster:
            nodes:
              - 120.26.173.108:7000
              - 120.26.173.108:7001
              - 120.26.173.108:7002
              - 120.26.173.108:7003
              - 120.26.173.108:7004
              - 120.26.173.108:7005
            max-redirects: 3
      
  • 設定一下redis序列化方式

    • /**
       * Redis 快取快取管理器
       *	JdkSerializationRedisSerializer:
       *
       *	GenericJackson2JsonRedisSerializer
       *
       *	StringRedisSerializer
       *
       *	GenericFastJsonRedisSerializer
       *
       *	GenericFastJsonRedisSerializer,最好用,不報錯
       
       * @author Jiangmanman
       * @date 2020/08/06
       */
      @Configuration
      public class RedisConfig {
          @Bean
          public RedisCacheConfiguration redisCacheConfiguration() {
              RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
              //設定redis快取值的序列化方式,此處採用json方式序列化
              redisCacheConfiguration =
                      redisCacheConfiguration.
                      serializeValuesWith(RedisSerializationContext.SerializationPair.
                      fromSerializer(new GenericFastJsonRedisSerializer()));
              return redisCacheConfiguration;
          }
      
      }
      

使用

在業務層層的查詢方法上新增@Cacheable(value = "快取數據的名稱空間名稱"),將查詢的數據儲存到redis中,後續執行這個業務層方法會優先從快取中獲取數據。

在業務層的增刪改方法上新增@CacheEvict(value="快取數據的名稱空間名稱",allEntries = true)註解,在執行增刪改時將快取數據刪除,下次查詢時會從數據庫查詢數據。

屬性詳細解釋如下

Cacheable

  • cacheNames:快取數據的名稱空間,可以用於區分不同的數據,其實該值就是快取數據鍵的字首。

  • key:定義快取數據的後綴,可以在同一個名稱空間下,對不同的數據進行更加細緻的拆分。在key中可以使用#參數名稱的方式用參數作爲key。

CacheEvict

  • cacheNames:需要清除的快取數據的名稱空間

  • allEntries:是否需要清空名稱空間中所有的數據

例如:

[外連圖片轉存失敗,源站可能有防盜鏈機制 機製,建議將圖片儲存下來直接上傳(img-gkTJp9NY-1597237183720)(https://s3.ap-northeast-1.wasabisys.com/img.tw511.com/202008/20200806120440wktabda0dim.png)]

問題解決

  • springboot整合redis叢集,組態檔IP地址爲伺服器公網ip,控制檯日誌報錯卻是伺服器內網ip連線不上.

    問題原因:整合springboot之後,他會用任意節點通過cluster slots 命令去獲取叢集中糟點資訊,根據控制檯報錯返回所有節點資訊都是內網ip。因爲每個節點都是從自己的nodes.conf檔案中獲取cluster-config-file的地址。叢集第一次啓動後,會在
    cd ./cluster/data/相對應的節點目錄下生成該檔案,開啓該檔案會發現cluster-config-file一行的ip地址是自身的內網ip.這是因爲生成時自己的的節點ip時通過網絡卡IP作爲地址的,由於雲伺服器網絡卡地址是內網,自然這裏的ip就是內網的IP地址了。

    解決方案:
    先殺死節點,修改每個節點的nodes.conf中的cluster-config-file,把內網ip改爲公網ip;再重新啓動。就OK了(後記:手動將cluster-config-file /root/redis-6.0.6/config/cluster/nodes-7000.conf這個檔案裏面的內網IP變成公網IP,:

    #改變命令,每一個都需要改
    sed -i "s/172.16.62.141/120.26.173.108/g" `grep "172.16.62.141" -rl ./nodes-7005.conf`
    #然後重新啓動
    ./../../src/redis-server /root/redis-6.0.6/config/cluster/redis_7004.conf
    #天吶,要不要這麼麻煩!!!
    

    1. 每個組態檔加上 bind 0.0.0.0重新啓動出現

      [ERR] Node 120.26.173.108:7000 is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0.

      伺服器重新啓動前節點中儲存了節點設定以及數據備份檔案。重新啓動時節點以及數據資訊衝突,故無法重新啓動。

      刪除節點設定(nodes.conf)以及數據備份檔案(dump.rdb、appendonly.aof)

      清除各節點數據
      2.1 連線進入各節點
      Linux命令 ./redis-cli -h 192.168.25.133 -p 7001
      2.2 清除節點數據
      Linux命令 flushdb

偶也,完成所有步驟,springboot測試也成功,將redis的cluster設定備份一下,備份連線如下

鏈接: https://pan.baidu.com/s/17WQFF-X488Cvx7mEopAmRA 提取碼: yka1