Redis總結筆記(總結自Redis開發與運維)

1、可以通過object encoding命令查詢內部編碼

內部編碼:

string:raw int embstr(小於39)

Hash:hashtable ziplist

List:linked list  ziplist

Set :hashtable intset

Zset skiplist ziplist

這樣設計的有點:改進內部編碼,對外的數據結構和命令沒有影響;多種內部編碼實現可以在不同的場景下發揮各自的優勢。

Redis的單線程模型:使用了單線程架構和IO多路複用模型來實現高性能的內存數據庫服務。

單線程性能高的原因:

  1. 純內存訪問,數據放在內存中,訪問快;
  2. 非阻塞IO,使用epoll作爲IO多路複用技術的實現,加上自身的事件處理模型將epoll中的鏈接 讀寫 關閉都轉換爲事件,不在網絡上浪費過多的時間。
  3. 單線程避免了現成切換和竟態產生的消耗。

單線程模型不會同時有兩個命令同時執行,沒有併發問題。但是若是一個命令執行時間過長,會造成其他命令的阻塞。因爲redis是面向快速執行場景的數據庫。

String:

命令 set setnx setex:

SET key value含義:

          將字符串值 value 關聯到 key . 如果 key 已經持有其他值, SET 就覆寫舊值,無視類型。

SETEX key seconds value

           將值 value 關聯到 key ,並將 key 的生存時間設爲 seconds (以秒爲單位)。如果 key 已經存在, SETEX 命令將覆寫舊值。

返回值:

           設置成功時返回 OK 。當 seconds 參數不合法時,返回一個錯誤。

SETNX key value 含義:  setxx,與nx相反,鍵必須存在才能設置成功,用戶更新。

           將 key 的值設爲 value ,當且僅當 key 不存在。 若給定的 key 已經存在,則 SETNX 不做任何動作。

           SETNX 是『SET if Not eXists』(如果不存在,則 SET)的簡寫。 設置成功,返回 1 .  設置失敗,返回 0 。

GETSET key value  含義:

          將給定 key 的值設爲 value ,並返回 key 的舊值(old value)。

          當 key 存在但不是字符串類型時,返回一個錯誤。

           返回給定 key 的舊值.  當 key 沒有舊值時,也即是, key 不存在時,返回 nil 。

因爲 SET 命令可以通過參數來實現和 SETNX 、 SETEX 和 PSETEX 三個命令的效果,所以將來的 Redis 版本可能會廢棄並最終移除 SETNX 、 SETEX 和 PSETEX 這三個命令。

SET key-with-expire-time "hello" EX 10086  ,爲key設置過期時間。

SET key-with-expire-and-NX "hello" EX 10086 NX 若是key不存在 則設置,並設置過期時間。

 

Setnx的應用場景,若是多個客戶端同時執行setnx key value,只能有一個客戶端設置成功,setnx可以作爲分佈式鎖的一種實現方案。

批量獲取命令:mset:提高開發效率,減少網絡消耗

計數:
incr key.自增;decr decr 自減,incrby 自增指定數字 decrby 自減指定數字 incrbyfloat 自增浮點數

String使用場景:緩存 ,計數 ,共享session ,限速(限制發短信次數,)

Hash

Hget hset hgetall  存儲結構信息。

List:列表原始有序(可通過下標獲取),可重複

查詢:lindex key index:獲取列表指定索引下標的元素。

Lrem key count value :刪除指定元素,從列表中找到等於value的元素進行刪除,

Ltrim key start end :按照索引範圍修建列表 ,保留list的start end之間的元素。

阻塞操作命令:blpop brpop, blpop key timeout;timeout 阻塞時間。

列表爲空:timeout=3 ,客戶端等3s後返回,timeout=0則一直阻塞。列表不爲空 則會立即返回。

list使用場景:
消息隊列:lpush +brpop 可實現阻塞隊列,

文章列表:分頁展示每個人的文章列表。若是列表較大,獲取中間數據比較慢,可以考慮將列表做二級拆分。

Lpush +lpop =stack(棧);

lpush +rpop =queue;l

push +ltrim = capped collection (有限集合),

lpush +brpop = message queue.

Set

不允許重複元素,元素無序;

Sadd,添加元素, sad key element ;srem 刪除元素 ;scard key 計算元素個數。Sismember key element ,判斷元素是否在集合中。Srandmember key count,隨機的返回count元素。Smembers key:獲取所有元素。Sinter key1 key2 ,求交集

Suinon:求並集;sdiff 求差集

使用場景:標籤(tag);sdd = tagging(標籤);spop/srandmember=random item(生成隨機數,抽獎);sadd+sinter=social graph(社交需求)

有序集合 zset :爲每個元素設置一個分數作爲排序的依據。Zadd key score member,也存在nx xx ch incr選項,ch表示此次操作後發生變化的個數。

Zrank key member 計算成員的排名 從低到高,zrevrank key member  從高到底排名。Zincrby key increment member .增加成員的分數。

使用場景:排行榜系統,獲取贊數。

鍵管理:單個建管理:rename :重命名,重命名的key若是存在,則值被覆蓋。爲了避免強行rename,使用renamenx,只有newkey不存在的時候纔會覆蓋。重命名會刪除舊鍵,若鍵對應的值比較大,會存在阻塞redis的可能。

隨機返回一個鍵 randomkey

鍵過期:expire ttl(顯示剩餘過期時間,-1表示鍵沒有過期時間,-2表示鍵不存在),persist key:清除鍵的過期時間。對於字符串類型,執行set會去掉過期時間。Redis不支持二級數據結構(hash 列表)內部元素的過期功能。不能對列表類型的一個元素設置過期時間。Setex= set+expire ,不但是原子執行 還減少了一次網絡通訊時間。

Keys獲取鍵可能會產生阻塞,類似還有hgetall smembers zrange,優化可以使用hscan ssan zscan.但是scan的過程中若是有鍵的變化,新增的可能遍歷不到等。

Pipeline:流水線機制。很多命令不支持批量執行的;例如hgetall,若要執行n次hgetall命令,需要消耗多次網絡連接,pipeline可以實現將一組Redis命令進行組裝,通過一次網絡傳輸給redis,在將這組redis命令的執行結果按順序返回給客戶端。

原生批量命令與pipeline的對比:

原生批量命令是原子的,pipeline不是,原生批量命令是一個命令對應多個key,pipeline支持多個命令,原生批量命令是Redis服務端支持實現的,pipeline需要服務端和客戶端的共同實現。

 

爲了保證這條命令組合的原子性,Redis提供了簡單的事務功能及集成lua腳本來解決這個問題。將一組命令放在multi exec命令之間。之間的命令是原子順序執行的,exec時纔會真正的執行命令,若其中一個命令執行了 另外一個命令運行出錯,redis不會回滾。同時也無法實現命令之間的邏輯計算關係。

 

Redis執行lua有兩種方式:使用eval命名,客戶端將腳本作爲字符串發到服務器,服務器將結果返回給客戶端。

使用evalsha命令,將lua腳本加載到Redis服務端,得到腳本的sha校驗和,evalsha命令使用sha作爲參數直接執行對應的lua腳本,避免每次發送lua腳本的開銷。腳本常駐服務端,腳本功能得到複用。

Lua腳本在Redis是原子執行的,執行過程中不會插入其他命令。

Lua的缺點:lua執行時間太長,阻礙客戶端命令時,需要script kill;將腳本殺掉。不過lua腳本正在執行寫操作的時候,scriptkill將不會生效。

Bitmaps:

Redis(減少內存使用量)提供bitmaps這個數據接口實現對位的操作。Bitmaps本身不是一種數據結構,實際上是字符串,但是他可以對字符串的位進行操作。Bitmaps可以認爲是一個以爲爲單位的數組,數組的每個單元只能存儲0和1,數組的下表在bitmaps叫做偏移量。

