溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Redis高延遲時(shí)會(huì)發(fā)生什么

發(fā)布時(shí)間:2021-11-30 09:15:50 來(lái)源:億速云 閱讀:153 作者:柒染 欄目:數(shù)據(jù)庫(kù)

這篇文章將為大家詳細(xì)講解有關(guān)Redis高延遲時(shí)會(huì)發(fā)生什么,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。

Redis 是一種內(nèi)存數(shù)據(jù)庫(kù),將數(shù)據(jù)保存在內(nèi)存中,讀寫(xiě)效率要比傳統(tǒng)的將數(shù)據(jù)保存在磁盤(pán)上的數(shù)據(jù)庫(kù)要快很多。但是 Redis 也會(huì)發(fā)生延遲時(shí),這是就需要我們對(duì)其產(chǎn)生原因有深刻的了解,以便于快速排查問(wèn)題,解決 Redis的延遲問(wèn)題。

一條命令執(zhí)行過(guò)程

在本文場(chǎng)景下,延遲 (latency) 是指從客戶端發(fā)送命令到客戶端接收到命令返回值的時(shí)間間隔。所以我們先來(lái)看一下 Redis 一條命令執(zhí)行的步驟,其中每個(gè)步驟出問(wèn)題都可能導(dǎo)致高延遲。

Redis高延遲時(shí)會(huì)發(fā)生什么

上圖是 Redis 客戶端發(fā)送一條命令的執(zhí)行過(guò)程示意圖,綠色的是執(zhí)行步驟,而藍(lán)色的則是可能出現(xiàn)的導(dǎo)致高延遲的原因。

網(wǎng)絡(luò)連接限制、網(wǎng)絡(luò)傳輸速率和CPU性能等是所有服務(wù)端都可能產(chǎn)生的性能問(wèn)題。但是 Redis 有自己獨(dú)有的可能導(dǎo)致高延遲的問(wèn)題:命令或者數(shù)據(jù)結(jié)構(gòu)誤用、持久化阻塞和內(nèi)存交換。

而且更為致命的是,Redis 采用單線程和事件驅(qū)動(dòng)的機(jī)制來(lái)處理網(wǎng)絡(luò)請(qǐng)求,分別有對(duì)應(yīng)的連接應(yīng)答處理器,命令請(qǐng)求處理器和命令回復(fù)處理器來(lái)處理客戶端的網(wǎng)絡(luò)請(qǐng)求事件,處理完一個(gè)事件就繼續(xù)處理隊(duì)列中的下一個(gè)。一條命令處理出現(xiàn)了高延遲會(huì)影響接下來(lái)處于排隊(duì)狀態(tài)的其他命令。有關(guān) Redis 事件處理機(jī)制的可以參考本篇文章。

Redis高延遲時(shí)會(huì)發(fā)生什么

對(duì)于高延遲,Redis 原生提供慢查詢統(tǒng)計(jì)功能,執(zhí)行 slowlog get {n} 命令可以獲取最近的 n 條慢查詢命令,默認(rèn)對(duì)于執(zhí)行超過(guò)10毫秒(可配置)的命令都會(huì)記錄到一個(gè)定長(zhǎng)隊(duì)列中,線上實(shí)例建議設(shè)置為1毫秒便于及時(shí)發(fā)現(xiàn)毫秒級(jí)以上的命令。 

# 超過(guò) slowlog-log-slower-than 閾值的命令都會(huì)被記錄到慢查詢隊(duì)列中     # 隊(duì)列最大長(zhǎng)度為 slowlog-max-len     slowlog-log-slower-than 10000     slowlog-max-len 128

如果命令執(zhí)行時(shí)間在毫秒級(jí),則實(shí)例實(shí)際OPS只有1000左右。慢查詢隊(duì)列長(zhǎng)度默認(rèn)128,可適當(dāng)調(diào)大。慢查詢本身只記錄了命令執(zhí)行時(shí)間,不包括數(shù)據(jù)網(wǎng)絡(luò)傳輸時(shí)間和命令排隊(duì)時(shí)間,因此客戶端發(fā)生阻塞異常 后,可能不是當(dāng)前命令緩慢,而是在等待其他命令執(zhí)行。需要重點(diǎn)比對(duì)異常和慢查詢發(fā)生的時(shí)間點(diǎn),確認(rèn)是否有慢查詢?cè)斐傻拿钭枞抨?duì)。

