redis從安裝到三主三從集羣

文章目錄

一、爲什麼要使用Redis

1、讀寫的二八原則

  大多數場景下用戶的寫操作次數遠遠小於讀操作次數,例如:用戶使用淘寶的時候,絕大部分時間是查詢商品,下單購買的操作頻率相對較低,將頻繁讀取的數據,寫入到reids中,可以減少讀取數據庫的次數,降低數據庫的負載,同時也能加快讀取速度

2、信息的統一管理

  當系統部署多個節點的時候,每個節點的緩存數據獨立管理,會導致系統之間的數據出現不一致,交互不及時的情況,使用redis統一對這部分數據進行關聯,可以降低維護代價和通知代價

二、常見緩存方案的對比

Ehcache Memcache Redis
優點 缺點 優點 缺點 優點 缺點
基於java開發 不支持集羣 key-value存儲 無法容災 數據結構豐富 單線程 (某些時候是優勢)
基於JVM緩存 不支持分佈式 內存使用率高 無法持久化 可持久化 單核
簡單、輕巧、方便 多核、多線程 主從同步、故障轉移

三、安裝redis

1、下載redis安裝包、上傳linux服務器

下載地址:https://redis.io/download

2、安裝依賴

yum install gcc-c++

3、解壓安裝包

tar -zxvf redis-5.0.5.tar.gz

4、進入文件夾、編譯並安裝

cd redis-5.0.5
make && make install

5、修改redis的核心配置文件

在redis根目錄下

#打開配置文件進行修改
vim redis.conf

修改配置

#後臺運行
daemonize yes
#工作目錄,一定是個目錄,不是文件(需要自己創建文件夾)
dir /usr/local/redis/working
#可以訪問redis的ip(這裏表示所有ip都可以訪問)
bind 0.0.0.0
#redis的密碼(需要自己添加這個配置項,嚴重的後門)
requirepass 123456
#redis的端口
port 6379
#redis啓動的進程號保存的位置
pidfile /var/run/redis_6379.pid

6、修改redis的啓動腳本

在redis的utils目錄下

#複製到init.d目錄下爲方便開機啓動
cp redis_init_script /etc/init.d/
cd /etc/init.d/
vim edis_init_script

修改配置

#redis的端口(建議在redis.config中修改)
REDISPORT=6379
#指定redis的服務啓動文件
EXEC=/usr/local/bin/redis-server
#指定redis的客戶端啓動文件
CLIEXEC=/usr/local/bin/redis-cli

#redis啓動的進程號保存的位置(建議在redis.config中修改)
PIDFILE=/var/run/redis_${REDISPORT}.pid
#redis默認使用的核心配置文件(redis.config),如果不換核心配置不需要修改
CONF="/usr/local/redis/redis.conf"

如果redis設置了密碼

在腳本中找到如下這句話
$CLIEXEC -p $REDISPORT shutdown
修改成
$CLIEXEC -a 這裏寫密碼 -p $REDISPORT shutdown

修改執行權限

chmod 777 redis_init_script

7、配置開機啓動

添加自啓動腳本,在redis_init_script文件的註釋下,配置前的位置上添加
注意:保留前面的“#”號

#chkonfig: 22345 10 90
#description: Start and Stop redis

保存並退出redis_init_script,配置開機啓動
注意:redis_init_script文件必須存放到/etc/init.d目錄下

chkconfig redis_init_script on

8、運行reids

./redis_init_script start

四、redis各數據類型的基本命令

1、通用的命令

命令 註釋
auth [password] 登錄
keys [*key*] 查詢key列表,查詢所有的key使用 keys *
ttl [key] 查看過期時間
expire [key] [time] 設置過期時間
select [num] 切換庫,默認0-15
flushdb 清空當前庫
flushall 清空所有庫

2、String的命令

命令 註釋
set [key] [value] 設置鍵值對(類似java Map的put方法)
get [key] 獲取鍵對應的值
del [key] 刪除鍵值對
setnx [key] [value] 當key不存的時候,設置value
append [key] [value] 將值,拼接到原本值的後面
strlen [key] 查看字符串長度
incr [key] 累加,自加一(純數字字符串)
decr [key] 累減,自減一(純數字字符串)
getrange [key] [start] [end] 截取值的一段內容
setrange [key] [start] [value] 替換值中的一段內容
mset [key] [value] [key value …] 同時設置多個鍵值對
mget [key] [key …] 同時獲取多個鍵值對
msetnx [key] [value] [key value …] 當key不存的時候,同時設置多個value