案例:記錄每個用戶是否訪問過某網站,將訪問的用戶記做1,沒有訪問過記做0,用偏移量作爲用戶的id。Setbit key offset value 初始化的時候爲0,很多應用的id以(10000)等開頭,若直接和bitmap對應,造成浪費,一般將id減去指定數字,不然偏移量太大,初始化的過程比較慢,造成redis阻塞。

獲取數據getbit key offset

獲取bitmap指定範圍值爲1的個數:bitcount start end

Bitmaps間的運算:bitop 是一個複合操作,可以做多個bitmap的and or not xor等操作。

Bitmap的使用注意:若是一億用戶,每天只有10W訪問量,大量的0用戶,這時就不合適使用bitmaps.

Hyperloglog

Hyperloglog實際上是字符串類型,是一種基數算法(給定一個含有重複元素的有限集合,計算其不重複的元素的個數),通過hyperloglog利用極小的內存空間完成獨立總數的統計。數據集可以是IP ID EMAIL等。有三個命令:pfadd pfcount pfmerge

選用pyperloglog的考慮:只爲了計算獨立總數,不需要獲取單條數據;

可以容忍一定的誤差率,畢竟內存佔用上優勢很大

用來統計:某家店鋪今天有多少不同用戶訪問;某家店鋪今天接待了多少不同買家這類信息,思想:hash函數,將同一個用戶hash到同一位中。

發佈訂閱:

Redis提供了發佈訂閱的消息機制,發佈訂閱中不直接通信,發佈者客戶端向指定的頻道發佈消息,訂閱該頻道的每個客戶端都可以收到該消息。

Publish channel message 發佈消息

訂閱消息:subscribe channel訂閱消息 可以一次訂閱多個頻道。

客戶端在執行訂閱命令之後進入了訂閱狀態,只能接收subscribe unsubscribe(取消訂閱 channel)psubscribe punsubscribe(這兩個是支持正則形式的channel)四個命令。

新開啓的訂閱客戶端,無法接收到該頻道之前的消息,因爲redis不會對發佈的消息進行持久化。

Redis的發佈訂閱消息系統粗糙,無法實現消息堆積和回溯。

使用場景:聊天室 公告牌 服務之間利用消息解耦的都可以。

GEO (地理信息定位)支持存儲地理位置信息用來實現附近位置,搖一搖等依賴地理位置信息的功能。

GEO實現是zset

4章客戶端

客戶端服務器通信使用的協議是resp(redis序列化協議).服務端返回結果格式:狀態回覆:+;錯誤回覆-;整數回覆:;字符串回覆$,多條字符串回覆 *。

Java的redis客戶端:jedis屬於java的第三方開發包,下載jedis.jar加入到項目中。

Jedis實現了pipeline,新建對象,進行操作即可。

輸入緩衝區:redis爲每個客戶端分配了輸入緩衝區,作用就是將客戶端發送的命令臨時保存,同時redis從緩衝區里拉取命令並執行,每個客戶端緩衝區不能超過1G,超過後將會關閉。

輸入緩衝區不受maxmemory控制,若redis存儲了2G數據,輸入緩衝區使用了3G,超過maxmemory的限制,可能會產生數據丟失,鍵值淘汰 ooM等情況。

兩種情況導致輸入緩衝區過大:redis的處理速度跟不上輸入速度,並且每次進入緩衝區的命令包含了大量的bigkey;

另外一種情況是redis發生了阻塞,短期內不能處理命令,造成客戶端積壓。

解決方案:監控 client list

輸出緩衝區:

保存命令執行的結果返回給客戶端,爲redis和客戶端交互返回結果提供緩衝。輸出緩衝區也不首maxmemory的限制。

及時監控內存,一旦發現內存抖動頻繁,可能就是輸出緩衝區過大導致的。

Monitor:該命令用戶監控redis正在執行的命令,monitor能監聽到所有的命令,缺點:一旦Redis的併發量太大,monitor客戶端的緩衝會暴漲,可能會瞬間站會大量內存。

5章,持久化

Redis支持RDB AOF兩種持久化機制,可以避免因進程退出造成的數據丟失問題,當下次重啓時利用之前的持久化的文件即可實現數據恢復。

RDB把當前進程數據生成快照保存到硬盤,手動觸發(save:保存數據期間會阻塞,  bgsave:fock子進程,子進程完成持久化,fock期間會阻塞,一般時間短)和自動觸發(save m n :m秒有n個更新時;從節點執行全量複製操作時,主節點自動執行bgsave生成rdb文件發給從節點;執行shutdown時,沒有開啓aof就會自動執行bgsave;執行rebug reload時會加載redis.)

Redis默認採用LZF算法對Rdb文件壓縮。

RDB優點:壓縮的二進制文件,文件小,含有某個時間點的數據快照,數據恢復快,適合備份,災難恢復

缺點:沒辦法實時持久化(bgsave每次運行會fock進程,成本高,不能頻繁操作)。

Aof:獨立日誌的方式記錄每次寫命令,重啓時重新執行AOF文件的命令恢復數據,主要是解決數據實時持久化的問題,主流持久化方式。Aof的工作流程:所有命令追加寫入AOF緩存;緩存根據策略向硬盤同步操作(AOF文件同步); 文件重寫:AOF越來越大需定期重寫,達到壓縮的目的。 重啓加載:服務器重啓時,加載sof文件恢復數據。

爲什麼命令先寫緩存?:單線程模式,每次都寫硬盤,性能完全取決於當前硬盤的負載,阻塞客戶端。

緩存同步文件的策略:always no everysec(默認,命令寫入緩存後,調用系統write操作,不對AOF文件做fsync同步,同步操作由專門的線程每秒調用一次,持久化到1秒前操作):

重寫:將Redis進程內的數據轉化爲寫命令同步到Aof文件的過程。子進程重寫期間使用copy-on write(寫時複製,在併發訪問的時候, 需要修改的元素,不直接修改容器,若是先複製一份副本,在副本上修改,修改完成後將指向原來容器的引用指向副本容器。讀原來的文件,寫副本,有數據不一致問題,可以最終一致。)機制與父進程共享內存,避免內存消耗翻倍。AOF重寫期間還需要維護重寫緩衝區,保存新的寫入命令避免數據丟失。

重啓加載:AOF開啓且存在AOF文件時,優先加載AOF文件,沒有采取找RDB文件。

持久化阻塞主線程的場景有:fock阻塞和AOF追加阻塞(超過2s數據還沒用同步完,則主線程阻塞,直到同步完成);

6章複製

保證分佈式Redis高可用:

  1. 複製的使用方式:只能有主到從複製(從節點的寫,無法同步到主)命令:從節點配置slaveof masterhost masterport.

斷開復制:從節點執行slaveof no one;slaveof 還可以切主(當前從節點對主節點的複製切換到另一個主節點,slaveof newMasterIp newMasterPort),切主的操作流程:1.斷開與舊主節點複製關係。2與新主節點建立複製關係;3刪除從節點所有的數據,4對新主節點進行復制操作。

  1. 複製支持的拓撲結構:一主一從(寫併發較大時,可以只在從節點開啓AOF),一主多從(多個從節點實現讀寫分離),樹狀(實現數據一層一層複製,減少主節點同步的數量)
  2. 分析複製的原理:過程 保存主節點信息;主從建立socket連接;發送ping命令(檢測主從之間的網絡套接字是否可用,主節點是否可以接受命令);權限驗證;同步數據集(主節點將全部數據發給從節點,分爲全量同步和部分同步);命令持續複製(主節點持續把寫命令發送給從節點,保證主從數據的一致性)。

全量複製(開銷大) 部分複製,主從維持心跳(ping命令);複製過程是異步的(主節點處理完寫直接返回,不等待從節點複製完成,從節點有數據延遲的情況)。使用從節點用於讀寫分離存在數據延遲,過期數據,從節點可用性等問題。

  1. 複製過程中的問題:

7章 阻塞

單線程架構,所有的讀寫操作都在一條主線程中完成,高併發場景,若是阻塞,就是噩夢。

