Redis-基礎和應用篇
阿新 • • 發佈:2021-01-07
> 2020,到新公司這一年多以來,更新文章和總結知識的習慣被丟掉了。我覆盤了下自己,原因不是公司技術氛圍不好,也不是每天業務需求太多,其根本原因還是---惰性。作為我們技術人隨著年齡的增長,精力也會被生活中許多瑣碎的事情分散,但我們不應該忘記當初寫下第一行程式碼時的初衷。我們一定要明白持之以恆、長遠規劃、階段性覆盤的重要性。2021新的一年,新的心態,新的目標,GO! GO! GO!!!
> 本文是讀錢文品《Redis深度歷險》的讀書筆記
## 一、redis應用
1、記錄帖子點贊數、評論數和點選數(hash)
2、記錄使用者的帖子ID列表,便於快速顯示使用者的帖子列表(zset)
3、記錄帖子的標題、摘要、作者和封面資訊,使用者列表頁展示(hash)
4、記錄帖子的點贊使用者ID列表,評論ID列表,用於顯示和去重計數(hash)
5、快取近期熱帖內容(帖子內容的空間佔用比較大),減少資料庫壓力(hash)
6、記錄帖子的相關文章ID,根據內容推薦相關帖子(list)
7、如果帖子ID是整數自增的,可以使用redis來分配帖子ID(計數器)
8、收藏集和帖子之間的關係(zset)
9、記錄熱榜帖子ID列表、總熱榜和分類熱榜(zset)
10、快取使用者行為歷史,過濾惡意行為(zset、hash)
11、保證同一使用者不會中獎兩次(set)
## 二、redis資料結構
> string(字串)、list(列表)、hash(字典)、set(集合)、zset(有序集合)
### 1. string(字串)
其內部字串是一個動態字串,類似於ArrayList的動態擴容。以此減少頻繁分配記憶體的開銷。字串長度小於1M時,成倍擴容;大於1M時,只增加1M;最大長度512M。
#### 使用場景:
快取使用者登入資訊。token作為key,使用者資訊使用JSON序列化成字串,獲取時再反序列化。
#### 常用命令
```
set name value #存值
get name #取值
exists name #判斷
del name
mset name1 name2 value1 value2 #批量取
mget name1 name2 ..... #批量取
expire name m秒 #m秒後過期
setex name m秒 value #存值,並且設定過期時間
setnx name value #存值,如果name已經存在,就返回0
set name 1 #設定為整數
incr name #還可以自增
incrby name 整數 #加
```
### 2. list(列表)
類似於LinkedList。插入刪除塊,查詢慢。
#### 使用場景
常用於非同步佇列實現(先進先出)
#### 常用命令
```
rpush name value1 value2... #入隊
llen name #佇列長度
lpop name #出隊 value1
```
### 3. hash(字典)
類似於HashMap
#### 使用場景
可以對儲存結構中每個欄位單獨儲存。過期時間是針對真個hash物件,而不是單獨的子key.
#### 常用命令
```
hset key filed1 value1
hset key filed2 value2 #存
hget key filed1 #取
```
### 4. set(集合)
sadd, smembers, scard
### 5. zset(有序集合)
zadd, zrange,zrank, zrem,zcard
## 三、HyperLogLog
- 場景:估數、精確度要求不高場景(統計網站的PV 和UV)
- 命令 pfadd、pfcount、pfmerge
- 記憶體佔用比set小,有一定的誤差
## 四、布隆過濾器
- 原理:布隆過濾器是一個BIT陣列
- 場景:資訊推薦去重(微博推薦重新整理時過濾已經看過的資訊),垃圾郵件過濾、爬蟲系統過濾已爬內容、解決快取穿透問題
- 布隆過濾器可以判斷某個資料一定不存在,但是無法判斷一定存在(不精確的SET)
- 佔用記憶體極少,並且插入和查詢速度都足夠快。
- 缺點,無法刪除資料;隨著資料的增加,誤判率會增加
- Redisson 實現
## 五、Reids6種淘汰策略
- volatile-lru:從設定了過期時間的資料集中,選擇最近最久未使用的資料釋放;
- allkeys-lru:從資料集中(包括設定過期時間以及未設定過期時間的資料集中),選擇最近最久未使用的資料釋放;
- volatile-random:從設定了過期時間的資料集中,隨機選擇一個數據進行釋放;
- allkeys-random:從資料集中(包括了設定過期時間以及未設定過期時間)隨機選擇一個數據進行入釋放;
- volatile-ttl:從設定了過期時間的資料集中,選擇馬上就要過期的資料進行釋放操作;
- noeviction:不刪除任意資料(但redis還會根據引用計數器進行釋放),這時如果記憶體不夠時,會直接返回錯誤。
> 預設策略是noeviction
> 推薦使用的策略是volatile-lru
> 通過maxmemory-samples配置樣本數量,預設為5
快取淘汰演算法(LFU、LRU、ARC、FIFO、MRU)
## 六、Redis 持久化方案:
- RDB 預設方式 (RDB持久化即通過建立快照的方式進行持久化,儲存某個時間點的全量資料。)
- AOF (Append-Only-File持久化即記錄所有變更資料庫狀態的指令,以append的形式追加儲存到AOF檔案中)
- 如果Redis只是用來做快取伺服器,比如資料庫查詢資料後快取,那可以不用考慮持久化,因為快取服務失效還能再從資料庫獲取恢復。
## 七、快取和資料庫資料一致性(併發競爭問題)
- 延時雙刪策略(在寫庫前、後進行redis.del,並且設定合理的延時時間。)
- 讀取binlog分析 ,利用訊息佇列(rabbitmq、kafka), 推送更新各臺的redis快取資料
## 八、快取穿透
- 現象:使用者大量併發請求的資料(key)對應的資料在redis和資料庫中都不存在,導致儘管資料不存在但還是每次都會進行查DB。
- 解決方案:從快取取不到的資料,在資料庫中也沒有取到,這時也可以將key-value對寫為key-null
## 九、快取擊穿
- 現象:快取擊穿是指快取中沒有但資料庫中有的資料(一般是快取時間到期),這時由於併發使用者特別多,同時讀快取沒讀到資料,又同時去資料庫去取資料,引起資料庫壓力瞬間增大,造成過大壓力。
- 解決方案:
1.設定熱點資料永遠不過期
2.介面限流與熔斷,降級
3.加互斥鎖
4.布隆過濾器
## 十、快取雪崩
- 現象:大量key同一時間點失效,同時又有大量請求打進來,導致流量直接打在DB上,造成DB不可用。
- 解決方案:
1.設定key永不失效(熱點資料);
2.設定key快取失效時候儘可能錯開;
3.使用多級快取機制,比如同時使用redsi和memcache快取,請求->redis->memcache->db;
4.購買第三方可靠性高的Redis雲伺服器;
## 十一、Redis熱點key處理
### 1 熱點key發現
- 監控熱key(抓包程式抓redis監聽埠的資料,抓到資料後往kafka裡丟。接下來,flink流式計算系統消費kafka裡的資料,進行資料統計即可)
- 通知系統做處理
### 2 解決方案
- 本地快取(利用ehcache或HashMap將發現的熱key載入到jvm,熱key直接走jvm查詢)
- 叢集(把這個熱key,在多個redis上都存一份)
- 阿里雲Redis已經在核心層面解決熱點key問題
### 3. 熱key的危害
- 流量集中,達到物理網絡卡上限。
- 請求過多,快取分片服務被打垮。
- DB 擊穿,引起業務雪崩。
## 十二、拒絕大KEY
- 叢集環境,大key會導致資料遷移卡頓
- 如果被刪除時,記憶體一次性回收,也會卡頓
- 擴容時,會一次性申請更大的記憶體,也會卡頓
- 注意:如果Redis記憶體起伏較大,很有可能是大key導致,這時需要定位大key並優化
- 定位大key可以使用scan、或者redis-cli指令完成
## 十三、Redis是單執行緒的,但Redis為什麼這麼快
- 1、基於記憶體
- 2、資料結構和操作簡單
- 3、多路I/O複用模型(非阻塞IO)
- 4、單執行緒避免了不必要的上下文切換和競爭條件
## 十四、漏斗限流
分散式限流:redis-cell
單機:Google的guava包提供了RateLimiter類
限流的常見演算法有以下三種:
1.時間視窗演算法
2.漏桶演算法
3.令牌演算法
## 十五、GEO
- Redis通過GeoHash演算法實現附近的人查詢功能;
- 內部資料結構是zset,通過score還原就可以得到原始座標;
- 叢集環境中單個key對應的資料不宜超過1M,如果超過需要按相應業務規則拆分降低key的資料大小。
## 十六、scan
- 通過遊標分步進行,相比於keys,不會阻塞執行緒
- 提供limit引數可以控制返回結