日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

有了这篇你还说你不会redis性能优化、内存分析及优化

發布時間:2025/3/20 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 有了这篇你还说你不会redis性能优化、内存分析及优化 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
點擊上方?好好學java?,選擇?星標?公眾號重磅資訊,干貨,第一時間送達 今日推薦:推薦19個github超牛逼項目!個人原創100W +訪問量博客:點擊前往,查看更多 來源: https://blog.csdn.net/An1090239782/article/details/106186041 作者: 愛是與世界平行

Redis 利用了多路 I/O 復用機制,處理客戶端請求時,不會阻塞主線程;Redis 單純執行(大多數指令)一個指令不到 1 微秒,如此,單核 CPU 一秒就能處理 1 百萬個指令(大概對應著幾十萬個請求吧),用不著實現多線程(網絡才是瓶頸)。

1. 優化網絡延時

Redis 客戶端和服務器的通訊一般使用 TCP 長鏈接。如果客戶端發送請求后需要等待 Redis 返回結果再發送下一個指令,客戶端和 Redis 的多個請求就構成下面的關系:

?

(備注:如果不是你要發送的 key 特別長,一個 TCP 包完全能放下 Redis 指令,所以只畫了一個 push 包)

?

這樣這兩次請求中,客戶端都需要經歷一段網絡傳輸時間。

但如果有可能,完全可以使用 multi-key 類的指令來合并請求,比如兩個 GET key 可以用 MGET key1 key2 合并。這樣在實際通訊中,請求數也減少了,延時自然得到好轉。

如果不能用 multi-key 指令來合并,比如一個 SET,一個 GET 無法合并。怎么辦?

Redis 中有至少這樣兩個方法能合并多個指令到一個 request 中,一個是 MULTI/EXEC,一個是 script。前者本來是構建 Redis 事務的方法,但確實可以合并多個指令為一個 request,它到通訊過程如下。至于 script,最好利用緩存腳本的 sha1 hash key 來調起腳本,這樣通訊量更小。

這樣確實更能減少網絡傳輸時間,不是么?但如此以來,就必須要求這個 transaction / script 中涉及的 key 在同一個 node 上,所以要酌情考慮。

如果上面的方法我們都考慮過了,還是沒有辦法合并多個請求,我們還可以考慮合并多個 responses。比如把 2 個回復信息合并:

這樣,理論上可以省去 1 次回復所用的網絡傳輸時間。這就是 pipeline 做的事情。不是任意多個回復信息都可以放進一個 TCP 包中,如果請求數太多,回復的數據很長(比如 get 一個長字符串),TCP 還是會分包傳輸,但使用 pipeline,依然可以減少傳輸次數。

pipeline 和上面的其他方法都不一樣的是,它不具有原子性。所以在 cluster 狀態下的集群上,實現 pipeline 比那些原子性的方法更有可能。

小結一下:

  • 使用 unix 進程間通信,如果單機部署

  • 使用 multi-key 指令合并多個指令,減少請求數,如果有可能的話

  • 使用 transaction、script 合并 requests 以及 responses

  • 使用 pipeline 合并 response

2. 警惕執行時間長的操作

在大數據量的情況下,有些操作的執行時間會相對長,比如 KEYS *,LRANGE mylist 0 -1,以及其他算法復雜度為 O(n) 的指令。因為 Redis 只用一個線程來做數據查詢,如果這些指令耗時很長,就會阻塞 Redis,造成大量延時。