3、hash的命令

命令 註釋
hset [key] [field] [value] 設置hash對象
gset [key] [field] 獲取指定hash對象的指定屬性值
hmset [key] [field] [value] [field value …] 同時設置hash對象的多個屬性
hmget [key] [field] [field …] 同時獲取hash對象的多個屬性
hgetall [key] 獲取hash對象裏面所有的屬性和值
hlen [key] 獲取hash對象裏屬性的個數
hkeys [key] 獲取hash對象裏屬性名
hvak [key] 獲取hash對象裏屬性值
hexists [key] [field] 判斷某個屬性是否存在
hdel [key] [field] 刪除指定屬性

4、list的命令

命令 註釋
lpush [key] [vlaue] [value …] 設置list對象,將值存到list的左邊
rpush [key] [vlaue] [value …] 設置list對象,將值存到list的右邊
lrange [key] [start] [end] 獲取指定list的某個區間裏面的對象
lpop [key] 彈出列表最左側的值
rpop [key] 彈出列表最右側的值
llen [key] 獲取list裏面的元素個數
lindex [key] [index] 獲取list中指定下標的值
lset [key] [index] [value] 修改指定下標的元素
linsert [key] [before after] [value] [newVlaue]
lrem [key] [count] [value] 刪除幾個指定值的元素

5、set的命令

命令 註釋
sadd [key] [vlaue] [value …] 添加不重複的值
smembers [key] 查看set裏面的所有值
scard [key] 查看set裏面值的個數
sismember [key] [value] 查看值是否存在
spop [key] 彈出第一個元素
spop [key] [num] 彈出前幾個元素
srandmember [key] [count] 從set裏面隨機獲取幾個對象
smove [oldkey] [newkey] [value] 將oldkey裏面的value元素移到newkey中
sdiff [key1] [key2] 獲取兩set的差集,key1中有但key2中沒有的
sunion [key1] [key2] 獲取兩set的並集
sinter [key1] [key2] 獲取兩set的交集

6、zset的命令(有序set)

命令 註釋
zadd [key] [score] [value] [score value …] 設置member和對應的分數
zrange [key] [start] [end] 查看set中的內容
zrank [key] [value] 獲取元素對應的下標
zscore [key] [value] 獲取元素對應的分數
zcard [key] 統計元素個數
zcount [key] [score1] [score2] 統計指定分數的個數
zrangebyscore [key] [score1] [score2] 查詢指定分數之間的member(包含分數1 分數2)
zrangebyscore [key] [score1] [score2] limit [start] [end] 查詢指定分數之間的member(包含分數1 分數2),做分頁
zrem [zset] [value] 刪除指定元素

五、SpringBoot整合Redis

1、添加redis依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2、修改配置文件

spring:
    redis:
        #使用redis的庫
        database: 0
        #連接地址
        host: 127.0.0.1
        #連接端口
        post: 6379
        #密碼
        password: 123456

3、編寫代碼

這裏以一個Controller爲例

@RestController
@RequestMapping("/redis")
public class RedisController{
    //注入模板
    @Autowired
    private RedisTemplate redis;
    //也可以使用StringRedisTemplate
    
    @GetMapping("/set")
    public Object set(String key,String value){
        redis.opsForValue.set(key,value);
        return "OK";
    }
    
     @GetMapping("/get")
    public String get(String key){
        return (String)redis.opsForValue.get(key);
    }
}

六、Redis進階

1、訂閱和發佈

準備2個客戶端,1臺爲發佈,1臺爲訂閱(這兩個客戶端要連接同一個redis)
注意:這裏只是演示,實際使用的時候,我們應當選擇專業的MQ

(1)進行訂閱

打開一個redis客戶端,用於訂閱

#訂閱一個叫food的頻道,food只是一個標識,可以填寫任意的字符串
subscribe food
#通配符訂閱,這樣所有的以chat爲標識的消息,都會被訂閱
psubscribe chat*

執行命令之後,客戶端會處於一個阻塞的監聽狀態

(2)進行發佈

打開一個redis客戶端,用於發佈

#向訂閱了food頻道的客戶端發送一個漢堡
publish food burger

2、持久化