slowlog的輸出格式如下所示。第一個(gè)字段表示該條記錄在所有慢日志中的序號(hào),最新的記錄被展示在最前面;第二個(gè)字段是這條記錄被記錄時(shí)的系統(tǒng)時(shí)間,可以用 date 命令來(lái)將其轉(zhuǎn)換為友好的格式第三個(gè)字段表示這條命令的響應(yīng)時(shí)間,單位為 us (微秒);第四個(gè)字段為對(duì)應(yīng)的 Redis 操作。 

> slowlog get     1) 1) (integer) 26     2) (integer) 1450253133     3) (integer) 43097     4) 1) "flushdb"

下面我們就來(lái)依次看一下不合理地使用命令或者數(shù)據(jù)結(jié)構(gòu)、持久化阻塞和內(nèi)存交換所導(dǎo)致的高延遲問(wèn)題。

不合理的命令或者數(shù)據(jù)結(jié)構(gòu)

一般來(lái)說(shuō) Redis 執(zhí)行命令速度都非常快,但是當(dāng)數(shù)據(jù)量達(dá)到一定級(jí)別時(shí),某些命令的執(zhí)行就會(huì)花費(fèi)大量時(shí)間,比如對(duì)一個(gè)包含上萬(wàn)個(gè)元素的 hash 結(jié)構(gòu)執(zhí)行 hgetall 操作,由于數(shù)據(jù)量比較大且命令算法復(fù)雜度是 O(n),這條命令執(zhí)行速度必然很慢。

這個(gè)問(wèn)題就是典型的不合理使用命令和數(shù)據(jù)結(jié)構(gòu)。對(duì)于高并發(fā)的場(chǎng)景我們應(yīng)該盡量避免在大對(duì)象上執(zhí)行算法復(fù)雜度超過(guò) O(n) 的命令。對(duì)于鍵值較多的 hash 結(jié)構(gòu)可以使用 scan 系列命令來(lái)逐步遍歷,而不是直接使用 hgetall 來(lái)全部獲取。

Redis 本身提供發(fā)現(xiàn)大對(duì)象的工具,對(duì)應(yīng)命令:redis-cli-h {ip} -p {port} bigkeys。這條命令會(huì)使用 scan 從指定的 Redis DB 中持續(xù)采樣,實(shí)時(shí)輸出當(dāng)時(shí)得到的 value 占用空間最大的 key 值,并在最后給出各種數(shù)據(jù)結(jié)構(gòu)的 biggest key 的總結(jié)報(bào)告。 

> redis-cli -h host -p 12345--bigkeys      # Scanning the entire keyspace to find biggest keys as well as      # average sizes per key type.  You can use -i 0.1 to sleep 0.1 sec      # per 100 SCAN commands (not usually needed).      [00.00%] Biggest hash   found so far 'idx:user'with2 fields      [00.00%] Biggest hash   found so far 'idx:product'with4 fields      [00.00%] Biggest hash   found so far 'idx:order'with24 fields      [02.29%] Biggest hash   found so far 'idx:fund'with26 fields      [02.29%] Biggest hash   found so far 'idx:pay'with79 fields      [04.45%] Biggestset    found so far 'indexed_word_set'with2482 members      [05.93%] Biggest hash   found so far 'idx:address'with259 fields      [11.79%] Biggest hash   found so far 'idx:reply'with296 fields      -------- summary -------      Sampled1484 keys in the keyspace!      Total key length in bytes is13488(avg len 9.09)      Biggestset found 'indexed_word_set' has 1482 members      Biggest   hash found 'idx:的' has 196 fields      0 strings with0 bytes (00.00% of keys, avg size 0.00)      0 lists with0 items (00.00% of keys, avg size 0.00)      2 sets with2710 members (00.13% of keys, avg size 855.00)      1482 hashs with7731 fields (99.87% of keys, avg size 4.54)      0 zsets with0 members (00.00% of keys, avg size 0.00)

持久化阻塞

對(duì)于開(kāi)啟了持久化功能的Redis節(jié)點(diǎn),需要排查是否是持久化導(dǎo)致的阻 塞。持久化引起主線程阻塞的操作主要有:fork 阻塞、AOF刷盤(pán)阻塞。

fork 操作發(fā)生在 RDB 和 AOF 重寫(xiě)時(shí),Redis 主線程調(diào)用 fork 操作產(chǎn)生共享內(nèi)存的子進(jìn)程,由子進(jìn)程完成對(duì)應(yīng)的持久化工作。如果 fork 操作本身耗時(shí)過(guò)長(zhǎng),必然會(huì)導(dǎo)致主線程的阻塞。

