Redis-基礎和應用篇

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參數可以控制返回結果條數
Tags: