久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

你的Redis真的變慢了嗎?性能優(yōu)化如何做

 昵稱10087950 2023-04-27 發(fā)布于江蘇

阿里妹導讀

本文先講述了Redis變慢的判別方法,,后面講述了如何提升性能,。

一、Redis為什么變慢了

1.Redis真的變慢了嗎,?

對 Redis 進行基準性能測試
例如,,我的機器配置比較低,當延遲為 2ms 時,,我就認為 Redis 變慢了,,但是如果你的硬件配置比較高,那么在你的運行環(huán)境下,,可能延遲是 0.5ms 時就可以認為 Redis 變慢了,。
所以,你只有了解了你的 Redis 在生產(chǎn)環(huán)境服務器上的基準性能,,才能進一步評估,,當其延遲達到什么程度時,,才認為 Redis 確實變慢了,。
為了避免業(yè)務服務器到 Redis 服務器之間的網(wǎng)絡延遲,你需要直接在 Redis 服務器上測試實例的響應延遲情況,。執(zhí)行以下命令,,就可以測試出這個實例 60 秒內(nèi)的最大響應延遲:
./redis-cli --intrinsic-latency 120Max latency so far: 17 microseconds.Max latency so far: 44 microseconds.Max latency so far: 94 microseconds.Max latency so far: 110 microseconds.Max latency so far: 119 microseconds.
36481658 total runs (avg latency: 3.2893 microseconds / 3289.32 nanoseconds per run).Worst run took 36x longer than the average latency.

從輸出結(jié)果可以看到,這 60 秒內(nèi)的最大響應延遲為 119 微秒(0.119毫秒),。你還可以使用以下命令,,查看一段時間內(nèi) Redis 的最小、最大,、平均訪問延遲

$ redis-cli -h 127.0.0.1 -p 6379 --latency-history -i 1min0max1, avg: 0.13 (100 samples) -- 1.01 seconds rangemin0max1, avg: 0.12 (99 samples) -- 1.01 seconds rangemin0max1, avg: 0.13 (99 samples) -- 1.01 seconds rangemin0max1, avg: 0.10 (99 samples) -- 1.01 seconds rangemin0max1, avg: 0.13 (98 samples) -- 1.00 seconds rangemin: 0, max: 1, avg: 0.08 (99 samples) -- 1.01 seconds range

如果你觀察到的 Redis 運行時延遲是其基線性能的 2 倍及以上,,就可以認定 Redis 變慢了。?

網(wǎng)絡對 Redis 性能的影響,,一個簡單的方法是用 iPerf 這樣的工具測試網(wǎng)絡極限帶寬,。
服務器端
# iperf -s -p 12345 -i 1 -Miperf: option requires an argument -- M------------------------------------------------------------Server listening on TCP port 12345TCP window size: 4.00 MByte (default)------------------------------------------------------------[ 4] local 172.20.0.113 port 12345 connected with 172.20.0.114 port 56796[ ID] Interval Transfer Bandwidth[ 4] 0.0- 1.0 sec 614 MBytes 5.15 Gbits/sec[ 4] 1.0- 2.0 sec 622 MBytes 5.21 Gbits/sec[ 4] 2.0- 3.0 sec 647 MBytes 5.42 Gbits/sec[ 4] 3.0- 4.0 sec 644 MBytes 5.40 Gbits/sec[ 4] 4.0- 5.0 sec 651 MBytes 5.46 Gbits/sec[ 4] 5.0- 6.0 sec 652 MBytes 5.47 Gbits/sec[ 4] 6.0- 7.0 sec 669 MBytes 5.61 Gbits/sec[ 4] 7.0- 8.0 sec 670 MBytes 5.62 Gbits/sec[ 4] 8.0- 9.0 sec 667 MBytes 5.59 Gbits/sec[ 4] 9.0-10.0 sec 667 MBytes 5.60 Gbits/sec[ 4] 0.0-10.0 sec 6.35 GBytes 5.45 Gbits/sec客戶端
# iperf -c 服務器端IP -p 12345 -i 1 -t 10 -w 20K------------------------------------------------------------Client connecting to 172.20.0.113, TCP port 12345TCP window size: 40.0 KByte (WARNING: requested 20.0 KByte)------------------------------------------------------------[ 3] local 172.20.0.114 port 56796 connected with 172.20.0.113 port 12345[ ID] Interval Transfer Bandwidth[ 3] 0.0- 1.0 sec 614 MBytes 5.15 Gbits/sec[ 3] 1.0- 2.0 sec 622 MBytes 5.21 Gbits/sec[ 3] 2.0- 3.0 sec 646 MBytes 5.42 Gbits/sec[ 3] 3.0- 4.0 sec 644 MBytes 5.40 Gbits/sec[ 3] 4.0- 5.0 sec 651 MBytes 5.46 Gbits/sec[ 3] 5.0- 6.0 sec 652 MBytes 5.47 Gbits/sec[ 3] 6.0- 7.0 sec 669 MBytes 5.61 Gbits/sec[ 3] 7.0- 8.0 sec 670 MBytes 5.62 Gbits/sec[ 3] 8.0- 9.0 sec 667 MBytes 5.59 Gbits/sec[ 3] 9.0-10.0 sec 668 MBytes 5.60 Gbits/sec[ 3] 0.0-10.0 sec 6.35 GBytes 5.45 Gbits/sec
2.使用復雜度過高的命令
首先,第一步,,你需要去查看一下 Redis 的慢日志(slowlog),。
Redis 提供了慢日志命令的統(tǒng)計功能,它記錄了有哪些命令在執(zhí)行時耗時比較久,。
查看 Redis 慢日志之前,,你需要設置慢日志的閾值。例如,設置慢日志的閾值為 5 毫秒,,并且保留最近 500 條慢日志記錄:
# 命令執(zhí)行耗時超過 5 毫秒,,記錄慢日志CONFIG SET slowlog-log-slower-than 5000# 只保留最近 500 條慢日志CONFIG SET slowlog-max-len 500
1.經(jīng)常使用 O(N) 以上復雜度的命令,例如 SORT,、SUNION,、ZUNIONSTORE 聚合類命令
2.使用 O(N) 復雜度的命令,但 N 的值非常大
第一種情況導致變慢的原因在于,,Redis 在操作內(nèi)存數(shù)據(jù)時,,時間復雜度過高,要花費更多的 CPU 資源,。
第二種情況導致變慢的原因在于,,Redis 一次需要返回給客戶端的數(shù)據(jù)過多,更多時間花費在數(shù)據(jù)協(xié)議的組裝和網(wǎng)絡傳輸過程中,。
另外,,我們還可以從資源使用率層面來分析,如果你的應用程序操作 Redis 的 OPS 不是很大,,但 Redis 實例的 CPU 使用率卻很高,,那么很有可能是使用了復雜度過高的命令導致的。

3.操作bigkey

如果你查詢慢日志發(fā)現(xiàn),,并不是復雜度過高的命令導致的,,而都是 SET / DEL 這種簡單命令出現(xiàn)在慢日志中,那么你就要懷疑你的實例否寫入了 bigkey,。
redis-cli -h 127.0.0.1 -p 6379 --bigkeys -i 1-------- summary -------Sampled 829675 keys in the keyspace!Total key length in bytes is 10059825 (avg len 12.13)Biggest string found 'key:291880' has 10 bytesBiggest   list found 'mylist:004' has 40 itemsBiggest    set found 'myset:2386' has 38 membersBiggest   hash found 'myhash:3574' has 37 fieldsBiggest   zset found 'myzset:2704' has 42 members36313 strings with 363130 bytes (04.38% of keys, avg size 10.00)787393 lists with 896540 items (94.90% of keys, avg size 1.14)1994 sets with 40052 members (00.24% of keys, avg size 20.09)1990 hashs with 39632 fields (00.24% of keys, avg size 19.92)1985 zsets with 39750 members (00.24% of keys, avg size 20.03)

這里我需要提醒你的是,,當執(zhí)行這個命令時,要注意 2 個問題:

1.對線上實例進行 bigkey 掃描時,,Redis 的 OPS 會突增,,為了降低掃描過程中對 Redis 的影響,最好控制一下掃描的頻率,,指定 -i 參數(shù)即可,,它表示掃描過程中每次掃描后休息的時間間隔,單位是秒
2.掃描結(jié)果中,,對于容器類型(List,、Hash、Set,、ZSet)的 key,,只能掃描出元素最多的 key。但一個 key 的元素多,,不一定表示占用內(nèi)存也多,,你還需要根據(jù)業(yè)務情況,,進一步評估內(nèi)存占用情況。

4.集中過期

如果你發(fā)現(xiàn),,平時在操作 Redis 時,,并沒有延遲很大的情況發(fā)生,但在某個時間點突然出現(xiàn)一波延時,,其現(xiàn)象表現(xiàn)為:變慢的時間點很有規(guī)律,,例如某個整點,或者每間隔多久就會發(fā)生一波延遲,。
如果是出現(xiàn)這種情況,,那么你需要排查一下,業(yè)務代碼中是否存在設置大量 key 集中過期的情況,。
如果有大量的 key 在某個固定時間點集中過期,,在這個時間點訪問 Redis 時,就有可能導致延時變大,。
Redis 的過期數(shù)據(jù)采用被動過期 + 主動過期兩種策略:
1.被動過期:只有當訪問某個 key 時,,才判斷這個 key 是否已過期,如果已過期,,則從實例中刪除
2.主動過期:Redis 內(nèi)部維護了一個定時任務,,默認每隔 100 毫秒(1秒10次)就會從全局的過期哈希表中隨機取出 20 個 key,然后刪除其中過期的 key,,如果過期 key 的比例超過了 25%,,則繼續(xù)重復此過程,直到過期 key 的比例下降到 25% 以下,,或者這次任務的執(zhí)行耗時超過了 25 毫秒,,才會退出循環(huán)
注意,,這個主動過期 key 的定時任務,,是在 Redis 主線程中執(zhí)行的。
也就是說如果在執(zhí)行主動過期的過程中,,出現(xiàn)了需要大量刪除過期 key 的情況,,那么此時應用程序在訪問 Redis 時,必須要等待這個過期任務執(zhí)行結(jié)束,,Redis 才可以服務這個客戶端請求,。
如果此時需要過期刪除的是一個 bigkey,那么這個耗時會更久,。而且,,這個操作延遲的命令并不會記錄在慢日志中。
因為慢日志中只記錄一個命令真正操作內(nèi)存數(shù)據(jù)的耗時,,而 Redis 主動刪除過期 key 的邏輯,,是在命令真正執(zhí)行之前執(zhí)行的。

5.實例內(nèi)存達到上限

當我們把 Redis 當做純緩存使用時,通常會給這個實例設置一個內(nèi)存上限 maxmemory,,然后設置一個數(shù)據(jù)淘汰策略,。
當 Redis 內(nèi)存達到 maxmemory 后,每次寫入新的數(shù)據(jù)之前,,Redis 必須先從實例中踢出一部分數(shù)據(jù),,讓整個實例的內(nèi)存維持在 maxmemory 之下,然后才能把新數(shù)據(jù)寫進來,。
這個踢出舊數(shù)據(jù)的邏輯也是需要消耗時間的,,而具體耗時的長短,要取決于你配置的淘汰策略:
  • allkeys-lru:不管 key 是否設置了過期,,淘汰最近最少訪問的 key
  • volatile-lru:只淘汰最近最少訪問,、并設置了過期時間的 key
  • allkeys-random:不管 key 是否設置了過期,隨機淘汰 key
  • volatile-random:只隨機淘汰設置了過期時間的 key
  • allkeys-ttl:不管 key 是否設置了過期,,淘汰即將過期的 key
  • noeviction:不淘汰任何 key,,實例內(nèi)存達到 maxmeory 后,再寫入新數(shù)據(jù)直接返回錯誤
  • allkeys-lfu:不管 key 是否設置了過期,,淘汰訪問頻率最低的 key(4.0+版本支持)
  • volatile-lfu:只淘汰訪問頻率最低,、并設置了過期時間 key(4.0+版本支持)

一般最常使用的是 allkeys-lru / volatile-lru 淘汰策略,它們的處理邏輯是,,每次從實例中隨機取出一批 key(這個數(shù)量可配置),,然后淘汰一個最少訪問的 key,之后把剩下的 key 暫存到一個池子中,,繼續(xù)隨機取一批 key,,并與之前池子中的 key 比較,再淘汰一個最少訪問的 key,。以此往復,,直到實例內(nèi)存降到 maxmemory 之下。

需要注意的是,,Redis 的淘汰數(shù)據(jù)的邏輯與刪除過期 key 的一樣,,也是在命令真正執(zhí)行之前執(zhí)行的,也就是說它也會增加我們操作 Redis 的延遲,,而且,,寫 OPS 越高,延遲也會越明顯,。

圖片?

如果此時你的 Redis 實例中還存儲了 bigkey,,那么在淘汰刪除 bigkey 釋放內(nèi)存時,也會耗時比較久,。

6.fork耗時嚴重

當 Redis 開啟了后臺 RDB 和 AOF rewrite 后,,在執(zhí)行時,,它們都需要主進程創(chuàng)建出一個子進程進行數(shù)據(jù)的持久化。

主進程創(chuàng)建子進程,,會調(diào)用操作系統(tǒng)提供的 fork 函數(shù),。

而 fork 在執(zhí)行過程中,主進程需要拷貝自己的內(nèi)存頁表給子進程,,如果這個實例很大,,那么這個拷貝的過程也會比較耗時。

而且這個 fork 過程會消耗大量的 CPU 資源,,在完成 fork 之前,,整個 Redis 實例會被阻塞住,無法處理任何客戶端請求,。

如果此時你的 CPU 資源本來就很緊張,,那么 fork 的耗時會更長,甚至達到秒級,,這會嚴重影響 Redis 的性能,。

那如何確認確實是因為 fork 耗時導致的 Redis 延遲變大呢?

你可以在 Redis 上執(zhí)行 INFO 命令,,查看 latest_fork_usec 項,,單位微秒。

# 上一次 fork 耗時,,單位微秒latest_fork_usec:59477

這個時間就是主進程在 fork 子進程期間,,整個實例阻塞無法處理客戶端請求的時間。

如果你發(fā)現(xiàn)這個耗時很久,,就要警惕起來了,,這意味在這期間,你的整個 Redis 實例都處于不可用的狀態(tài),。

除了數(shù)據(jù)持久化會生成 RDB 之外,,當主從節(jié)點第一次建立數(shù)據(jù)同步時,主節(jié)點也創(chuàng)建子進程生成 RDB,,然后發(fā)給從節(jié)點進行一次全量同步,,所以,這個過程也會對 Redis 產(chǎn)生性能影響,。

圖片

7.開啟內(nèi)存大頁

除了上面講到的子進程 RDB 和 AOF rewrite 期間,fork 耗時導致的延時變大之外,,這里還有一個方面也會導致性能問題,,這就是操作系統(tǒng)是否開啟了內(nèi)存大頁機制

什么是內(nèi)存大頁,?

我們都知道,,應用程序向操作系統(tǒng)申請內(nèi)存時,,是按內(nèi)存頁進行申請的,而常規(guī)的內(nèi)存頁大小是 4KB,。

Linux 內(nèi)核從 2.6.38 開始,,支持了內(nèi)存大頁機制,該機制允許應用程序以 2MB 大小為單位,,向操作系統(tǒng)申請內(nèi)存,。

應用程序每次向操作系統(tǒng)申請的內(nèi)存單位變大了,但這也意味著申請內(nèi)存的耗時變長,。

這對 Redis 會有什么影響呢,?

當 Redis 在執(zhí)行后臺 RDB,采用 fork 子進程的方式來處理,。但主進程 fork 子進程后,,此時的主進程依舊是可以接收寫請求的,而進來的寫請求,,會采用 Copy On Write(寫時復制)的方式操作內(nèi)存數(shù)據(jù),。

也就是說,主進程一旦有數(shù)據(jù)需要修改,,Redis 并不會直接修改現(xiàn)有內(nèi)存中的數(shù)據(jù),,而是先將這塊內(nèi)存數(shù)據(jù)拷貝出來,再修改這塊新內(nèi)存的數(shù)據(jù),,這就是所謂的「寫時復制」,。

寫時復制你也可以理解成,誰需要發(fā)生寫操作,,誰就需要先拷貝,,再修改。

這樣做的好處是,,父進程有任何寫操作,,并不會影響子進程的數(shù)據(jù)持久化(子進程只持久化 fork 這一瞬間整個實例中的所有數(shù)據(jù)即可,不關心新的數(shù)據(jù)變更,,因為子進程只需要一份內(nèi)存快照,,然后持久化到磁盤上)。

但是請注意,,主進程在拷貝內(nèi)存數(shù)據(jù)時,,這個階段就涉及到新內(nèi)存的申請,如果此時操作系統(tǒng)開啟了內(nèi)存大頁,,那么在此期間,,客戶端即便只修改 10B 的數(shù)據(jù),Redis 在申請內(nèi)存時也會以 2MB 為單位向操作系統(tǒng)申請,,申請內(nèi)存的耗時變長,,進而導致每個寫請求的延遲增加,,影響到 Redis 性能。

同樣地,,如果這個寫請求操作的是一個 bigkey,,那主進程在拷貝這個 bigkey 內(nèi)存塊時,一次申請的內(nèi)存會更大,,時間也會更久,。可見,,bigkey 在這里又一次影響到了性能,。

8.開啟AOF

前面我們分析了 RDB 和 AOF rewrite 對 Redis 性能的影響,主要關注點在 fork 上,。