阻塞內在原因:不合理的使用API或數據結構(使用keys sort hgetall等),CPU飽和,持久化阻塞等。(慢查詢、fork aof阻塞 lua執行太長)

外在原因:CPU競爭,內存交換,網絡問題。

8章 理解內存

1、內存消耗分析:info memory統計內存;內存消耗=自身內存+對象內存+緩衝內存+內存碎片

對象內存:sizeof(key)+sizeof(value);緩衝內存=客戶端緩衝+複製積壓緩衝區(實現部分複製)+AOF緩衝

2、管理內存的原理與方法:通過內存上限和回收策略實現內存管理。

內存回收策略:刪除到達過期時間的鍵對象:採用惰性刪除(客戶端讀取時判斷超時,超時會刪除並返空)和定時任務刪除(定時任務,每秒運行10次,根據鍵的過期比例,使用快慢等回收鍵:定時任務在每個數據庫空間隨機檢查20鍵,發現過期刪除,若是超過25%的鍵過期,則循環回收知道不足25%爲止,若回收超時(25ms超時時間)則在觸發之前再次快速運行回收(快速超時時間1ms,其他和慢回收一樣))機制實現

內存使用到達maxmemory上限時觸發內存溢出控制策略:6種

1noeviction.默認策略:不刪除數據,拒絕所有寫入,返回OOM錯誤。只支持讀

2.volatile-lru:根據LRU算法刪除設置超時時間的鍵,若沒有可刪除的,則退回1模式

3.all-key-lru:不管設置沒設置超時時間,都採用lru算法,知道足夠空間

4 allkey-random;隨機刪除所有鍵,

5volatile-random 隨機刪除過期鍵。

Volatile-ttl:根據鍵的ttl屬性,刪除最近將要過期的數據,若是沒有,則退回1模式。

3、內存優化技巧:

1.使用scan +object idletime 命令批量查詢哪些鍵長時間未被訪問,清理,降低內存佔用。

2、高併發場景下,建議字符串長度控制在39字節內,減少創建RedisObject內存分配次數,提高性能。

3.縮減鍵和值的長度。

4.共享對象池,Redis內部維護0-9999的整數對象池,但是用到LRU的淘汰時Redis禁用共享對象池。

5字符串優化 ; 編碼優化;控制鍵的數量

9章 哨兵(故障自動處理 Redis sentinel實現故障發現 故障自動轉移 配置中心客戶端通知)

10章集羣

11章緩存設計

緩存能加速應用的讀寫速度,同時降低後端負載。

1、緩存的收益和成本:收益-加速讀寫,降低後端負載;成本-數據不一致,代碼維護成本;運維成本。緩存使用場景:開銷大的複雜計算;加速請求響應。

2、緩存更新策略的選擇和使用場景:低一致性業務建議配置最大內存和淘汰策略;高一致性業務使用超時剔除和主動更新(可以利用ans通知緩存更新)

3、緩存力度控制方法:緩存全部內容 or部分內容?

4、緩存穿透問題:穿透是查詢一個不存在的數據,緩存和存儲層都不會命中;緩存穿透將導致不存在的數據每次請求都要到存儲層查詢,原因:代碼數據問題,惡意攻擊

解決:1緩存空對象(問題:緩存需要空間多,設置更短的超時時間;數據不一致)2.對key做過濾攔截

5、緩存無底洞問題優化:添加太多的節點導致 集羣性能降低。批量操作key在不同的節點上,多網絡延遲

6、緩存雪崩問題優化:緩存突然大量失效,請求打到存儲層 調用暴增。解決:1、保證緩存層服務的高可用性;2、依賴隔離組件爲後端限流並降級。使用鎖,一次只能一個請求訪問後端獲取數據;緩存失效後端異步更新,

7、熱點key重建優化

熱點key,併發量大,重建緩存不能在短時間完成,可能是一個複雜計算,例如複雜SQL,多個依賴等。互斥鎖來實現:setnx;2 永不過期:緩存層面不過期;功能層面 爲每個value設置一個邏輯過期時間(超時後,使用單獨現成去構建緩存),但是會有數據不一致的風險。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章