盡管官方文檔中說 KEYS * 的查詢挺快的,(在普通筆記本上)掃描 1 百萬個 key,只需 40 毫秒(參見:https://redis.io/commands/keys),但幾十 ms 對于一個性能要求很高的系統來說,已經不短了,更何況如果有幾億個 key(一臺機器完全可能存幾億個 key,比如一個 key 100字節,1 億個 key 只有 10GB),時間更長。Redis 中 transaction,script,因為可以合并多個 commands 為一個具有原子性的執行過程,所以也可能占用 Redis 很長時間,需要注意。

如果你想找出生產環境使用的「慢指令」,那么可以利用 SLOWLOG GET count 來查看最近的 count 個執行時間很長的指令。至于多長算長,可以通過「在 redis.conf 中設置 slowlog-log-slower-than 來定義。」

注意

除此之外,在很多地方都沒有提到的一個可能的慢指令是 DEL,但 「redis.conf 文件的注釋[9]中」倒是說了。長話短說就是 DEL 一個大的 object 時候,回收相應的內存可能會需要很長時間(甚至幾秒),所以,建議用 DEL 的異步版本:UNLINK。后者會啟動一個新的 thread 來刪除目標 key,而不阻塞原來的線程。

更進一步,當一個 key 過期之后,Redis 一般也需要同步的把它刪除。其中一種刪除 keys 的方式是,每秒 10 次的檢查一次有設置過期時間的 keys,這些 keys 存儲在一個全局的 struct 中,可以用server.db->expires訪問。檢查的方式是:

從中隨機取出 20 個 keys,把過期的刪掉。

如果剛剛 20 個 keys 中,有 25% 以上(也就是 5 個以上)都是過期的,Redis 認為,過期的 keys 還挺多的,繼續重復步驟 1,直到滿足退出條件:某次取出的 keys 中沒有那么多過去的 keys。

這里對于性能的影響是,如果真的有很多的 keys 在同一時間過期,那么 Redis 真的會一直循環執行刪除,占用主線程。

對此,Redis 作者的建議是「警惕 EXPIREAT」這個指令,因為它更容易產生 keys 同時過期的現象。我還見到過一些建議是給 keys 的過期時間設置一個隨機波動量。

最后,redis.conf 中也給出了一個方法,把 keys 的過期刪除操作變為異步的,即,在 redis.conf 中設置 lazyfree-lazy-expire yes。

3. 優化數據結構、使用正確的算法

一種數據類型(比如 string,list)進行增刪改查的效率是由其底層的存儲結構決定的。

除了時間性能上的考慮,有時候我們還需要節省存儲空間。比如上面提到的 ziplist 結構,就比 hashtable 結構節省存儲空間

?

(Redis Essentials 的作者分別在 hashtable 和 ziplist 結構的 Hash 中插入 500 個 fields,每個 field 和 value 都是一個 15 位左右的字符串,結果是 hashtable 結構使用的空間是 ziplist 的 4 倍。)。

?

但節省空間的數據結構,其算法的復雜度可能很高。所以,這里就需要在具體問題面前做出權衡。

在使用一種數據類型時,可以適當關注一下它底層的存儲結構及其算法,避免使用復雜度太高的方法。舉兩個例子:

「ZADD 的時間復雜度是 O(log(N)),這比其他數據類型增加一個新元素的操作更復雜,所以要小心使用。」

若 Hash 類型的值的 fields 數量有限,它很有可能采用 ziplist 這種結構做存儲,而 ziplist 的查詢效率可能沒有同等字段數量的 hashtable 效率高,在必要時,可以調整 Redis 的存儲結構。

4. 考慮操作系統和硬件是否影響性能

Redis 運行的外部環境,也就是操作系統和硬件顯然也會影響 Redis 的性能。

CPU

Intel 多種 CPU 都比 AMD 皓龍系列好

虛擬化

實體機比虛擬機好,主要是因為部分虛擬機上,硬盤不是本地硬盤,監控軟件導致 fork 指令的速度慢(持久化時會用到 fork),尤其是用 Xen 來做虛擬化時。

內存管理

在 linux 操作系統中,為了讓 translation lookaside buffer,即 TLB,能夠管理更多內存空間(TLB 只能緩存有限個 page),操作系統把一些 memory page 變得更大,比如 2MB 或者 1GB,而不是通常的 4096 字節,這些大的內存頁叫做 huge pages。

同時,為了方便程序員使用這些大的內存 page,操作系統中實現了一個 transparent huge pages(THP)機制,使得大內存頁對他們來說是透明的,可以像使用正常的內存 page 一樣使用他們。但這種機制并不是數據庫所需要的,可能是因為 THP 會把內存空間變得緊湊而連續吧,就像mongodb 的文檔[11]中明確說的,數據庫需要的是稀疏的內存空間,所以請禁掉 THP 功能。Redis 也不例外,但 Redis 官方博客上給出的理由是:使用大內存 page 會使 bgsave 時,fork 的速度變慢;如果 fork 之后,這些內存 page 在原進程中被修改了,他們就需要被復制(即 copy on write),這樣的復制會消耗大量的內存(畢竟,人家是 huge pages,復制一份消耗成本很大)。

所以,請禁止掉操作系統中的 transparent huge pages 功能。

交換空間:當一些內存 page 被存儲在交換空間文件上,而 Redis 又要請求那些數據,那么操作系統會阻塞 Redis 進程,然后把想要的 page,從交換空間中拿出來,放進內存。這其中涉及整個進程的阻塞,所以可能會造成延時問題,一個解決方法是禁止使用交換空間(Redis Essentials 中如是建議,如果內存空間不足,請用別的方法處理)。

5. 考慮持久化帶來的開銷

5.1 RDB 全量持久化。

這種持久化方式把 Redis 中的全量數據打包成 rdb 文件放在硬盤上。但是執行 RDB 持久化過程的是原進程 fork 出來一個子進程,而 fork 這個系統調用是需要時間的,根據Redis Lab 6 年前做的實驗[12],在一臺新型的 AWS EC2 m1.small^13 上,fork 一個內存占用 1GB 的 Redis 進程,需要 700+ 毫秒,而這段時間,redis 是無法處理請求的。

雖然現在的機器應該都會比那個時候好,但是 fork 的開銷也應該考慮吧。為此,要使用合理的 RDB 持久化的時間間隔,不要太頻繁。

5.2 AOF 增量持久化。

這種持久化方式會把你發到 redis server 的指令以文本的形式保存下來(格式遵循 redis protocol),這個過程中,會調用兩個系統調用,一個是 write(2),同步完成,一個是 fsync(2),異步完成。

這兩部都可能是延時問題的原因:

write 可能會因為輸出的 buffer 滿了,或者 kernal 正在把 buffer 中的數據同步到硬盤,就被阻塞了。fsync 的作用是確保 write 寫入到 aof 文件的數據落到了硬盤上,在一個 7200 轉/分的硬盤上可能要延時 20 毫秒左右,消耗還是挺大的。更重要的是,在 fsync 進行的時候,write 可能會被阻塞。其中,write 的阻塞貌似只能接受,因為沒有更好的方法把數據寫到一個文件中了。但對于 fsync,Redis 允許三種配置,選用哪種取決于你對備份及時性和性能的平衡:always:當把 appendfsync 設置為 always,fsync 會和客戶端的指令同步執行,因此最可能造成延時問題,但備份及時性最好。everysec:每秒鐘異步執行一次 fsync,此時 redis 的性能表現會更好,但是 fsync 依然可能阻塞 write,算是一個折中選擇。no:redis 不會主動出發 fsync (并不是永遠不 fsync,那是不太可能的),而由 kernel 決定何時 fsync。

6. 使用分布式架構 —— 讀寫分離、數據分片

把慢速的指令發到某些從庫中執行 把持久化功能放在一個很少使用的從庫上 把某些大 list 分片 其中前兩條都是根據 Redis 單線程的特性,用其他進程(甚至機器)做性能補充的方法。

7. reids 內存分析及使用優化

redis內存使用情況:info memory

7.1 內存使用

redis的內存使用分布:自身內存,鍵值對象占用、緩沖區內存占用及內存碎片占用。redis 空進程自身消耗非常的少,可以忽略不計,優化內存可以不考慮此處的因素。

7.1.1 對象內存

對象內存,也即真實存儲的數據所占用的內存。redis k-v結構存儲,對象占用可以簡單的理解為 k-size + v-size。redis的鍵統一都為字符串類型,值包含多種類型:string、list、hash、set、zset五種基本類型及基于string的Bitmaps和HyperLogLog類型等。

在實際的應用中,一定要做好kv的構建形式及內存使用預期。

7.1.2 緩沖內存

緩沖內存包括三部分:客戶端緩存、復制積壓緩存及AOF緩沖區。

  • 客戶端緩存:接入redis服務器的TCP連接輸入輸出緩沖內存占用,TCP輸入緩沖占用是不受控制的,最大允許空間為1G。輸出緩沖占用可以通過client-output-buffer-limit參數配置。redis 客戶端主要分為從客戶端、訂閱客戶端和普通客戶端。

  • 從客戶端連接占用:也就是我們所說的slave,主節點會為每一個從節點建立一條連接用于命令復制,緩沖配置為:client-output-buffer-limit slave 256mb 64mb 60。主從之間的間絡延遲及掛載的從節點數量是影響內存占用的主要因素。因此在涉及需要異地部署主從時要特別注意,另外,也要避免主節點上掛載過多的從節點(<=2);

    訂閱客戶端內存占用:發布訂閱功能連接客戶端使用單獨的緩沖區,默認配置:client-output-buffer-limit pubsub 32mb 8mb 60。當消費慢于生產時會造成緩沖區積壓,因此需要特別注意消費者角色配比及生產、消費速度的監控。

    普通客戶端內存占用:除了上述之外的其它客戶端,如我們通常的應用連接,默認配置:client-output-buffer-limit normal 1000。可以看到,普通客戶端沒有配置緩沖區限制,通常一般的客戶端內存消耗也可以忽略不計。

    但是當redis服務器響應較慢時,容易造成大量的慢連接,主要表現為連接數的突增,如果不能及時處理,此時會嚴重影響redis服務節點的服務及恢復。公眾號搜索:[Java學習之道],回復'福利',3T資料等你來拿!

    關于此,在實際應用中需要注意幾點:

    maxclients最大連接數配置必不可少。合理預估單次操作數據量(寫或讀)及網絡時延ttl。禁止線上大吞吐量命令操作,如keys等。高并發應用情景下,redis內存使用需要有實時的監控預警機制,

  • 復制積壓緩沖區 v2.8之后提供的一個可重用的固定大小緩沖區,用以實現向從節點的部分復制功能,避免全量復制。配置單數:repl-backlog-size,默認1M。單個主節點配置一個復制積壓緩沖區。

  • AOF緩沖區 AOF重寫期間增量的寫入命令保存,此部分緩存占用大小取決于AOF重寫時間及增量。

  • 7.2 redis子進程內存消耗

    子進程即redis執行持久化(RDB/AOF)時fork的子任務進程。

  • 關于linux系統的寫時復制機制:父子進程會共享相同的物理內存頁,父進程處理寫請求時會對需要修改的頁復制一份副本進行修改,子進程讀取的內存則為fork時的父進程內存快照,因此,子進程的內存消耗由期間的寫操作增量決定。

  • 關于linux的透明大頁機制THP(Transparent Huge Page):THP機制會降低fork子進程的速度;寫時復制內存頁由4KB增大至2M。高并發情境下,寫時復制內存占用消耗影響會很大,因此需要選擇性關閉。

  • 關于linux配置:一般需要配置linux系統 vm.overcommit_memory=1,以允許系統可以分配所有的物理內存。防止fork任務因內存而失敗。

  • 7.3 redis內存管理

    redis的內存管理主要分為兩方面:內存上限控制及內存回收管理。

    7.3.1 內存上限:maxmemory

    目的:緩存應用內存回收機制觸發 + 防止物理內存用盡(redis 默認無限使用服務器內存) + 服務節點內存隔離(單服務器上部署多個redis服務節點) 在進行內存分配及限制時要充分考慮內存碎片占用影響。動態調整,擴展redis服務節點可用內存:config set maxmemory {}。

    7.3.2 內存回收

    「回收時機:鍵過期、內存占用達到上限」

  • 「過期鍵刪除:」redis 鍵過期時間保存在內部的過期字典中,redis采用惰性刪除機制+定時任務刪除機制。惰性刪除:即讀時刪除,讀取帶有超時屬性的鍵時,如果鍵已過期,則刪除然后返回空值。這種方式存在問題是,觸發時機,加入過期鍵長時間未被讀取,那么它將會一直存在內存中,造成內存泄漏。

  • 定時任務刪除:redis內部維護了一個定時任務(默認每秒10次,可配置),通過自適應法進行刪除。刪除邏輯如下:

    需要說明的一點是,快慢模式執行的刪除邏輯相同,這是超時時間不同。

  • 「內存溢出控制」

  • 當內存達到maxmemory,會觸發內存回收策略,具體策略依據maxmemory-policy來執行。

    • noevication:默認不回收,達到內存上限,則不再接受寫操作,并返回錯誤。

    • volatile-lru:根據LRU算法刪除設置了過期時間的鍵,如果沒有則不執行回收。

    • allkeys-lru:根據LRU算法刪除鍵,針對所有鍵。

    • allkeys-random:隨機刪除鍵。

    • volatitle-random:速記刪除設置了過期時間的鍵。

    • volatilte-ttl:根據鍵ttl,刪除最近過期的鍵,同樣如果沒有設置過期的鍵,則不執行刪除。動態配置:config set maxmemory-policy {} 在設置了maxmemory情況下,每次的redis操作都會檢查執行內存回收,因此對于線上環境,要確保所這只的maxmemory>used_memory。

    另外,可以通過動態配置maxmemory來主動觸發內存回收。

    推薦文章
    • 面試官問:前后端分離項目,有什么優缺點?我說:沒

    • 2020 年騰訊新增 20 億行代碼,鵝廠第一編程語言還是它

    • 通俗講解分布式鎖,看完不懂算我輸

    • 寫博客能月入10K?

    • 一款基于 Spring Boot 的現代化社區(論壇/問答/社交網絡/博客)

    更多項目源碼
    • 這或許是最美的Vue+Element開源后臺管理UI

    • 推薦一款高顏值的 Spring Boot 快速開發框架

    • 一款基于 Spring Boot 的現代化社區(論壇/問答/社交網絡/博客)

    • 13K點贊都基于 Vue+Spring 前后端分離管理系統ELAdmin,大愛

    • 想接私活時薪再翻一倍,建議根據這幾個開源的SpringBoot項目

    總結

    以上是生活随笔為你收集整理的有了这篇你还说你不会redis性能优化、内存分析及优化的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。