其實,,關于數(shù)據(jù)持久化方面,還有影響 Redis 性能的因素,,這次我們重點來看 AOF 數(shù)據(jù)持久化,。

如果你的 AOF 配置不合理,還是有可能會導致性能問題,。

當 Redis 開啟 AOF 后,,其工作原理如下:

1.Redis 執(zhí)行寫命令后,把這個命令寫入到 AOF 文件內(nèi)存中(write 系統(tǒng)調(diào)用)

2.Redis 根據(jù)配置的 AOF 刷盤策略,,把 AOF 內(nèi)存數(shù)據(jù)刷到磁盤上(fsync 系統(tǒng)調(diào)用)

為了保證 AOF 文件數(shù)據(jù)的安全性,,Redis 提供了 3 種刷盤機制:

1.appendfsync always:主線程每次執(zhí)行寫操作后立即刷盤,此方案會占用比較大的磁盤 IO 資源,,但數(shù)據(jù)安全性最高

2.appendfsync no:主線程每次寫操作只寫內(nèi)存就返回,,內(nèi)存數(shù)據(jù)什么時候刷到磁盤,交由操作系統(tǒng)決定,,此方案對性能影響最小,,但數(shù)據(jù)安全性也最低,Redis 宕機時丟失的數(shù)據(jù)取決于操作系統(tǒng)刷盤時機

3.appendfsync everysec:主線程每次寫操作只寫內(nèi)存就返回,,然后由后臺線程每隔 1 秒執(zhí)行一次刷盤操作(觸發(fā)fsync系統(tǒng)調(diào)用),,此方案對性能影響相對較小,但當 Redis 宕機時會丟失 1 秒的數(shù)據(jù)

看到這里,,我猜你肯定和大多數(shù)人的想法一樣,,選比較折中的方案 appendfsync everysec 就沒問題了吧?

這個方案優(yōu)勢在于,,Redis 主線程寫完內(nèi)存后就返回,,具體的刷盤操作是放到后臺線程中執(zhí)行的,后臺線程每隔 1 秒把內(nèi)存中的數(shù)據(jù)刷到磁盤中,。

這種方案既兼顧了性能,,又盡可能地保證了數(shù)據(jù)安全,是不是覺得很完美,?

但是,,這里我要給你潑一盆冷水了,采用這種方案你也要警惕一下,,因為這種方案還是存在導致 Redis 延遲變大的情況發(fā)生,,甚至會阻塞整個 Redis。

你試想這樣一種情況:當 Redis 后臺線程在執(zhí)行 AOF 文件刷盤時,,如果此時磁盤的 IO 負載很高,,那這個后臺線程在執(zhí)行刷盤操作(fsync系統(tǒng)調(diào)用)時就會被阻塞住。

此時的主線程依舊會接收寫請求,,緊接著,,主線程又需要把數(shù)據(jù)寫到文件內(nèi)存中(write 系統(tǒng)調(diào)用),當主線程使用后臺子線程執(zhí)行了一次 fsync,,需要再次把新接收的操作記錄寫回磁盤時,,如果主線程發(fā)現(xiàn)上一次的 fsync 還沒有執(zhí)行完,那么它就會阻塞,。所以,,如果后臺子線程執(zhí)行的 fsync 頻繁阻塞的話(比如 AOF 重寫占用了大量的磁盤 IO 帶寬),主線程也會阻塞,,導致 Redis 性能變慢,。

圖片

看到了么?在這個過程中,,主線程依舊有阻塞的風險,。

所以,盡管你的 AOF 配置為 appendfsync everysec,,也不能掉以輕心,,要警惕磁盤壓力過大導致的 Redis 有性能問題。

那什么情況下會導致磁盤 IO 負載過大,?以及如何解決這個問題呢,?

我總結(jié)了以下幾種情況,你可以參考進行問題排查:

1.子進程正在執(zhí)行 AOF rewrite,,這個過程會占用大量的磁盤 IO 資源

2.有其他應用程序在執(zhí)行大量的寫文件操作,,也會占用磁盤 IO 資源

對于情況1,說白了就是,,Redis 的 AOF 后臺子線程刷盤操作,,撞上了子進程 AOF rewrite!

9.綁定CPU

很多時候,我們在部署服務時,,為了提高服務性能,,降低應用程序在多個 CPU 核心之間的上下文切換帶來的性能損耗,通常采用的方案是進程綁定 CPU 的方式提高性能,。

我們都知道,,一般現(xiàn)代的服務器會有多個 CPU,而每個 CPU 又包含多個物理核心,,每個物理核心又分為多個邏輯核心,,每個物理核下的邏輯核共用 L1/L2 Cache。

而 Redis Server 除了主線程服務客戶端請求之外,,還會創(chuàng)建子進程,、子線程。

其中子進程用于數(shù)據(jù)持久化,,而子線程用于執(zhí)行一些比較耗時操作,,例如異步釋放 fd、異步 AOF 刷盤,、異步 lazy-free 等等,。

如果你把 Redis 進程只綁定了一個 CPU 邏輯核心上,那么當 Redis 在進行數(shù)據(jù)持久化時,,fork 出的子進程會繼承父進程的 CPU 使用偏好,。

而此時的子進程會消耗大量的 CPU 資源進行數(shù)據(jù)持久化(把實例數(shù)據(jù)全部掃描出來需要耗費CPU),這就會導致子進程會與主進程發(fā)生 CPU 爭搶,,進而影響到主進程服務客戶端請求,,訪問延遲變大。

這就是 Redis 綁定 CPU 帶來的性能問題,。

10.使用Swap

如果你發(fā)現(xiàn) Redis 突然變得非常慢,,每次的操作耗時都達到了幾百毫秒甚至秒級,那此時你就需要檢查 Redis 是否使用到了 Swap,,在這種情況下 Redis 基本上已經(jīng)無法提供高性能的服務了,。

什么是 Swap?為什么使用 Swap 會導致 Redis 的性能下降,?

