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)
12、登錄時獲取短信驗證碼限速(string)
13、分布式系統,緩存用戶登錄信息
二、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 整數 #加
- setex : 如果 key 已經存在, SETEX 命令將覆寫舊值
- setnx : 若給定的 key 已經存在,則 SETNX 不做任何動作。
2. list(列表)
類似於LinkedList 雙向鏈表。插入刪除塊,查詢慢。
使用場景
- lpush 、lpop 棧(stack)
- lpush 、rpop 隊列(queue)
- lpush 、brpop 消息隊列
常用命令
blpop key timeout # 移出並獲取列表的第一個元素, 如果列表沒有元素會阻塞列表直到等待超時返回nil或發現可彈出元素為止。
lpush key value1...value #先進后出,將一個或多個元素插入到列表頭部。 如果 key 不存在,一個空列表會被創建並執行 LPUSH 操作。 當 key 存在但不是列表類型時,返回一個錯誤。
rpush key value1...value... #先進先出,在列表的尾部插入一個或多個元素
llen key #隊列長度
lpop key # 移除並返回列表第一個元素
rpop key # 移除並返回列表最后一個元素
lrange key start end # 根據索引獲取區間元素
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),Redis使用epoll作為I/O多路復用技術的實現,再加上Redis自身的事件處理模型將epoll中的連接、讀寫、關閉都轉換為事件,不在網絡I/O上浪費過多的時間,
- 4、單線程避免了不必要的上下文切換和競爭條件
Redis是單線程來處理命令的,所以一條命令從客戶端達到服務端不會立刻被執行,所有命令都會進入一個隊列中,然后逐個被執行。
十四、漏斗限流
分布式限流:redis-cell
單機:Google的guava包提供了RateLimiter類
限流的常見算法有以下三種:
1.時間窗口算法
2.漏桶算法
3.令牌算法
十五、GEO
- Redis通過GeoHash算法實現附近的人查詢功能;
- 內部數據結構是zset,通過score還原就可以得到原始坐標;
- 集群環境中單個key對應的數據不宜超過1M,如果超過需要按相應業務規則拆分降低key的數據大小。
十六、scan
- 通過游標分步進行,相比於keys,不會阻塞線程
- 提供limit參數可以控制返回結果條數