redis的數據是存在內存中的,如果突然斷電我們的數據就會就是,使用持久化讓數據保存在硬盤上

(1)RDB

每個一段時間,進行對redis數據進行快照,是一種全量的持久化
優勢:

  • 可以靈活的配置備份的時間,每個幾小時或者幾天備一份一次都可以
  • 會備份成單獨的一個文件,文件名稱也可以進行自定義,這意味着他的還原和移動更加方便
  • 可以很容易的進行容災,以遠程的方式傳輸到文件服務中進行備份
  • 恢復速度相比AOF更加快速

缺點:

  • 如果在備份的過程中,出現爲錯誤,最後一次備份的數據就會丟失
  • 在備份的時候,是通一個子進程進行備份的,在數據量較大的時候對會服務器產生壓力
  • 他是一個間隔時間的備份,所以不是實施的對數據進行備份

配置RDB備份
打開核心配置文件(redis.conf),找到SNAPSHOTTING的區域

#保存規則,如果發生了一次key的變化,那麼900秒之後就會備份一次
save 900 1
#如果發生了十次key的變化,那麼300秒之後就會備份一次
save 300 10
#如果發生了一萬次key的變化,那麼60秒之後就會備份一次
save 60 10000
#默認保存的名稱
dbfilename dump.rdb
#保存的位置
dir /usr/local/redis/working

(2)AOF

以日誌的方式存在,地方進行寫操作的時候就會觸發AOF
優勢:

  • 可以使得redis,更加持久健壯
  • 使用日誌的形式追加,如果磁盤滿了 或者其他原因出現問,可以很簡單的進行修復
  • 可以進行重寫,減少日誌的大小
  • 包含所有的寫操作,可以記錄下所有的信息

缺點:

  • 日誌文件相對較大
  • 在超高併發的時候會影響機器的io性能
  • AOF不會根據之前的老舊數據進行重構

配置AOF備份
打開核心配置文件(redis.conf),找到APPEND ONLY MODE的區域

#開啓AOF
appendonly yes
#備份文件名稱
appendfilename "appendonly.aof"
#同步策略
#一共有三種 always(每次寫操作),everysec(每秒),no(關閉)
appendfsync always
#重寫日誌的時候是否將新的操作同步到日誌中,建議用no
no-appendfsync-on-rewrite no
#重寫日誌的觸發添加
#當文件大小超過上次文件備份之後大小的100%後,觸發
auto-aof-rewrite-percentage 100
#當文件小於指定大小時,不觸重寫操作
auto-aof-rewrite-min-size 64mb

恢復的時候上一次誤刪除的數據,只需要刪除AOF日誌中的最後一條命令,然後重啓就可以了

3、內存清理策略

  在redis中如果key設置了過期時間,並且key已經過期,此時已經無法查詢value了,但是這個數據依舊會被保留在內存中,這是因爲redis的內存清理策略造成的

(1)定時刪除(主動)

定時隨機的檢查的key,如果過期則清理刪除。
在redis.conf中修改

每秒隨機抽取10個key進行檢查
hz 10
(2)惰性刪除(被動)

  當客戶端請求一個已經過期的key的時候,那麼redis會檢查這個key是否過期,如果過期了,則刪除,然後返回一個nil。這種策略對cpu比較友好,不會有太多的損耗,但是內存佔用會比較高

4、內存佔滿的清理策略

在redis.conf中添加

#當redis的內存超過這個值的時候會真正釋放掉redis中的某些鍵值對(單位:bytes)
maxmemory 4096
#內存滿載的清理策略 默認爲noeviction
maxmemory_policy noeviction
noeviction:舊緩存永不過期,新緩存設置不了,返回錯誤 
allkeys-lru:清除最少用的舊緩存,然後保存新的緩存(推薦使用)
allkeys-random:在所有的緩存中隨機刪除(不推薦)
volatile-lru:在那些設置了expire過期時間的緩存中,清除最少用的舊緩存,然後保存新的緩存 
volatile-random:在那些設置了expire過期時間的緩存中,隨機刪除緩存 
volatile-ttl:在那些設置了expire過期時間的緩存中,刪除即將過期的

七、主從複製(讀寫分離)

準備3臺裝有redis的機器,1臺作爲master,2臺作爲slave

1、查看redis當前的狀態

進入redis-cli,登錄後輸入

info replication

顯示以下數據