如果你對操作系統(tǒng)有些了解,,就會知道操作系統(tǒng)為了緩解內(nèi)存不足對應用程序的影響,允許把一部分內(nèi)存中的數(shù)據(jù)換到磁盤上,,以達到應用程序?qū)?nèi)存使用的緩沖,,這些內(nèi)存數(shù)據(jù)被換到磁盤上的區(qū)域,就是 Swap,。

問題就在于,,當內(nèi)存中的數(shù)據(jù)被換到磁盤上后,Redis 再訪問這些數(shù)據(jù)時,就需要從磁盤上讀取,,訪問磁盤的速度要比訪問內(nèi)存慢幾百倍,!

尤其是針對 Redis 這種對性能要求極高、性能極其敏感的數(shù)據(jù)庫來說,,這個操作延時是無法接受的,。

此時,,你需要檢查 Redis 機器的內(nèi)存使用情況,,確認是否存在使用了 Swap。

你可以通過以下方式來查看 Redis 進程是否使用到了 Swap:

# 先找到 Redis 的進程 ID$ ps -aux | grep redis-server# 查看 Redis Swap 使用情況$ cat /proc/$pid/smaps | egrep '^(Swap|Size)'
輸出結(jié)果如下:
Size:               1256 kBSwap:                  0 kBSize:                  4 kBSwap:                  0 kBSize:                132 kBSwap:                  0 kBSize:              63488 kBSwap:                  0 kBSize:                132 kBSwap:                  0 kBSize:              65404 kBSwap:                  0 kBSize:            1921024 kBSwap:                  0 kB
每一行 Size 表示 Redis 所用的一塊內(nèi)存大小,,Size 下面的 Swap 就表示這塊 Size 大小的內(nèi)存,,有多少數(shù)據(jù)已經(jīng)被換到磁盤上了,如果這兩個值相等,,說明這塊內(nèi)存的數(shù)據(jù)都已經(jīng)完全被換到磁盤上了,。

如果只是少量數(shù)據(jù)被換到磁盤上,例如每一塊 Swap 占對應 Size 的比例很小,,那影響并不是很大,。如果是幾百兆甚至上 GB 的內(nèi)存被換到了磁盤上,那么你就需要警惕了,,這種情況 Redis 的性能肯定會急劇下降,。

11.碎片整理

Redis 的數(shù)據(jù)都存儲在內(nèi)存中,當我們的應用程序頻繁修改 Redis 中的數(shù)據(jù)時,,就有可能會導致 Redis 產(chǎn)生內(nèi)存碎片,。

內(nèi)存碎片會降低 Redis 的內(nèi)存使用率,我們可以通過執(zhí)行 INFO 命令,,得到這個實例的內(nèi)存碎片率:

# Memoryused_memory:5709194824used_memory_human:5.32Gused_memory_rss:8264855552used_memory_rss_human:7.70G...mem_fragmentation_ratio:1.45
這個內(nèi)存碎片率是怎么計算的,?

很簡單,mem_fragmentation_ratio = used_memory_rss / used_memory,。

其中 used_memory 表示 Redis 存儲數(shù)據(jù)的內(nèi)存大小,,而 used_memory_rss 表示操作系統(tǒng)實際分配給 Redis 進程的大小。

如果 mem_fragmentation_ratio > 1.5,,說明內(nèi)存碎片率已經(jīng)超過了 50%,,這時我們就需要采取一些措施來降低內(nèi)存碎片了。

解決的方案一般如下:

1.如果你使用的是 Redis 4.0 以下版本,,只能通過重啟實例來解決

2.如果你使用的是 Redis 4.0 版本,,它正好提供了自動碎片整理的功能,可以通過配置開啟碎片自動整理,。

但是,,開啟內(nèi)存碎片整理,它也有可能會導致 Redis 性能下降。

原因在于,,Redis 的碎片整理工作是也在主線程中執(zhí)行的,,當其進行碎片整理時,必然會消耗 CPU 資源,,產(chǎn)生更多的耗時,,從而影響到客戶端的請求。

所以,,當你需要開啟這個功能時,,最好提前測試評估它對 Redis 的影響。

Redis 碎片整理的參數(shù)配置如下:

# 開啟自動內(nèi)存碎片整理(總開關)activedefrag yes
# 內(nèi)存使用 100MB 以下,,不進行碎片整理active-defrag-ignore-bytes 100mb
# 內(nèi)存碎片率超過 10%,,開始碎片整理active-defrag-threshold-lower 10# 內(nèi)存碎片率超過 100%,盡最大努力碎片整理active-defrag-threshold-upper 100
# 內(nèi)存碎片整理占用 CPU 資源最小百分比active-defrag-cycle-min 1# 內(nèi)存碎片整理占用 CPU 資源最大百分比active-defrag-cycle-max 25
# 碎片整理期間,,對于 List/Set/Hash/ZSet 類型元素一次 Scan 的數(shù)量active-defrag-max-scan-fields 1000

二,、Redis如何優(yōu)化

1.慢查詢優(yōu)化

1.盡量不使用 O(N) 以上復雜度過高的命令,對于數(shù)據(jù)的聚合操作,,放在客戶端做

2.執(zhí)行 O(N) 命令,,保證 N 盡量的小(推薦 N <= 300),,每次獲取盡量少的數(shù)據(jù),,讓 Redis 可以及時處理返回

2.集中過期優(yōu)化

一般有兩種方案來規(guī)避這個問題:

1.集中過期 key 增加一個隨機過期時間,把集中過期的時間打散,,降低 Redis 清理過期 key 的壓力

2.如果你使用的 Redis 是 4.0 以上版本,,可以開啟 lazy-free 機制,當刪除過期 key 時,,把釋放內(nèi)存的操作放到后臺線程中執(zhí)行,,避免阻塞主線程。

第一種方案,,在設置 key 的過期時間時,,增加一個隨機時間,偽代碼可以這么寫:

