redis
一、redis的作用和使用場景
redis是一個內存級的高速緩存數據庫。(對比磁盤IO)
使用場景:1、并發訪問量大的 2、數據量小 3、修改不頻繁
項目中:1、驗證碼 2、登錄成功用戶信息 3、首頁(模塊數據 輪播圖,商品分類,熱點模塊,推薦模塊) 4、購物車(商品信息)
二、redis支持的數據類型有哪些?
目前支持10種,常用的有5種:String、list、set、zset、hash(map)、stream
三、redis的常用命令
通用命令
每種數據類型各自的命令
string類型
設置過期時間1、set k1 v1 ex 1002、setex k1 100 v23、set k1 v1 expire k1 100
list類型 (有序,可以重復)
1.94.230.82:6381> lpush addressList hangzhou zhengzhou guangzhou
(integer) 3
1.94.230.82:6381> llen addressList
(integer) 3
1.94.230.82:6381> lrange addressList 0 -1
1) "guangzhou"
2) "zhengzhou"
3) "hangzhou"
set類型(無序、不可重復)
1.94.230.82:6381> sadd subjects java c java
(integer) 2
1.94.230.82:6381> sadd subjects c
(integer) 0
1.94.230.82:6381> sadd subjects mysql
(integer) 1
1.94.230.82:6381> smembers subjects
1) "mysql"
2) "java"
3) "c"
1.94.230.82:6381> sadd subjects py
(integer) 1
1.94.230.82:6381> smembers subjects
1) "mysql"
2) "java"
3) "py"
4) "c"
hash類型(map類型 : 存儲map名 key value 存儲對象:對象名 屬性名 屬性值)
1.94.230.82:6381> hset user id 111
(integer) 1
1.94.230.82:6381> hset user name zhangsan
(integer) 1
1.94.230.82:6381> hset user age 20
(integer) 1
1.94.230.82:6381> hset user age 22
(integer) 0
1.94.230.82:6381> hget user id
"111"
1.94.230.82:6381> hget user name
"zhangsan"
1.94.230.82:6381> hget user age
"22"
1.94.230.82:6381> hdel user name
(integer) 1
1.94.230.82:6381> hkeys user
1) "id"
2) "age"
1.94.230.82:6381> hvals user
1) "111"
2) "22"
zset 有序的set集合 有序,不能重復 (給每一個value值,添加一個數值,通過數值保持有序)
1.94.230.82:6381> zadd sbs 1 java 2 c 3 py
(integer) 3
1.94.230.82:6381> zrange sbs 0 -1
1) "java"
2) "c"
3) "py"
1.94.230.82:6381> zadd sbs 10 html
(integer) 1
1.94.230.82:6381> zrange sbs 0 -1
1) "java"
2) "c"
3) "py"
4) "html"
1.94.230.82:6381> zadd sbs 5 c#
(integer) 1
1.94.230.82:6381> zrange sbs 0 -1
1) "java"
2) "c"
3) "py"
4) "c#"
5) "html"
四、redis的持久化機制
rdb:默認 快照模式
定時同步(1min 10000、5min 10、15min 1)
可能造成最后一次同步之后的數據丟失
性能高(新建一個進程 執行數據同步操作,不影響主進程)
dump.rdb 本地磁盤文件(實時數據)
恢復速度快
aof:日志追加模式 日志文件大
rdb文件 全量
追加命令 到 aof日志文件
恢復慢 rdb+aof 逐個命令執行,恢復
更安全,丟失的數據更少
同步機制(no(30s) always everySec)
五、數據的刪除策略
定時刪除 定時器刪除(根據設置的過期時間)
定期刪除 根據配置文件參數hz值,60/hz 循環檢測各個倉庫刪除(抽取w個值進行檢測,根據過期key的百分比 25% 循環刪除)
惰性刪除 過期后不立即刪除。(使用時判斷,過期刪除)
六、數據的淘汰策略
volatile- lru lfu ttl random
allkeys- lru lfu random
no 放棄驅逐
七、事務機制
multi exec discard watch unwatch
事務的特點:
是否滿足原子性: 不滿足
八、鎖機制
樂觀鎖: watch unwatch
悲觀鎖:setnx 分布式鎖 setnx(key,value) 1 執行代碼 ---- del key
九、發布訂閱機制
publish 頻道 message
subscribe 頻道
十、一主二從(高可用)
實現讀寫分離、主從復制、主機執行讀寫,從機執行讀取。
主從切換
手動切換: slaveof no one slaveof ip port
自動切換: 哨兵模式
哨兵
哨兵的作用是監控 redis系統的運行狀況,他的功能如下:
監控主從數據庫是否正常運行
master出現故障時,自動將slave轉化為master
多哨兵配置的時候,哨兵之間也會自動監控
多個哨兵可以監控同一個redis
哨兵工作機制
./redis-sentinel sentinel.conf
哨兵進程啟動時會讀取配置文件的內容,通過sentinel monitor master-name ip port quorum查找到master的ip端口。一個哨兵可以監控多個master數據庫,只需要提供多個該配置項即可。
配置文件還定義了與監控相關的參數,比如master多長時間無響應即即判定位為下線。
哨兵啟動后,會與要監控的master建立倆條連接:
一條連接用來訂閱master的sentinel:hello頻道與獲取其他監控該master的哨兵節點信息
另一條連接定期向master發送INFO等命令獲取master本身的信息
與master建立連接后,哨兵會執行三個操作,這三個操作的發送頻率都可以在配置文件中配置:
定期向master和slave發送INFO命令
定期向master和slave的sentinel:hello頻道發送自己的信息
定期向master、slave和其他哨兵發送PING命令
這三個操作的意義非常重大,發送INFO命令可以獲取當前數據庫的相關信息從而實現新節點的自動發現。所以說哨兵只需要配置master數據庫信息就可以自動發現其slave信息。獲取到slave信息后,哨兵也會與slave建立倆條連接執行監控。通過INFO命令,哨兵可以獲取主從數據庫的最新信息,并進行相應的操作,比如角色變更等。
接下來哨兵向主從數據庫的sentinel:hello頻道發送信息與同樣監控這些數據庫的哨兵共享自己的信息,發送內容為哨兵的ip端口、運行id、配置版本、master名字、master的ip端口還有master的配置版本。這些信息有以下用處:
其他哨兵可以通過該信息判斷發送者是否是新發現的哨兵,如果是的話會創建一個到該哨兵的連接用于發送PING命令。
其他哨兵通過該信息可以判斷master的版本,如果該版本高于直接記錄的版本,將會更新
當實現了自動發現slave和其他哨兵節點后,哨兵就可以通過定期發送PING命令定時監控這些數據庫和節點有沒有停止服務。發送頻率可以配置,但是最長間隔時間為1s,可以通過sentinel down-after-milliseconds mymaster 600設置。
如果被ping的數據庫或者節點超時未回復,哨兵認為其主觀下線。如果下線的是master,哨兵會向其他哨兵點發送命令詢問他們是否也認為該master主觀下線,如果達到一定數目(即配置文件中的quorum)投票,哨兵會認為該master已經客觀下線,并選舉領頭的哨兵節點對主從系統發起故障恢復。
如上文所說,哨兵認為master客觀下線后,故障恢復的操作需要由選舉的領頭哨兵執行,選舉采用Raft算法:
發現master下線的哨兵節點(我們稱他為A)向每個哨兵發送命令,要求對方選自己為領頭哨兵
如果目標哨兵節點沒有選過其他人,則會同意選舉A為領頭哨兵
如果有超過一半的哨兵同意選舉A為領頭,則A當選
如果有多個哨兵節點同時參選領頭,此時有可能存在一輪投票無競選者勝出,此時每個參選的節點等待一個隨機時間后再次發起參選請求,進行下一輪投票精選,直至選舉出領頭哨兵
選出領頭哨兵后,領頭者開始對進行故障恢復,從出現故障的master的從數據庫中挑選一個來當選新的master,選擇規則如下:
所有在線的slave中選擇優先級最高的,優先級可以通過slave-priority配置
如果有多個最高優先級的slave,則選取復制偏移量最大(即復制越完整)的當選
如果以上條件都一樣,選取id最小的slave
挑選出需要繼任的slave后,領頭哨兵向該數據庫發送命令使其升格為master,然后再向其他slave發送命令接受新的master,最后更新數據。將已經停止的舊的master更新為新的master的從數據庫,使其恢復服務后以slave的身份繼續運行。
十一、集群模式(高可用)
3主6從
創建redis目錄和配置文件 由于這里需要配置6個redis實例的配置文件,所以在這里,可以先創建一個模板配置文件,之后使用代碼批量替換的方式來創建每個redis的配置文件。
創建模板文件
創建目錄
mkdir -p /data/docker_redis/
?
編寫redis_cluster.conf.template文件
?
vi redis_cluster.conf.template
port ${PORT}
requirepass 1234
masterauth 1234
protected-mode no
daemonize no
appendonly yes
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 82.157.9.251
cluster-announce-port ${PORT}
cluster-announce-bus-port 1${PORT}
在這里需要講解2個參數 cluster-announce-port:此端口為redis提供服務端口,用于應用客戶端連接 cluster-announce-bus-port:此端口用于redis集群進行故障檢測、配置更新、故障轉移授權和內部通訊使用,不用于應用客戶端連接使用。
創建6個redis實例的數據目錄,配置文件目錄和配置文件
for port in `seq 8001 8006`; do \mkdir -p /data/redis_data/${port}/conf \&& PORT=${port} envsubst < /data/docker_redis/redis_cluster.conf.template > /data/redis_data/${port}/conf/redis.conf \&& mkdir -p /data/redis_data/${port}/data;\
done
批量創建容器 在這里使用for循環批量創建redis容器,代碼如下所示
for port in $(seq 8001 8006); do \docker run -id -p ${port}:${port} -p 1${port}:1${port} --restart always --name redis-${port} \-v /data/redis_data/${port}/conf/redis.conf:/usr/local/etc/redis/redis.conf \-v /data/redis_data/${port}/data:/data \redis:7.0.2 redis-server /usr/local/etc/redis/redis.conf; \
done
?
開放防火墻端口:(云服務器端口也要開放)
sudo firewall-cmd --permanent --add-port=8001-8006/tcp
sudo firewall-cmd --permanent --add-port=18001-18006/tcp
?
啟動容器:
for port in $(seq 8001 8006); do \docker start redis-${port} ;\
done
在這里使用-v參數,讓容器的redis配置參數和數據全部使用宿主機的文件目錄。這樣做的好處是,保證redis的數據不丟失。
查看剛剛創建的redis容器
[root@mysql data]# docker ps
CONTAINER ID ? ? ? IMAGE ? ? ? ? ? ? ? COMMAND ? ? ? ? ? ? ? ? CREATED ? ? ? ? ? ? STATUS ? ? ? ? ? ? PORTS ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? NAMES
f4c971ce2d84 ? ? ? redis:7.0.2-buster ? "docker-entrypoint.s鈥 6 seconds ago ? ? ? Up 5 seconds ? ? ? 0.0.0.0:8006->8006/tcp, 6379/tcp, 0.0.0.0:18006->18006/tcp ? redis-8006
1b5a8b0a1c93 ? ? ? redis:7.0.2-buster ? "docker-entrypoint.s鈥 7 seconds ago ? ? ? Up 6 seconds ? ? ? 0.0.0.0:8005->8005/tcp, 6379/tcp, 0.0.0.0:18005->18005/tcp ? redis-8005
35d8fe01fc71 ? ? ? redis:7.0.2-buster ? "docker-entrypoint.s鈥 7 seconds ago ? ? ? Up 6 seconds ? ? ? 0.0.0.0:8004->8004/tcp, 6379/tcp, 0.0.0.0:18004->18004/tcp ? redis-8004
408e5302378a ? ? ? redis:7.0.2-buster ? "docker-entrypoint.s鈥 8 seconds ago ? ? ? Up 7 seconds ? ? ? 0.0.0.0:8003->8003/tcp, 6379/tcp, 0.0.0.0:18003->18003/tcp ? redis-8003
c1c23c543233 ? ? ? redis:7.0.2-buster ? "docker-entrypoint.s鈥 8 seconds ago ? ? ? Up 7 seconds ? ? ? 0.0.0.0:8002->8002/tcp, 6379/tcp, 0.0.0.0:18002->18002/tcp ? redis-8002
c752fab6c6b8 ? ? ? redis:7.0.2-buster ? "docker-entrypoint.s鈥 9 seconds ago ? ? ? Up 8 seconds ? ? ? 0.0.0.0:8001->8001/tcp, 6379/tcp, 0.0.0.0:18001->18001/tcp ? redis-8001
創建redis-cluster集群 6個redis容器創建好之后,選擇其中的一個容器進入,進行redis-cluster集群創建
[root@mysql bin]# docker exec -it redis-8001 /bin/bash
root@f4c971ce2d84:~# redis-cli -a 1234 --cluster create 82.157.9.251:8001 82.157.9.251:8002 82.157.9.251:8003 82.157.9.251:8004 82.157.9.251:8005 82.157.9.251:8006 --cluster-replicas 1
回車后顯示:
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 82.157.9.251:8005 to 82.157.9.251:8001
Adding replica 82.157.9.251:8006 to 82.157.9.251:8002
Adding replica 82.157.9.251:8004 to 82.157.9.251:8003
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: c9e549f4d04d4466d2550d1e027412304099b1f2 82.157.9.251:8001slots:[0-5460] (5461 slots) master
M: 0956927ee74737d5ff91a1885e77f94d531dab76 82.157.9.251:8002slots:[5461-10922] (5462 slots) master
M: d6321788b2717b4142390c158ba70ca758f70964 82.157.9.251:8003slots:[10923-16383] (5461 slots) master
S: 96d2cb55f163ecc13a714ba01d90348c1c3ad02f 82.157.9.251:8004replicates c9e549f4d04d4466d2550d1e027412304099b1f2
S: 8a771c26a95e82d9a6818e372d7c0226937670ac 82.157.9.251:8005replicates 0956927ee74737d5ff91a1885e77f94d531dab76
S: aa7c2f6173904973f041b35efc5200359188eb0f 82.157.9.251:8006replicates d6321788b2717b4142390c158ba70ca758f70964
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
.
>>> Performing Cluster Check (using node 82.157.9.251:8001)
M: c9e549f4d04d4466d2550d1e027412304099b1f2 82.157.9.251:8001slots:[0-5460] (5461 slots) master1 additional replica(s)
M: 0956927ee74737d5ff91a1885e77f94d531dab76 82.157.9.251:8002slots:[5461-10922] (5462 slots) master1 additional replica(s)
S: 96d2cb55f163ecc13a714ba01d90348c1c3ad02f 82.157.9.251:8004slots: (0 slots) slavereplicates c9e549f4d04d4466d2550d1e027412304099b1f2
S: aa7c2f6173904973f041b35efc5200359188eb0f 82.157.9.251:8006slots: (0 slots) slavereplicates d6321788b2717b4142390c158ba70ca758f70964
S: 8a771c26a95e82d9a6818e372d7c0226937670ac 82.157.9.251:8005slots: (0 slots) slavereplicates 0956927ee74737d5ff91a1885e77f94d531dab76
M: d6321788b2717b4142390c158ba70ca758f70964 82.157.9.251:8003slots:[10923-16383] (5461 slots) master1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
上述命令執行之后,redis-cluster集群就搭建好了
在主機上測試:
模仿某一節點故障:
在cmd窗口:
重啟:docker start redis-8001
下面是整個redis-cluster集群的架構圖
redis-cluster集群狀態檢查 檢查redis-cluster集群狀態
root@f4c971ce2d84:~# redis-cli -a 1234 --cluster check 1.94.230.82:8001
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
82.157.9.251:8001 (c9e549f4...) -> 0 keys | 5461 slots | 1 slaves.
82.157.9.251:8002 (0956927e...) -> 0 keys | 5462 slots | 1 slaves.
82.157.9.251:8003 (d6321788...) -> 0 keys | 5461 slots | 1 slaves.
[OK] 0 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 82.157.9.251:8001)
M: c9e549f4d04d4466d2550d1e027412304099b1f2 82.157.9.251:8001slots:[0-5460] (5461 slots) master1 additional replica(s)
M: 0956927ee74737d5ff91a1885e77f94d531dab76 82.157.9.251:8002slots:[5461-10922] (5462 slots) master1 additional replica(s)
S: 96d2cb55f163ecc13a714ba01d90348c1c3ad02f 82.157.9.251:8004slots: (0 slots) slavereplicates c9e549f4d04d4466d2550d1e027412304099b1f2
S: aa7c2f6173904973f041b35efc5200359188eb0f 82.157.9.251:8006slots: (0 slots) slavereplicates d6321788b2717b4142390c158ba70ca758f70964
S: 8a771c26a95e82d9a6818e372d7c0226937670ac 82.157.9.251:8005slots: (0 slots) slavereplicates 0956927ee74737d5ff91a1885e77f94d531dab76
M: d6321788b2717b4142390c158ba70ca758f70964 82.157.9.251:8003slots:[10923-16383] (5461 slots) master1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
檢查redis集群節點信息
root@f4c971ce2d84:~# redis-cli -c -a 1234 -h 82.157.9.251 -p 8001
82.157.9.251:8001> cluster nodes
0956927ee74737d5ff91a1885e77f94d531dab76 82.157.9.251:8002@18002 master - 0 1603265855374 2 connected 5461-10922
96d2cb55f163ecc13a714ba01d90348c1c3ad02f 82.157.9.251:8004@18004 slave c9e549f4d04d4466d2550d1e027412304099b1f2 0 1603265856080 4 connected
aa7c2f6173904973f041b35efc5200359188eb0f 82.157.9.251:8006@18006 slave d6321788b2717b4142390c158ba70ca758f70964 0 1603265855575 6 connected
8a771c26a95e82d9a6818e372d7c0226937670ac 82.157.9.251:8005@18005 slave 0956927ee74737d5ff91a1885e77f94d531dab76 0 1603265855575 5 connected
d6321788b2717b4142390c158ba70ca758f70964 82.157.9.251:8003@18003 master - 0 1603265856382 3 connected 10923-16383
c9e549f4d04d4466d2550d1e027412304099b1f2 82.157.9.251:8001@18001 myself,master - 0 1603265855000 1 connected 0-5460
?
在這里可以很清晰明了的看到有3個master節點和3個slave節點的IP地址和端口信息。
redis-cluster連接測試
redis-cluster搭建好之后,隨意連接一個redis節點,無論是主節點還是從節點都可以,在這里選擇連接一個從節點。
82.157.9.251:8001> cluster nodes
0956927ee74737d5ff91a1885e77f94d531dab76 82.157.9.251:8002@18002 master - 0 1603265855374 2 connected 5461-10922
96d2cb55f163ecc13a714ba01d90348c1c3ad02f 82.157.9.251:8004@18004 slave c9e549f4d04d4466d2550d1e027412304099b1f2 0 1603265856080 4 connected
aa7c2f6173904973f041b35efc5200359188eb0f 82.157.9.251:8006@18006 slave d6321788b2717b4142390c158ba70ca758f70964 0 1603265855575 6 connected
8a771c26a95e82d9a6818e372d7c0226937670ac 82.157.9.251:8005@18005 slave 0956927ee74737d5ff91a1885e77f94d531dab76 0 1603265855575 5 connected
d6321788b2717b4142390c158ba70ca758f70964 82.157.9.251:8003@18003 master - 0 1603265856382 3 connected 10923-16383
c9e549f4d04d4466d2550d1e027412304099b1f2 82.157.9.251:8001@18001 myself,master - 0 1603265855000 1 connected 0-5460
?
?
root@f4c971ce2d84:~# redis-cli -c -a 1234 -h 82.157.9.251 -p 8004
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
82.157.9.251:8004>
82.157.9.251:8004> set 5 test5
-> Redirected to slot [9974] located at 82.157.9.251:8002
82.157.9.251:8002>
1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.
當在slave節點寫入數據時,redis-cluster將你的寫請求重定向到另一個master節點了.
root@f4c971ce2d84:~# redis-cli -c -a 1234 -h 82.157.9.251 -p 8006
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
82.157.9.251:8006>
82.157.9.251:8006> get 5
-> Redirected to slot [9974] located at 82.157.9.251:8002
"test5"
82.157.9.251:8002>
十二、重點題
redis和java結合
redis雪崩、穿透、擊穿
1. redis緩存使用流程
使用Redis緩存數據的流程是:
-
數據查詢首先進行緩存查詢。
-
如果數據存在則直接返回緩存數據。
-
如果數據不存在,就對數據庫進行查詢,并把查詢到的數據放進緩存。
-
如果數據庫查詢數據為空,則不放進緩存。
2. 緩存穿透
2.1. 概念
緩存穿透是指查詢緩存和數據庫中都不存在的數據。比如id為-1的數據。
2.2. 解決方案
-
緩存空值:當第一次請求時,數據不存在 Redis 也不存在數據庫的時候,設置一個缺省值(比如:None)。當后續再次進行查詢則直接返回空值或者缺省值。
//偽代碼 public object GetProductListNew(String key) {//首先查詢redis數據String value = redis.get(key);//如果redis中沒有數據if(value == null){//查詢數據庫中的數據String dbValue = db.get();//如果數據庫中的數據為null,則設置默認值if (dbValue == null) {dbValue = "none";}//將數據存儲到redisredis.set(key,dbValue,50000);//返回return dbvalue;}}else{//第二次查詢數據時,redis有數據,就直接返回return value;} }
-
布隆過濾器:在數據寫入數據庫的同時將這個 ID 同步到到布隆過濾器中,當請求的 id 不存在布隆過濾器中則說明該請求查詢的數據一定沒有在數據庫中保存,就不要去數據庫查詢了。
<dependencies> ?<dependency> ?<groupId>com.google.guava</groupId> ?<artifactId>guava</artifactId> ?<version>23.0</version> ?</dependency> ? </dependencies>
//偽代碼 public class BloomFilterTest {private static final int capacity = 1000000;private static final int key = 999998;private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), capacity);static {for (int i = 0; i < capacity; i++) {bloomFilter.put(i);}}
?public static void main(String[] args) {/*返回計算機最精確的時間,單位微妙*/long start = System.nanoTime();if (bloomFilter.mightContain(key)) {System.out.println("成功過濾到" + key);}long end = System.nanoTime();System.out.println("布隆過濾器消耗時間:" + (end - start));int sum = 0;for (int i = capacity + 20000; i < capacity + 30000; i++) {if (bloomFilter.mightContain(i)) {sum = sum + 1;}}System.out.println("錯判率為:" + sum);} }
3. 緩存雪崩
3.1. 概念
是指在某一個時間段,redis服務器故障或者redis緩存的數據集中全部過期失效或者大量key過期失效,在緩存集中失效的這個時間段對數據的訪問查詢,都落到了數據庫上,對于數據庫而言,就會產生周期性的壓力。
3.2. 解決方案
事前:
redis服務器設置高可用(一主二從或者集群3主3從)
-
隨機設置key失效時間,避免大量key集體失效。
-
不設置過期時間(不推薦)。
-
用加鎖或者隊列的方式保證來保證不會有大量的線程對數據庫一次性進行讀寫。
-
跑定時任務,在緩存失效前刷進新的緩存。
事中:
可以考略多種緩存機制,比如redis緩存+其他緩存機制 spring cache +mysql
事后:
redis崩潰、mysql崩潰,系統崩潰
解決方案:快速恢復系統(重啟mysql、重啟redis服務 快速恢復數據到內存中 rdb aof機制 、重啟java服務)
4. 緩存擊穿
4.1. 概念
指一個Key非常熱點,在不停的扛著大并發,大并發集中對這一個點進行訪問,當這個Key在失效的瞬間,持續的大并發就穿破緩存,直接請求數據庫,就像在一個完好無損的桶上鑿開了一個洞。
4.2. 解決方案
-
預先把熱門數據提前存入 Redis 中,并設熱門數據的過期時間超大值。
-
使用分布式鎖,當發現緩存失效的時候,不是立即從數據庫加載數據。而是先獲取分布式鎖,獲取鎖成功才執行數據庫查詢和寫數據到緩存的操作,獲取鎖失敗,則說明當前有線程在執行數據庫查詢操作,當前線程睡眠一段時間在重試。這樣只讓一個請求去數據庫讀取數據。
//偽代碼 public String get(key) {String value = redis.get(key);if (value == null) { //代表緩存值過期//設置3min的超時,防止del操作失敗的時候,下次緩存過期一直不能load dbif (redis.setnx(key_mutex, 1, 3 * 60) == 1) { ?//代表設置成功value = db.get(key);redis.set(key, value, expire_secs);redis.del(key_mutex);} else { ?//這個時候代表同時候的其他線程已經load db并回設到緩存了,這時候重試獲取緩存值即可sleep(50);get(key); ?//重試}} else {return value; ? ? ?}}
十三、springboot項目和redis的整合
1、spring-boot-starter-data-redis
2、配置redis的連接信息(host、port、password、database)
3、使用redisTemplate模版工具類
4、常見方法
redisTemplate.opsForValue.set(key,value);
redisTemplate.opsForValue.get(key)