#這臺機器爲master
role:master
#連接的從機個數
connected_slaves:0
master_replid:505dd5ab28e221e8fdff546a1ebbfb4219147693
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

2、將兩臺機器修改爲從機

主機是不需要修改的,我們只需要核心配置文件(redis.conf)將其設置爲從機並且連接到主機上就可以了

(1)打開配置文件找到REPLICATION區域
vim redis.conf
/REPLICATION
(2)配置連接的主機地址

在模塊的任意位置(實際上在配置文件的任意位置上都可以),添加配置

#連接master的地址和端口 replicaof <masterip> <masterport>
replicaof 192.168.85.200 6379
# #連接master的密碼 masterauth <master-password>
masterauth 123456

找下面的配置並修改

#從機的數據設置爲只讀
replica-read-only yes
(3)重啓redis

重啓之後當前的這個redis就會變成從節點連接上master,並且同步master的數據,如果master上有數據且數據量較大,可能會有點慢

(4)再次查看redis的狀態

進入redis-cli,登錄後輸入

info replication

顯示以下數據

#這裏已經變從節點了
role:slave
#連接的主機地址
master_host:192.168.85.200
#連接的主機端口
master_port:6379
master_link_status:up
master_last_io_seconds_ago:7
master_sync_in_progress:0
slave_repl_offset:252
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:82067392216d7336830be1a9e27ad0a6a188bb2a
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:252
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:252
(5)另外一臺機器也同樣配置

redis的從節點不宜太多,不然可能會佔用過多的網絡帶寬和性能
一般有三種配法
1、1主1從
2、1主2從(現在的公司一般都使用這個)
3、在1主2從的基礎上給從節點再增加從節點

(6)測試

在主節點上添加數據,查看兩個從節點上數據是否同步

3、無磁盤化複製(試用功能)

  在配置好從機之後,重啓redis,從機會到master上覆制數據文件,實際上就是master提供一個RDB給從機進行下載,是下載到在硬盤上的,如果數據量較大,且是內網環境的時候,普通的磁盤可能會限制傳輸速度。此時我們就需要同到無磁盤戶複製,讓redis將這個過程從硬盤上移到內存上

(1)打開核心配置文件修改參數
#啓動無磁盤化複製
repl-diskless-sync yes
#等待時間 單位秒
repl-diskless-sync-delay 5

八、哨兵模式(sentinel)

1、什麼是哨兵

  redis在主從分離的模式下,如果master發生了宕機,redis自己是不會像其他的中間件一樣自動的推選出新的master的,這是就需要哨兵進程的幫助。

  哨兵(Sentinel)是用於監控Redis集羣中Master狀態的工具,是 Redis 高可用解決方案,哨兵可以監視一個或者多個redis master服務,以及這些master服務的所有從服務;當某個master服務宕機後,會把這個master下的某個從服務升級爲master來替代已宕機的master繼續工作。

2、配置哨兵

在redis的根目錄下找到sentinel.conf,修改其配置
有些配置是需要自己添加或者去掉註解的

#這個配置和redis.conf是一樣,綁定的ip
bind 127.0.0.1
#是否啓動保護模式,如果啓動了保護模式那麼只有綁定的ip才能操作redis
protected-mode no
#端口號
port 26379
#是否後臺運行
daemonize yes
#哨兵進程的保存文件
pidfile /var/run/redis-sentinel.pid

#哨兵的日誌
logfile /usr/local/redis/sentinel/redis-sentinel.log

#工作路徑
dir /tmp

#連接master的配置
#sentinel moitor <master的別名> <master的地址> <master端口> <故障轉移觸發的哨兵數量>
#master的別名:只能使用A-z,0-9,“.-_”這些字符
#故障轉移觸發的哨兵數量:例如部署了5個哨兵對這個master進行監控,當有2個哨兵發現master沒有響應的時候,開始進行故障轉移
sentinel monitor master-200 192.168.85.200 6479 2

#master登錄密碼
sentinel auth-pass master-200 123456
# master被sentinel認定爲失效的間隔時間 
sentinel down-after-milliseconds master-200 30000
#剩餘的slaves重新和新的master做同步的並行個數 
sentinel parallel-syncs master-200 1
# 主備切換的超時時間,哨兵要去做故障轉移,這個時候哨兵也是一個進程,如果他沒有去執行,超過這個時間後,會由其他的哨兵來處理 
sentinel failover-timeout master-200 180000

3、啓動哨兵

之前配置了日誌文件位置的話 需要創建對應的文件夾

redis-sentinel sentinel.conf

4、一些其他細節

  • 多個哨兵監控同一個主節點的時候,會自動變成集羣
  • 哨兵節點至少3個或者奇數個,故障轉移觸發的哨兵數量一定是一半+1個,這樣避免哨兵的主觀判斷
  • 哨兵最好分佈在不同的服務器節點上
  • 一組哨兵最好只監聽一組主從
  • 當主節點宕機後,哨兵會在從節點中推選一個做爲主節點
  • 當之前的主節點重新上線後,會以從節點的身份加入集羣

5、原Master恢復後不同步問題

  如果你仔細的操作個上面的配置,會發現原來的Master恢復成Slave後,狀態爲master_link_status:down,實際上是因爲我們沒有配置它的masterauth屬性,所以只需要修改redis.conf中的masterauth爲對應的密碼即可,這裏爲123456。

(1)一般master數據無法同步給slave的方案檢查爲如下:
  • 網絡通信問題,要保證互相ping通,內網互通。
  • 關閉防火牆,對應的端口開發(虛擬機中建議永久關閉防火牆,雲服務器的話需要保證內網互通)。
  • 統一所有的密碼,不要漏了某個節點沒有設置。

6、哨兵信息檢查

在redis-cli中可以使用命令讓哨兵查看各節點的信息

# 查看master節點信息 
sentinel master <masterName> 
# 查看slaves節點信息 
sentinel slaves <masterName> 
# 查看哨兵節點信息 
sentinel sentinels <masterName> 

7、SpringBoot整合哨兵

(1)配置SpringBoot

原先spring整合單機單節的配置

spring:
    redis:
        database: 1
        host: 192.168.85.200
        port: 6379
        password: 123456

將配置修改成哨兵的模式

spring:
    redis:
        database: 1
        password: 123456
        sentinel:
            #master的別名
            master: master-200
            #所有的哨兵節點
            nodes: 192.158.85.200:26379,192.158.85.201:26379,192.158.85.202:26379
(2)爲什麼需要通過哨兵模式進行連接
  • 當主節點宕機之後,如果使用原先的單節點連接的方法,就無法在使用redis
  • 原先的配置並沒有做到讀寫分離,還是單節的讀寫,從節點壓根沒有使用上

8、哨兵模式的缺點

  在主從複製的基礎上,哨兵引入了主節點的自動故障轉移,進一步提高了Redis的高可用性;但是哨兵的缺陷同樣很明顯:哨兵無法對從節點進行自動故障轉移,在讀寫分離場景下,從節點故障會導致讀服務不可用,需要我們對從節點做額外的監控、切換操作。
  此外,哨兵仍然沒有解決寫操作無法負載均衡、及存儲能力受到單機限制的問題;這些問題的解決需要使用集羣。

九、Redis集羣

1、Redis-Cluster 集羣

  主從複製以及哨兵,他們可以提高讀的併發,但是單個master容量有限,數據達到一定程度會有瓶頸,這個時候可以通過水平擴展爲多master-slave成爲集羣。支持海量數據,實現高可用與高併發。哨兵模式其實也是一種集羣,他能夠提高讀請求的併發,但是容錯方面可能會有一些問題,比如master同步數據給slave的時候,這其實是異步複製,這個時候master掛了,那麼slave上的數據就沒有master新,數據同步需要時間的,1-2秒的數據會丟失。master恢復並轉換成slave後,新數據則丟失。

特點
  • 每個節點知道彼此之間的關係,也會知道自己的角色,當然他們也會知道自己存在與一個集羣環境中,他們彼此之間可以交互和通信,比如ping pong。那麼這些關係都會保存到某個配置文件中,每個節點都有,這個我們在搭建的時候會做配置的。
  • 客戶端要和集羣建立連接的話,只需要和其中一個建立關係就行。
  • 某個節點掛了,也是通過超過半數的節點來進行的檢測,客觀下線後主從切換,和我們之前在哨兵模式中提到的是一個道理。
  • Redis中存在很多的插槽,又可以稱之爲槽節點,用於存儲數據
集羣容錯

  構建Redis集羣,需要至少3個節點作爲master,以此組成一個高可用的集羣,此外每個master都需要配備一個slave,所以整個集羣需要6個節點,這也是最經典的Redis集羣,也可以稱之爲三主三從,容錯性更佳。所以在搭建的時候需要有6臺虛擬機。請各自準備6臺虛擬機,可以通過克隆去構建,使用單實例的Redis 去克隆即可,如果之前配置了主從或是哨兵建議刪除 。
在這裏插入圖片描述

2、配置三主三從集羣

這裏演示是新版本的集羣搭建,老版本需要使用redis-trib.rb這裏不做演示,使用機器的配置如下

主從一 主從二 主從三
主節點 192.168.85.200 192.168.85.201 192.168.85.202
從節點 192.168.85.203 192.168.85.204 192.168.85.205
(1)修改集羣配置文件(這裏的配置是所有的節點都要設置的)

配置前保證redis是關閉的
在redis的核心配置文件(redis.conf)裏面找到REDIS CLUSTER區域

#啓動Redis-Cluster 集羣
cluster-enabled yes

#每個節點的配置文件
#這裏保存着對應節點的配置信息,和其他節點的對應關係
#這是redis自己進行管理的,不需要我們進行干預,只需要打開原本的註釋就行了
cluster-config-file nodes-6379.conf

#節點超時的時間 (單位:秒)
cluster-node-timeout 5000

#開啓AOF持久化模式
appendonly yes

如果配置出現問題,可以刪除掉原先的AOF持久化文件,但是要注意文件刪除,持久化的數據也就消失了

(2)使用redis-cli配置集羣

進入redis目錄裏的src文件夾
每一個節點之間使用空格分開

redis-cli -a 密碼 --cluster create 192.168.85.200:6379 192.168.85.201:6379 192.168.85.202:6379 192.168.85.203:6379 192.168.85.204:6379 192.168.85.205:6379 --cluster-replicas 1

執行後,會打印出集羣配置信息,如果沒有問題就輸入yes,回車

(3)檢測集羣信息
redis-cli --cluster check 192.168.85..200:6380
(4)連接集羣中的某個節點
redis-cli -c -a 密碼 -h 節點ip -p 節點端口

#連接後,查看當前節點信息
cluster info
#查看所有節點信息
cluster nodes

3、slots槽節點

當我們查詢集羣信息的時候,可以看到這麼一段話

[OK] All 16384 slots covered

這裏的意思就是 一共有16384個槽節點,那麼什麼是槽節點

