持久化
Redis 支持持久化, 其持久化數據有兩種方式. 兩種可以同時使用. 如果同時使用, Reids 在重啟時將使用 AOF 方式來還原數據.
RDB
按照一定策略定時同步內存的數據到磁盤.文件名 dump.rdb
-
snapshot: 快照復制. Redis在指定情況下觸發快照: (1) 按配置的規則;(2) save 或 bgsave 命令執行;(3) flushall 命令; (4)執行復制
-
配置的規則:
save seconds exchange
當在 seconds 指定的時間內, key 的數量更改大於 exchange 時發生快照. -
save 或 bgsave 命令: 執行快照同步操作, 注意
save
這個操作會暫時阻塞客戶端請求.bgsave
則不會阻塞 -
flushall: 清除內存所有數據, 只要規則不為空, redis就會執行快照
-
執行復制:
-
-
快照原理
fork 復制一份當前進程的副本, 這個進程是子進程, 負責同步持久化到磁盤. 而父進程負責處理客戶端請求.
-
快照的優缺點:
- 缺點: 可能會丟失數據, 在下一次快照前宕機
- 優點: 最大化Redis的性能, 父子進程職責分離
AOF
保存命令到磁盤, 也就是持久化日志.
-
配置:
appendonly yes
啟動aof. 默認的文件名是appendonly.aof
. -
配置
auto-aof-rewrite-percentage 100
當 aof 文件與上一次文件的大小相比, 超過配置的百分比就進行重寫 -
配置
auto-aof-rewrite-min-size 64m
限制允許重寫最小 aof 文件大小, 即小於64m時不重寫 -
aof 重寫原理: aof 重寫是安全的. 相當於同時將命令追加到現有的aof文件, 同時寫入新的 aof 臨時文件, 臨時文件最終將覆蓋原 aof 文件.
-
同步磁盤數據: aof機制會將命令記錄到aof文件, 但實際是同步到操作系統的緩存區, 最終由操作系統同步到磁盤. 可以通過下面配置修改策略
appendsync always
每次執行寫入就同步, 安全但影響性能
appendsync everysec
每一秒執行
appendsync no
不執行同步, 由操作系統去執行, 效率高但不安全 -
aof 文件損壞后的修復, 使用
redis-check-aof-fix
集群
master/slave 復制
主從方式, 從是只讀的, slave也可以有自己的slave.
-
slave節點上配置
slaveof masterip masterport
-
配置
slave-serve-stale-data no
可以用來保證數據同步后再做其他操作 -
命令
info replication
可以查看信息. -
實現原理:
- slave 連接到 master 后, 會向 master 發送
SYNC
命令. - master 收到命令后, 會做兩件事(1) 執行bgsave;(2)master 將收到的修改命令存入緩沖區, 再將命令傳輸給slave
- slave 連接到 master 后, 會向 master 發送
-
復制方式:
- 基於rdb文件復制
- 無硬盤復制 配置
rpli-diskless-sync yes
- 增量復制 PSYNC master run id. offset
-
命令
replconf listening-port 6379
可以用來查看復制過程 -
缺點: 無法做master選舉
sentinale 哨兵模式
- 監控
- master選舉
-
配置文件為
sentinel.conf
-
配置節點:
sentinel monitor mymaster 192,168,11,111 6379 2
最后的2為投票數 -
是高可用方案, 但不是高性能方案
集群
-
原理
Reids有slot槽的概念: redis中有16384個. 根據key的 CRC16 算法, 取得的結果與槽數取模.落入的槽的索引是固定的. 然后根據節點數將槽的范圍確定到每個節點上.
當節點新增和刪除時, 節點的槽范圍發生變化, 數據遷移需要人工干預.
-
三方方案
- redis shardding : jedis支持
- codis : 代理, 分片和數據遷移自動化
- twemproxy :
緩存穿透問題
- 數據庫中不存在的對象查詢后也緩存.
if(objJson!=null){
redisService.expire("key",timeout:3*60);
} else {
redisService.expire("key",timeout:5); //5秒內不查詢數據庫
}
synchronized
方法, 不理想, 排隊影響性能.- 同步塊, 需要雙重判定, 否則阻塞的線程均會查詢數據庫, 代碼如下
String value = redisService.get("key");
if(value==null){
synchronized(lock){
value = redisService.get("key"); //多線程再次查詢,防止數據庫多次查詢
if(value==null){
value = dao.get();
redisService.set("key",value);
if(objJson!=null){
redisService.expire("key",timeout:3*60);
} else {
redisService.expire("key",timeout:5); //5秒內不查詢數據庫
}
}
}
}
-
使用
ReentrantLock
的tryLock()
,else中線程等待一段時間, 這樣不阻塞進程 -
使用分布式鎖
if(redisService.setNx("key")==true){
//查詢數據庫
}
- 不設超時時間或設置一個較長的超時時間, 然后判斷當前時間是否超過緩存時間, 結合分布式鎖, 超過則刷新緩存. 此解決方法可能會發生少量數據不一致的情況.
采用何種策略需要結合實際需求, 是保證可用, 還是保證一致性? 對於一致性要求不高的場景, 可能最后一種方案可以徹底解決擊穿問題. 對於一致性要求較高的場景, 使用同步/鎖的方式會更好.