# 在過期時間點之后的 5 分鐘內(nèi)隨機過期掉redis.expireat(key, expire_time + random(300))
第二種方案,,Redis 4.0 以上版本,,開啟 lazy-free 機制:
# 釋放過期 key 的內(nèi)存,放到后臺線程執(zhí)行lazyfree-lazy-expire yes
運維層面,,你需要把 Redis 的各項運行狀態(tài)數(shù)據(jù)監(jiān)控起來,,在 Redis 上執(zhí)行 INFO 命令就可以拿到這個實例所有的運行狀態(tài)數(shù)據(jù)。

在這里我們需要重點關注 expired_keys 這一項,,它代表整個實例到目前為止,,累計刪除過期 key 的數(shù)量,。

你需要把這個指標監(jiān)控起來,當這個指標在很短時間內(nèi)出現(xiàn)了突增,,需要及時報警出來,,然后與業(yè)務應用報慢的時間點進行對比分析,確認時間是否一致,,如果一致,,則可以確認確實是因為集中過期 key 導致的延遲變大。

3.實例內(nèi)存達到上限優(yōu)化

1.避免存儲 bigkey,,降低釋放內(nèi)存的耗時

2.淘汰策略改為隨機淘汰,,隨機淘汰比 LRU 要快很多(視業(yè)務情況調(diào)整)

3.拆分實例,把淘汰 key 的壓力分攤到多個實例上

4.如果使用的是 Redis 4.0 以上版本,,開啟 layz-free 機制,,把淘汰 key 釋放內(nèi)存的操作放到后臺線程中執(zhí)行(配置 lazyfree-lazy-eviction = yes)

4.fork耗時嚴重優(yōu)化

1.控制 Redis 實例的內(nèi)存:盡量在 10G 以下,執(zhí)行 fork 的耗時與實例大小有關,,實例越大,耗時越久,。

2.合理配置數(shù)據(jù)持久化策略:在 slave 節(jié)點執(zhí)行 RDB 備份,,推薦在低峰期執(zhí)行,而對于丟失數(shù)據(jù)不敏感的業(yè)務(例如把 Redis 當做純緩存使用),,可以關閉 AOF 和 AOF rewrite,。

3.Redis 實例不要部署在虛擬機上:fork 的耗時也與系統(tǒng)也有關,虛擬機比物理機耗時更久,。

4.降低主從庫全量同步的概率:適當調(diào)大 repl-backlog-size 參數(shù),,避免主從全量同步。

從建立同步時,,優(yōu)先檢測是否可以嘗試只同步部分數(shù)據(jù),,這種情況就是針對于之前已經(jīng)建立好了復制鏈路,只是因為故障導致臨時斷開,,故障恢復后重新建立同步時,,為了避免全量同步的資源消耗,Redis會優(yōu)先嘗試部分數(shù)據(jù)同步,,如果條件不符合,,才會觸發(fā)全量同步。這個判斷依據(jù)就是在master上維護的復制緩沖區(qū)大小,,如果這個緩沖區(qū)配置的過小,,很有可能在主從斷開復制的這段時間內(nèi),master產(chǎn)生的寫入導致復制緩沖區(qū)的數(shù)據(jù)被覆蓋,,重新建立同步時的slave需要同步的offset位置在master的緩沖區(qū)中找不到,,那么此時就會觸發(fā)全量同步,。如何避免這種情況?解決方案就是適當調(diào)大復制緩沖區(qū)repl-backlog-size的大小,,這個緩沖區(qū)的大小默認為1MB,,如果實例寫入量比較大,可以針對性調(diào)大此配置,。

5.多核CPU優(yōu)化

那如何解決這個問題呢,?

如果你確實想要綁定 CPU,可以優(yōu)化的方案是,,不要讓 Redis 進程只綁定在一個 CPU 邏輯核上,,而是綁定在多個邏輯核心上,而且,,綁定的多個邏輯核心最好是同一個物理核心,,這樣它們還可以共用 L1/L2 Cache。

當然,,即便我們把 Redis 綁定在多個邏輯核心上,,也只能在一定程度上緩解主線程、子進程,、后臺線程在 CPU 資源上的競爭,。

因為這些子進程、子線程還是會在這多個邏輯核心上進行切換,,存在性能損耗,。?

如何再進一步優(yōu)化?

可能你已經(jīng)想到了,,我們是否可以讓主線程,、子進程、后臺線程,,分別綁定在固定的 CPU 核心上,,不讓它們來回切換,這樣一來,,他們各自使用的 CPU 資源互不影響,。

其實,這個方案 Redis 官方已經(jīng)想到了,。

Redis 在 6.0 版本已經(jīng)推出了這個功能,,我們可以通過以下配置,對主線程,、后臺線程,、后臺 RDB 進程、AOF rewrite 進程,,綁定固定的 CPU 邏輯核心:

Redis6.0 前綁定CPU核

taskset -c 0 ./redis-server
Redis6.0 后綁定CPU核
# Redis Server 和 IO 線程綁定到 CPU核心 0,2,4,6server_cpulist 0-7:2# 后臺子線程綁定到 CPU核心 1,3bio_cpulist 1,3# 后臺 AOF rewrite 進程綁定到 CPU 核心 8,9,10,11aof_rewrite_cpulist 8-11# 后臺 RDB 進程綁定到 CPU 核心 1,10,11# bgsave_cpulist 1,10-1
如果你使用的正好是 Redis 6.0 版本,,就可以通過以上配置,,來進一步提高 Redis 性能。

這里我需要提醒你的是,,一般來說,,Redis 的性能已經(jīng)足夠優(yōu)秀,除非你對 Redis 的性能有更加嚴苛的要求,,否則不建議你綁定 CPU,。

6.查看Redis內(nèi)存是否發(fā)生Swap

$ redis-cli info | grep process_idprocess_id: 5332
然后,進入 Redis 所在機器的 /proc 目錄下的該進程目錄中:
$ cd /proc/5332
最后,,運行下面的命令,,查看該 Redis 進程的使用情況。在這兒,,我只截取了部分結(jié)果:
$cat smaps | egrep '^(Swap|Size)'Size: 584 kBSwap: 0 kBSize: 4 kBSwap: 4 kBSize: 4 kBSwap: 0 kBSize: 462044 kBSwap: 462008 kBSize: 21392 kBSwap: 0 kB
一旦發(fā)生內(nèi)存 swap,,最直接的解決方法就是增加機器內(nèi)存。如果該實例在一個 Redis 切片集群中,,可以增加 Redis 集群的實例個數(shù),,來分攤每個實例服務的數(shù)據(jù)量,進而減少每個實例所需的內(nèi)存量,。

7.內(nèi)存大頁

如果采用了內(nèi)存大頁,,那么,即使客戶端請求只修改 100B 的數(shù)據(jù),,Redis 也需要拷貝 2MB 的大頁。相反,,如果是常規(guī)內(nèi)存頁機制,,只用拷貝 4KB。兩者相比,,你可以看到,,當客戶端請求修改或新寫入數(shù)據(jù)較多時,內(nèi)存大頁機制將導致大量的拷貝,,這就會影響 Redis 正常的訪存操作,,最終導致性能變慢。

首先,,我們要先排查下內(nèi)存大頁,。方法是:在 Redis 實例運行的機器上執(zhí)行如下命令:

$ cat /sys/kernel/mm/transparent_hugepage/enabled[always] madvise never
如果執(zhí)行結(jié)果是 always,就表明內(nèi)存大頁機制被啟動了,;如果是 never,,就表示,內(nèi)存大頁機制被禁止,。

在實際生產(chǎn)環(huán)境中部署時,,我建議你不要使用內(nèi)存大頁機制,,操作也很簡單,只需要執(zhí)行下面的命令就可以了:

echo never /sys/kernel/mm/transparent_hugepage/enabled
其實,,操作系統(tǒng)提供的內(nèi)存大頁機制,,其優(yōu)勢是,可以在一定程序上降低應用程序申請內(nèi)存的次數(shù),。

但是對于 Redis 這種對性能和延遲極其敏感的數(shù)據(jù)庫來說,,我們希望 Redis 在每次申請內(nèi)存時,耗時盡量短,,所以我不建議你在 Redis 機器上開啟這個機制,。

8.刪除使用Lazy Free

支持版本:Redis 4.0+

8.1 主動刪除鍵使用lazy free

UNLINK命令
127.0.0.1:7000> LLEN mylist(integer) 2000000127.0.0.1:7000> UNLINK mylist(integer) 1127.0.0.1:7000> SLOWLOG get1) 1) (integer) 1 2) (integer) 1505465188 3) (integer) 30 4) 1) 'UNLINK' 2) 'mylist' 5) '127.0.0.1:17015' 6) ''
注意:DEL命令,還是并發(fā)阻塞的刪除操作
FLUSHALL/FLUSHDB ASYNC
127.0.0.1:7000> DBSIZE(integer) 1812295127.0.0.1:7000> flushall  //同步清理實例數(shù)據(jù),,180萬個key耗時1020毫秒OK(1.02s)127.0.0.1:7000> DBSIZE(integer) 1812637127.0.0.1:7000> flushall async  //異步清理實例數(shù)據(jù),,180萬個key耗時約9毫秒OK127.0.0.1:7000> SLOWLOG get 1) 1) (integer) 2996109    2) (integer) 1505465989    3) (integer) 9274       //指令運行耗時9.2毫秒    4) 1) 'flushall'       2) 'async'    5) '127.0.0.1:20110'    6) ''

8.2 被動刪除鍵使用lazy free

lazy free應用于被動刪除中,目前有4種場景,,每種場景對應一個配置參數(shù),;默認都是關閉。

lazyfree-lazy-eviction nolazyfree-lazy-expire nolazyfree-lazy-server-del noslave-lazy-flush no
lazyfree-lazy-eviction

針對redis內(nèi)存使用達到maxmeory,,并設置有淘汰策略時,;在被動淘汰鍵時,是否采用lazy free機制,;因為此場景開啟lazy free, 可能使用淘汰鍵的內(nèi)存釋放不及時,,導致redis內(nèi)存超用,超過maxmemory的限制,。此場景使用時,,請結(jié)合業(yè)務測試。(生產(chǎn)環(huán)境不建議設置yes

lazyfree-lazy-expire

針對設置有TTL的鍵,,達到過期后,,被redis清理刪除時是否采用lazy free機制;此場景建議開啟,,因TTL本身是自適應調(diào)整的速度,。

lazyfree-lazy-server-del

針對有些指令在處理已存在的鍵時,會帶有一個隱式的DEL鍵的操作,。如rename命令,,當目標鍵已存在,redis會先刪除目標鍵,如果這些目標鍵是一個big key,那就會引入阻塞刪除的性能問題,。此參數(shù)設置就是解決這類問題,,建議可開啟。

slave-lazy-flush

針對slave進行全量數(shù)據(jù)同步,,slave在加載master的RDB文件前,,會運行flushall來清理自己的數(shù)據(jù)場景,, 參數(shù)設置決定是否采用異常flush機制。如果內(nèi)存變動不大,,建議可開啟,。可減少全量同步耗時,,從而減少主庫因輸出緩沖區(qū)爆漲引起的內(nèi)存使用增長,。

8.3 lazy free的監(jiān)控

lazy free能監(jiān)控的數(shù)據(jù)指標,只有一個值:lazyfree_pending_objects,,表示redis執(zhí)行l(wèi)azy free操作,,在等待被實際回收內(nèi)容的鍵個數(shù)。并不能體現(xiàn)單個大鍵的元素個數(shù)或等待lazy free回收的內(nèi)存大小,。所以此值有一定參考值,,可監(jiān)測redis lazy free的效率或堆積鍵數(shù)量;比如在flushall async場景下會有少量的堆積,。

# info memory
# Memorylazyfree_pending_objects:0
注意事項:unlink命令入口函數(shù)unlinkCommand()和del調(diào)用相同函數(shù)delGenericCommand()進行刪除KEY操作,,使用lazy標識是否為lazyfree調(diào)用。如果是lazyfree,則調(diào)用dbAsyncDelete()函數(shù),。
但并非每次unlink命令就一定啟用lazy free,,redis會先判斷釋放KEY的代價(cost),當cost大于LAZYFREE_THRESHOLD(64)才進行l(wèi)azy free.
釋放key代價計算函數(shù)lazyfreeGetFreeEffort(),集合類型鍵,,且滿足對應編碼,,cost就是集合鍵的元數(shù)個數(shù),否則cost就是1. 
舉例:
1 一個包含100元素的list key, 它的free cost就是100 
2 一個512MB的string key, 它的free cost是1 所以可以看出,,redis的lazy free的cost計算主要時間復雜度相關,。

9.AOF優(yōu)化

Redis 提供了一個配置項,當子進程在 AOF rewrite 期間,,可以讓后臺子線程不執(zhí)行刷盤(不觸發(fā) fsync 系統(tǒng)調(diào)用)操作。

這相當于在 AOF rewrite 期間,,臨時把 appendfsync 設置為了 none,,配置如下:

# AOF rewrite 期間,AOF 后臺子線程不進行刷盤操作# 相當于在這期間,,臨時把 appendfsync 設置為了 noneno-appendfsync-on-rewrite yes
當然,,開啟這個配置項,在 AOF rewrite 期間,,如果實例發(fā)生宕機,,那么此時會丟失更多的數(shù)據(jù),性能和數(shù)據(jù)安全性,,你需要權衡后進行選擇,。

如果占用磁盤資源的是其他應用程序,,那就比較簡單了,你需要定位到是哪個應用程序在大量寫磁盤,,然后把這個應用程序遷移到其他機器上執(zhí)行就好了,,避免對 Redis 產(chǎn)生影響。

當然,,如果你對 Redis 的性能和數(shù)據(jù)安全都有很高的要求,,那么建議從硬件層面來優(yōu)化,更換為 SSD 磁盤,,提高磁盤的 IO 能力,,保證 AOF 期間有充足的磁盤資源可以使用。同時盡可能讓Redis運行在獨立的機器上,。

10.Swap優(yōu)化

1.增加機器的內(nèi)存,,讓 Redis 有足夠的內(nèi)存可以使用

2.整理內(nèi)存空間,釋放出足夠的內(nèi)存供 Redis 使用,,然后釋放 Redis 的 Swap,,讓 Redis 重新使用內(nèi)存

釋放 Redis 的 Swap 過程通常要重啟實例,為了避免重啟實例對業(yè)務的影響,,一般會先進行主從切換,,然后釋放舊主節(jié)點的 Swap,重啟舊主節(jié)點實例,,待從庫數(shù)據(jù)同步完成后,,再進行主從切換即可。

預防的辦法就是,,你需要對 Redis 機器的內(nèi)存和 Swap 使用情況進行監(jiān)控,,在內(nèi)存不足或使用到 Swap 時報警出來,及時處理,。

三,、Redis變慢了排查步驟

1、獲取 Redis 實例在當前環(huán)境下的基線性能,。

2,、是否用了慢查詢命令?如果是的話,,就使用其他命令替代慢查詢命令,,或者把聚合計算命令放在客戶端做。

3,、是否對過期 key 設置了相同的過期時間,?對于批量刪除的 key,可以在每個 key 的過期時間上加一個隨機數(shù),避免同時刪除,。

4,、是否存在 bigkey?對于 bigkey 的刪除操作,,如果你的 Redis 是 4.0 及以上的版本,,可以直接利用異步線程機制減少主線程阻塞;如果是 Redis 4.0 以前的版本,,可以使用 SCAN 命令迭代刪除,;對于 bigkey 的集合查詢和聚合操作,可以使用 SCAN 命令在客戶端完成,。

5,、Redis AOF 配置級別是什么?業(yè)務層面是否的確需要這一可靠性級別,?如果我們需要高性能,,同時也允許數(shù)據(jù)丟失,可以將配置項 no-appendfsync-on-rewrite 設置為 yes,,避免 AOF 重寫和 fsync 競爭磁盤 IO 資源,,導致 Redis 延遲增加。當然,, 如果既需要高性能又需要高可靠性,,最好使用高速固態(tài)盤作為 AOF 日志的寫入盤。

6,、Redis 實例的內(nèi)存使用是否過大,?發(fā)生 swap 了嗎?如果是的話,,就增加機器內(nèi)存,,或者是使用 Redis 集群,分攤單機 Redis 的鍵值對數(shù)量和內(nèi)存壓力,。同時,,要避免出現(xiàn) Redis 和其他內(nèi)存需求大的應用共享機器的情況。

7,、在 Redis 實例的運行環(huán)境中,,是否啟用了透明大頁機制?如果是的話,,直接關閉內(nèi)存大頁機制就行了。

8,、是否運行了 Redis 主從集群,?如果是的話,把主庫實例的數(shù)據(jù)量大小控制在 2~4GB,以免主從復制時,,從庫因加載大的 RDB 文件而阻塞,。

9、是否使用了多核 CPU 或 NUMA 架構(gòu)的機器運行 Redis 實例,?使用多核 CPU 時,,可以給 Redis 實例綁定物理核;使用 NUMA 架構(gòu)時,,注意把 Redis 實例和網(wǎng)絡中斷處理程序運行在同一個 CPU Socket 上,。

“飛天免費試用計劃”來啦!免費領用

阿里云推出“飛天免費試用計劃”,,面向國內(nèi)1000萬云上開發(fā)者,,提供包括ECS、數(shù)據(jù)庫PolarDB,、機器學習PAI等在內(nèi) 60 多款云產(chǎn)品的免費試用,,時長支持1個月、3個月到長期免費,。

    本站是提供個人知識管理的網(wǎng)絡存儲空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點,。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導購買等信息,謹防詐騙,。如發(fā)現(xiàn)有害或侵權內(nèi)容,,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多