(1)slot是怎麼分配的

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-hOQnhwV1-1577677344717)(en-resource://database/2525:1)]
這裏可以看到3個master是平分了所有槽節點,而從節點是沒有槽節點的

(2)slot是怎麼存儲數據的

  當來了一條數據,redis就會對數據的key進行計算 "hash(key)%槽數量 "這樣就可以算出一個對應的slot,經過計算所有值相同的數據都會被完整的保存在這個slot中。如果在200的機器上查詢keys * 是查看不到其他slot裏的數據,但是get [key]是會自動切換節點查詢到的
  當然存入主節點的時候,同時會複製一份到對應的從節點上,當主節點宕機後,從節點就會接替主節點的位置。

4、SpringBoot整合Redis-Cluster集羣

spring:
    redis:
        password: 123456
        cluster:
            #所有的節點,nodes後面是不換行的,頁面上顯示的有問題
            nodes: 192.158.85.200:6379,192.158.85.201:6379,192.158.85.202:6379,192.158.85.203:6379,192.158.85.204:6379,192.158.85.205:6379

十、Redis緩存穿透與雪崩

1、緩存穿透的解決方案

(1)什麼是緩存穿透

  緩存穿透是指查詢一個一定不存在的數據,由於緩存是不命中時需要從數據庫查詢,查不到數據則不寫入緩存,這將導致這個不存在的數據每次請求都要到數據庫去查詢,造成緩存穿透。

(2)緩存穿透解決方案

緩存空對象. 將 null 變成一個值.
  可以採用一個簡單粗暴的方法,如果一個查詢返回的數據爲空(不管是數據不存在,還是系統故障),我們仍然把這個空結果進行緩存,但它的過期時間會很短,最長不超過五分鐘。
緩存空對象會有兩個問題:
  第一,空值做了緩存,意味着緩存層中存了更多的鍵,需要更多的內存空間 ( 如果是攻擊,問題更嚴重 ),比較有效的方法是針對這類數據設置一個較短的過期時間,讓其自動剔除。
  第二,緩存層和存儲層的數據會有一段時間窗口的不一致,可能會對業務有一定影響。例如過期時間設置爲5分鐘,如果此時存儲層添加了這個數據,那此段時間就會出現緩存層和存儲層數據的不一致,此時可以利用消息系統或者其他方式清除掉緩存層中的空對象。

布隆過濾
  對所有可能查詢的參數以hash形式存儲,在控制層先進行校驗,不符合則丟棄。還有最常見的則是採用布隆過濾器,將所有可能存在的數據哈希到一個足夠大的bitmap中,一個一定不存在的數據會被這個bitmap攔截掉,從而避免了對底層存儲系統的查詢壓力。
布隆過濾器的問題:

  • 當數據從數據庫和redis中刪除的時候,無法從布隆過濾器中無法刪除的
  • 誤判率,由於布隆過濾器的本身的運行機制,會導致,對於某些key 在默寫情況下出現誤判,把一個不存在的值,判斷成存在

2、布隆過濾器

  布隆過濾器的原理是,當一個元素被加入集合時,通過K個散列函數將這個元素映射成一個位數組中的K個點,把它們置爲1。檢索時,我們只要看看這些點是不是都是1就(大約)知道集合中有沒有它了:如果這些點有任何一個0,則被檢元素一定不在;如果都是1,則被檢元素很可能在。這就是布隆過濾器的基本思想。

  bloom filter之所以能做到在時間和空間上的效率比較高,是因爲犧牲了判斷的準確率、刪除的便利性

  • 存在誤判,可能要查到的元素並沒有在容器中,但是hash之後得到的k個位置上值都是1。如果bloom filter中存儲的是黑名單,那麼可以通過建立一個白名單來存儲可能會誤判的元素。
  • 刪除困難。一個放入容器的元素映射到bit數組的k個位置上是1,刪除的時候不能簡單的直接置爲0,可能會影響其他元素的判斷。可以採用Counting Bloom Filter

3、緩存雪崩與預防

(1)什麼是緩存雪崩

緩存擊穿
  緩存擊穿是指緩存中沒有但數據庫中有的數據(一般是緩存時間到期),這時由於併發用戶特別多,同時讀緩存沒讀到數據,又同時去數據庫去取數據,引起數據庫壓力瞬間增大,造成過大壓力
緩存雪崩
  緩存雪崩是指緩存中數據大批量到過期時間,而查詢數據量巨大,引起數據庫壓力過大甚至down機。和緩存擊穿不同的是,緩存擊穿指併發查同一條數據,緩存雪崩是不同數據都過期了,很多數據都查不到從而查數據庫

(2)解決方案

緩存擊穿

  • 設置熱點數據永遠不過期
  • 加互斥鎖

緩存雪崩

  • 緩存數據的過期時間設置隨機,防止同一時間大量數據過期現象發生。
  • 如果緩存數據庫是分佈式部署,將熱點數據均勻分佈在不同搞得緩存數據庫中。
  • 設置熱點數據永遠不過期。
  • 多緩存結合(使用Redis做分佈式緩存,memcache做單節點緩存)
  • 使用第三方Redis 例如:阿里雲和騰訊雲

十一、批量查詢優化

1、MultiGet(速度快,開銷小)

對於需要一次性獲取多個key的value時,例如下面代碼

public Object getAlot(String... keys){
    List<String> resutl = new ArrayList<>();
    for(String k:keys){
        resutl.add(redisOperator.get(k));
    }
    return resutl;
}

修改成使用MultiGet,對應的就是mget命令

public Object getAlot(String... keys){
    List<String> keysList = Arrays.asList(keys);
    return redisTemplate.opsForValue().multGet(keys);
}

2、pipeline(更加靈活)

每次對redis發起請求,都需要創建相互的鏈接,這樣就有額外的開銷,使用pipeline建立管道,進行優化

public List<Object> getAlot(String... keys){
     List<Object> resutl = redisTemplate.executePipelined(new RedisCallback<String>() {
        @Override
        public String doInRedis(RedisConnection connection) throws DataAccess{
            StringRedisConnection src = (StringRedisConnection)connection;
            for(String k:keys){
                src.get(k);
            }
            return null;
        }
    });
    return resutl;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章