Redis高延遲時(shí)會(huì)發(fā)生什么

Redis 執(zhí)行 fork 操作產(chǎn)生的子進(jìn)程內(nèi)存占用量表現(xiàn)為與父進(jìn)程相同,理論上需要一倍的物理內(nèi)存來(lái)完成相應(yīng)的操作。但是 Linux 具有寫(xiě)時(shí)復(fù)制技術(shù) (copy-on-write),父子進(jìn)程會(huì)共享相同的物理內(nèi)存頁(yè),當(dāng)父進(jìn)程處理寫(xiě)請(qǐng)求時(shí)會(huì)對(duì)需要修改的頁(yè)復(fù)制出一份副本完成寫(xiě)操作,而子進(jìn)程依然讀取 fork 時(shí)整個(gè)父進(jìn)程的內(nèi)存快照。所以,一般來(lái)說(shuō),fork 不會(huì)消耗過(guò)多時(shí)間。

可以執(zhí)行 info stats命令獲取到 latestforkusec 指標(biāo),表示 Redis 最近一次 fork 操作耗時(shí),如果耗時(shí)很大,比如超過(guò)1秒,則需要做出優(yōu)化調(diào)整。 

> redis-cli -c -p 7000 info | grep -w latest_fork_usec     latest_fork_usec:315

當(dāng)我們開(kāi)啟AOF持久化功能時(shí),文件刷盤(pán)的方式一般采用每秒一次,后 臺(tái)線程每秒對(duì)AOF文件做 fsync 操作。當(dāng)硬盤(pán)壓力過(guò)大時(shí),fsync 操作需要等待,直到寫(xiě)入完成。如果主線程發(fā)現(xiàn)距離上一次的 fsync 成功超過(guò)2秒,為了數(shù)據(jù)安全性它會(huì)阻塞直到后臺(tái)線程執(zhí)行 fsync 操作完成。這種阻塞行為主要是硬盤(pán)壓力引起,可以查看 Redis日志識(shí)別出這種情況,當(dāng)發(fā)生這種阻塞行為時(shí),會(huì)打印如下日志: 

Asynchronous AOF fsync is taking too long(disk is busy). \      Writing the AOF buffer without waiting for fsync to complete, \      this may slow down Redis.

也可以查看 info persistence 統(tǒng)計(jì)中的 aofdelayedfsync 指標(biāo),每次發(fā)生 fdatasync 阻塞主線程時(shí)會(huì)累加。 

>info persistence     loading:0     aof_pending_bio_fsync:0     aof_delayed_fsync:0

內(nèi)存交換

內(nèi)存交換(swap)對(duì)于 Redis 來(lái)說(shuō)是非常致命的,Redis 保證高性能的一個(gè)重要前提是所有的數(shù)據(jù)在內(nèi)存中。如果操作系統(tǒng)把 Redis 使用的部分內(nèi)存換出到硬盤(pán),由于內(nèi)存與硬盤(pán)讀寫(xiě)速度差幾個(gè)數(shù)量級(jí),會(huì)導(dǎo)致發(fā)生交換后的 Redis 性能急劇下降。識(shí)別 Redis 內(nèi)存交換的檢查方法如下:

>redis-cli -p 6383 info server | grep process_id # 查詢 redis 進(jìn)程號(hào)    >cat /proc/4476/smaps | grep Swap# 查詢內(nèi)存交換大小    Swap: 0 kB    Swap: 4 kB    Swap: 0 kB    Swap: 0 kB

如果交換量都是0KB或者個(gè)別的是4KB,則是正?,F(xiàn)象,說(shuō)明Redis進(jìn)程內(nèi)存沒(méi)有被交換。

有很多方法可以避免內(nèi)存交換的發(fā)生。比如說(shuō):

  •  保證機(jī)器充足的可用內(nèi)存

  •  確保所有Redis實(shí)例設(shè)置最大可用內(nèi)存(maxmemory),防止極端情況下 Redis 內(nèi)存不可控的增長(zhǎng)。

  •  降低系統(tǒng)使用swap優(yōu)先級(jí),如 echo10>/proc/sys/vm/swappiness。 

關(guān)于Redis高延遲時(shí)會(huì)發(fā)生什么就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI