一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Redis常見延遲問題排查手冊!附33條優(yōu)化建議

 airen89 2020-09-24

作者介紹

Kaito,90 后,坐標(biāo)北京,6 年+工作經(jīng)驗(yàn),就職于一家移動互聯(lián)網(wǎng)公司,目前從事基礎(chǔ)架構(gòu)和數(shù)據(jù)庫中間件研發(fā)。

Redis作為內(nèi)存數(shù)據(jù)庫,擁有非常高的性能,單個(gè)實(shí)例的QPS能夠達(dá)到10W左右。但我們在使用Redis時(shí),經(jīng)常時(shí)不時(shí)會出現(xiàn)訪問延遲很大的情況,如果你不知道Redis的內(nèi)部實(shí)現(xiàn)原理,在排查問題時(shí)就會一頭霧水。

很多時(shí)候,Redis出現(xiàn)訪問延遲變大,都與我們的使用不當(dāng)或運(yùn)維不合理導(dǎo)致的。

Redis變慢了?常見延遲問題定位與分析

下面我們就來分析一下Redis在使用過程中,經(jīng)常會遇到的延遲問題以及如何定位和分析。

使用復(fù)雜度高的命令

如果在使用Redis時(shí),發(fā)現(xiàn)訪問延遲突然增大,如何進(jìn)行排查?

首先,第一步,建議你去查看一下Redis的慢日志。Redis提供了慢日志命令的統(tǒng)計(jì)功能,我們通過以下設(shè)置,就可以查看有哪些命令在執(zhí)行時(shí)延遲比較大。

首先設(shè)置Redis的慢日志閾值,只有超過閾值的命令才會被記錄,這里的單位是微妙,例如設(shè)置慢日志的閾值為5毫秒,同時(shí)設(shè)置只保留最近1000條慢日志記錄:

# 命令執(zhí)行超過5毫秒記錄慢日志

CONFIG SET slowlog-log-slower-than 5000

# 只保留最近1000條慢日志

CONFIG SET slowlog-max-len 1000

設(shè)置完成之后,所有執(zhí)行的命令如果延遲大于5毫秒,都會被Redis記錄下來,我們執(zhí)行SLOWLOG get 5查詢最近5條慢日志:

127.0.0.1:6379> SLOWLOG get 5

1) 1) (integer) 32693       # 慢日志ID

   2) (integer) 1593763337  # 執(zhí)行時(shí)間

   3) (integer) 5299        # 執(zhí)行耗時(shí)(微妙)

   4) 1) 'LRANGE'           # 具體執(zhí)行的命令和參數(shù)

      2) 'user_list_2000'

      3) '0'

      4) '-1'

2) 1) (integer) 32692

   2) (integer) 1593763337

   3) (integer) 5044

   4) 1) 'GET'

      2) 'book_price_1000'

...

通過查看慢日志記錄,我們就可以知道在什么時(shí)間執(zhí)行哪些命令比較耗時(shí),如果你的業(yè)務(wù)經(jīng)常使用O(n)以上復(fù)雜度的命令,例如sort、sunion、zunionstore,或者在執(zhí)行O(n)命令時(shí)操作的數(shù)據(jù)量比較大,這些情況下Redis處理數(shù)據(jù)時(shí)就會很耗時(shí)。

如果你的服務(wù)請求量并不大,但Redis實(shí)例的CPU使用率很高,很有可能是使用了復(fù)雜度高的命令導(dǎo)致的。

解決方案就是,不使用這些復(fù)雜度較高的命令,并且一次不要獲取太多的數(shù)據(jù),每次盡量操作少量的數(shù)據(jù),讓Redis可以及時(shí)處理返回。

存儲大key

如果查詢慢日志發(fā)現(xiàn),并不是復(fù)雜度較高的命令導(dǎo)致的,例如都是SET、DELETE操作出現(xiàn)在慢日志記錄中,那么你就要懷疑是否存在Redis寫入了大key的情況。

Redis在寫入數(shù)據(jù)時(shí),需要為新的數(shù)據(jù)分配內(nèi)存,當(dāng)從Redis中刪除數(shù)據(jù)時(shí),它會釋放對應(yīng)的內(nèi)存空間。

如果一個(gè)key寫入的數(shù)據(jù)非常大,Redis在分配內(nèi)存時(shí)也會比較耗時(shí)。同樣的,當(dāng)刪除這個(gè)key的數(shù)據(jù)時(shí),釋放內(nèi)存也會耗時(shí)比較久。

你需要檢查你的業(yè)務(wù)代碼,是否存在寫入大key的情況,需要評估寫入數(shù)據(jù)量的大小,業(yè)務(wù)層應(yīng)該避免一個(gè)key存入過大的數(shù)據(jù)量。

那么有沒有什么辦法可以掃描現(xiàn)在Redis中是否存在大key的數(shù)據(jù)嗎?

Redis也提供了掃描大key的方法:

redis-cli -h $host -p $port --bigkeys -i 0.01

使用上面的命令就可以掃描出整個(gè)實(shí)例key大小的分布情況,它是以類型維度來展示的。

需要注意的是當(dāng)我們在線上實(shí)例進(jìn)行大key掃描時(shí),Redis的QPS會突增,為了降低掃描過程中對Redis的影響,我們需要控制掃描的頻率,使用-i參數(shù)控制即可,它表示掃描過程中每次掃描的時(shí)間間隔,單位是秒。

使用這個(gè)命令的原理,其實(shí)就是Redis在內(nèi)部執(zhí)行scan命令,遍歷所有key,然后針對不同類型的key執(zhí)行strlen、llen、hlen、scard、zcard來獲取字符串的長度以及容器類型(list/dict/set/zset)的元素個(gè)數(shù)。

而對于容器類型的key,只能掃描出元素最多的key,但元素最多的key不一定占用內(nèi)存最多,這一點(diǎn)需要我們注意下。不過使用這個(gè)命令一般我們是可以對整個(gè)實(shí)例中key的分布情況有比較清晰的了解。

針對大key的問題,Redis官方在4.0版本推出了lazy-free的機(jī)制,用于異步釋放大key的內(nèi)存,降低對Redis性能的影響。即使這樣,我們也不建議使用大key,大key在集群的遷移過程中,也會影響到遷移的性能,這個(gè)后面在介紹集群相關(guān)的文章時(shí),會再詳細(xì)介紹到。

集中過期

有時(shí)你會發(fā)現(xiàn),平時(shí)在使用Redis時(shí)沒有延時(shí)比較大的情況,但在某個(gè)時(shí)間點(diǎn)突然出現(xiàn)一波延時(shí),而且報(bào)慢的時(shí)間點(diǎn)很有規(guī)律,例如某個(gè)整點(diǎn),或者間隔多久就會發(fā)生一次。

如果出現(xiàn)這種情況,就需要考慮是否存在大量key集中過期的情況。

如果有大量的key在某個(gè)固定時(shí)間點(diǎn)集中過期,在這個(gè)時(shí)間點(diǎn)訪問Redis時(shí),就有可能導(dǎo)致延遲增加。

Redis的過期策略采用主動過期+懶惰過期兩種策略:

  • 主動過期:Redis內(nèi)部維護(hù)一個(gè)定時(shí)任務(wù),默認(rèn)每隔100毫秒會從過期字典中隨機(jī)取出20個(gè)key,刪除過期的key,如果過期key的比例超過了25%,則繼續(xù)獲取20個(gè)key,刪除過期的key,循環(huán)往復(fù),直到過期key的比例下降到25%或者這次任務(wù)的執(zhí)行耗時(shí)超過了25毫秒,才會退出循環(huán);

  • 懶惰過期:只有當(dāng)訪問某個(gè)key時(shí),才判斷這個(gè)key是否已過期,如果已經(jīng)過期,則從實(shí)例中刪除。

注意,Redis的主動過期的定時(shí)任務(wù),也是在Redis主線程中執(zhí)行的,也就是說如果在執(zhí)行主動過期的過程中,出現(xiàn)了需要大量刪除過期key的情況,那么在業(yè)務(wù)訪問時(shí),必須等這個(gè)過期任務(wù)執(zhí)行結(jié)束,才可以處理業(yè)務(wù)請求。此時(shí)就會出現(xiàn),業(yè)務(wù)訪問延時(shí)增大的問題,最大延遲為25毫秒。

而且這個(gè)訪問延遲的情況,不會記錄在慢日志里。慢日志中只記錄真正執(zhí)行某個(gè)命令的耗時(shí),Redis主動過期策略執(zhí)行在操作命令之前,如果操作命令耗時(shí)達(dá)不到慢日志閾值,它是不會計(jì)算在慢日志統(tǒng)計(jì)中的,但我們的業(yè)務(wù)卻感到了延遲增大。

此時(shí)你需要檢查你的業(yè)務(wù),是否真的存在集中過期的代碼,一般集中過期使用的命令是expireat或pexpireat命令,在代碼中搜索這個(gè)關(guān)鍵字就可以了。

如果你的業(yè)務(wù)確實(shí)需要集中過期掉某些key,又不想導(dǎo)致Redis發(fā)生抖動,有什么優(yōu)化方案?

解決方案是,在集中過期時(shí)增加一個(gè)隨機(jī)時(shí)間,把這些需要過期的key的時(shí)間打散即可。

偽代碼可以這么寫:

# 在過期時(shí)間點(diǎn)之后的5分鐘內(nèi)隨機(jī)過期掉

redis.expireat(key, expire_time + random(300))

這樣Redis在處理過期時(shí),不會因?yàn)榧袆h除key導(dǎo)致壓力過大,阻塞主線程。

另外,除了業(yè)務(wù)使用需要注意此問題之外,還可以通過運(yùn)維手段來及時(shí)發(fā)現(xiàn)這種情況。

做法是我們需要把Redis的各項(xiàng)運(yùn)行數(shù)據(jù)監(jiān)控起來,執(zhí)行info可以拿到所有的運(yùn)行數(shù)據(jù),在這里我們需要重點(diǎn)關(guān)注expired_keys這一項(xiàng),它代表整個(gè)實(shí)例到目前為止,累計(jì)刪除過期key的數(shù)量。

我們需要對這個(gè)指標(biāo)監(jiān)控,當(dāng)在很短時(shí)間內(nèi)這個(gè)指標(biāo)出現(xiàn)突增時(shí),需要及時(shí)報(bào)警出來,然后與業(yè)務(wù)報(bào)慢的時(shí)間點(diǎn)對比分析,確認(rèn)時(shí)間是否一致,如果一致,則可以認(rèn)為確實(shí)是因?yàn)檫@個(gè)原因?qū)е碌难舆t增大。

實(shí)例內(nèi)存達(dá)到上限

有時(shí)我們把Redis當(dāng)做純緩存使用,就會給實(shí)例設(shè)置一個(gè)內(nèi)存上限maxmemory,然后開啟LRU淘汰策略。

當(dāng)實(shí)例的內(nèi)存達(dá)到了maxmemory后,你會發(fā)現(xiàn)之后的每次寫入新的數(shù)據(jù),有可能變慢了。

導(dǎo)致變慢的原因是,當(dāng)Redis內(nèi)存達(dá)到maxmemory后,每次寫入新的數(shù)據(jù)之前,必須先踢出一部分?jǐn)?shù)據(jù),讓內(nèi)存維持在maxmemory之下。

這個(gè)踢出舊數(shù)據(jù)的邏輯也是需要消耗時(shí)間的,而具體耗時(shí)的長短,要取決于配置的淘汰策略:

  • allkeys-lru:不管key是否設(shè)置了過期,淘汰最近最少訪問的key;

  • volatile-lru:只淘汰最近最少訪問并設(shè)置過期的key;

  • allkeys-random:不管key是否設(shè)置了過期,隨機(jī)淘汰;

  • volatile-random:只隨機(jī)淘汰有設(shè)置過期的key;

  • allkeys-ttl:不管key是否設(shè)置了過期,淘汰即將過期的key;

  • noeviction:不淘汰任何key,滿容后再寫入直接報(bào)錯(cuò);

  • allkeys-lfu:不管key是否設(shè)置了過期,淘汰訪問頻率最低的key(4.0+支持);

  • volatile-lfu:只淘汰訪問頻率最低的過期key(4.0+支持)。

具體使用哪種策略,需要根據(jù)業(yè)務(wù)場景來決定。

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

如果使用的是allkeys-random或volatile-random策略,那么就會快很多,因?yàn)槭请S機(jī)淘汰,那么就少了比較key訪問頻率時(shí)間的消耗了,隨機(jī)拿出一批key后直接淘汰即可,因此這個(gè)策略要比上面的LRU策略執(zhí)行快一些。

但以上這些邏輯都是在訪問Redis時(shí),真正命令執(zhí)行之前執(zhí)行的,也就是它會影響我們訪問Redis時(shí)執(zhí)行的命令。

另外,如果此時(shí)Redis實(shí)例中有存儲大key,那么在淘汰大key釋放內(nèi)存時(shí),這個(gè)耗時(shí)會更加久,延遲更大,這需要我們格外注意。

如果你的業(yè)務(wù)訪問量非常大,并且必須設(shè)置maxmemory限制實(shí)例的內(nèi)存上限,同時(shí)面臨淘汰key導(dǎo)致延遲增大的的情況,要想緩解這種情況,除了上面說的避免存儲大key、使用隨機(jī)淘汰策略之外,也可以考慮拆分實(shí)例的方法來緩解,拆分實(shí)例可以把一個(gè)實(shí)例淘汰key的壓力分?jǐn)偟蕉鄠€(gè)實(shí)例上,可以在一定程度降低延遲。

fork耗時(shí)嚴(yán)重

如果你的Redis開啟了自動生成RDB和AOF重寫功能,那么有可能在后臺生成RDB和AOF重寫時(shí)導(dǎo)致Redis的訪問延遲增大,而等這些任務(wù)執(zhí)行完畢后,延遲情況消失。

遇到這種情況,一般就是執(zhí)行生成RDB和AOF重寫任務(wù)導(dǎo)致的。

生成RDB和AOF都需要父進(jìn)程fork出一個(gè)子進(jìn)程進(jìn)行數(shù)據(jù)的持久化,在fork執(zhí)行過程中,父進(jìn)程需要拷貝內(nèi)存頁表給子進(jìn)程,如果整個(gè)實(shí)例內(nèi)存占用很大,那么需要拷貝的內(nèi)存頁表會比較耗時(shí),此過程會消耗大量的CPU資源,在完成fork之前,整個(gè)實(shí)例會被阻塞住,無法處理任何請求,如果此時(shí)CPU資源緊張,那么fork的時(shí)間會更長,甚至達(dá)到秒級。這會嚴(yán)重影響Redis的性能。

具體原理也可以參考我之前寫的文章:Redis持久化是如何做的?RDB和AOF對比分析。

我們可以執(zhí)行info命令,查看最后一次fork執(zhí)行的耗時(shí)latest_fork_usec,單位微妙。這個(gè)時(shí)間就是整個(gè)實(shí)例阻塞無法處理請求的時(shí)間。

除了因?yàn)閭浞莸脑蛏蒖DB之外,在主從節(jié)點(diǎn)第一次建立數(shù)據(jù)同步時(shí),主節(jié)點(diǎn)也會生成RDB文件給從節(jié)點(diǎn)進(jìn)行一次全量同步,這時(shí)也會對Redis產(chǎn)生性能影響。

要想避免這種情況,我們需要規(guī)劃好數(shù)據(jù)備份的周期,建議在從節(jié)點(diǎn)上執(zhí)行備份,而且最好放在低峰期執(zhí)行。如果對于丟失數(shù)據(jù)不敏感的業(yè)務(wù),那么不建議開啟AOF和AOF重寫功能。

另外,fork的耗時(shí)也與系統(tǒng)有關(guān),如果把Redis部署在虛擬機(jī)上,那么這個(gè)時(shí)間也會增大。所以使用Redis時(shí)建議部署在物理機(jī)上,降低fork的影響。

綁定CPU

很多時(shí)候,我們在部署服務(wù)時(shí),為了提高性能,降低程序在使用多個(gè)CPU時(shí)上下文切換的性能損耗,一般會采用進(jìn)程綁定CPU的操作。

但在使用Redis時(shí),我們不建議這么干,原因如下。

綁定CPU的Redis,在進(jìn)行數(shù)據(jù)持久化時(shí),fork出的子進(jìn)程,子進(jìn)程會繼承父進(jìn)程的CPU使用偏好,而此時(shí)子進(jìn)程會消耗大量的CPU資源進(jìn)行數(shù)據(jù)持久化,子進(jìn)程會與主進(jìn)程發(fā)生CPU爭搶,這也會導(dǎo)致主進(jìn)程的CPU資源不足訪問延遲增大。

所以在部署Redis進(jìn)程時(shí),如果需要開啟RDB和AOF重寫機(jī)制,一定不能進(jìn)行CPU綁定操作!

開啟AOF

上面提到了,當(dāng)執(zhí)行AOF文件重寫時(shí)會因?yàn)閒ork執(zhí)行耗時(shí)導(dǎo)致Redis延遲增大,除了這個(gè)之外,如果開啟AOF機(jī)制,設(shè)置的策略不合理,也會導(dǎo)致性能問題。

開啟AOF后,Redis會把寫入的命令實(shí)時(shí)寫入到文件中,但寫入文件的過程是先寫入內(nèi)存,等內(nèi)存中的數(shù)據(jù)超過一定閾值或達(dá)到一定時(shí)間后,內(nèi)存中的內(nèi)容才會被真正寫入到磁盤中。

AOF為了保證文件寫入磁盤的安全性,提供了3種刷盤機(jī)制:

  • appendfsync always:每次寫入都刷盤,對性能影響最大,占用磁盤IO比較高,數(shù)據(jù)安全性最高;

  • appendfsync everysec:1秒刷一次盤,對性能影響相對較小,節(jié)點(diǎn)宕機(jī)時(shí)最多丟失1秒的數(shù)據(jù);

  • appendfsync no:按照操作系統(tǒng)的機(jī)制刷盤,對性能影響最小,數(shù)據(jù)安全性低,節(jié)點(diǎn)宕機(jī)丟失數(shù)據(jù)取決于操作系統(tǒng)刷盤機(jī)制。

當(dāng)使用第一種機(jī)制appendfsync always時(shí),Redis每處理一次寫命令,都會把這個(gè)命令寫入磁盤,而且這個(gè)操作是在主線程中執(zhí)行的。

內(nèi)存中的的數(shù)據(jù)寫入磁盤,這個(gè)會加重磁盤的IO負(fù)擔(dān),操作磁盤成本要比操作內(nèi)存的代價(jià)大得多。如果寫入量很大,那么每次更新都會寫入磁盤,此時(shí)機(jī)器的磁盤IO就會非常高,拖慢Redis的性能,因此我們不建議使用這種機(jī)制。

與第一種機(jī)制對比,appendfsync everysec會每隔1秒刷盤,而appendfsync no取決于操作系統(tǒng)的刷盤時(shí)間,安全性不高。因此我們推薦使用appendfsync everysec這種方式,在最壞的情況下,只會丟失1秒的數(shù)據(jù),但它能保持較好的訪問性能。

當(dāng)然,對于有些業(yè)務(wù)場景,對丟失數(shù)據(jù)并不敏感,也可以不開啟AOF。

使用Swap

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

我們知道,操作系統(tǒng)提供了Swap機(jī)制,目的是為了當(dāng)內(nèi)存不足時(shí),可以把一部分內(nèi)存中的數(shù)據(jù)換到磁盤上,以達(dá)到對內(nèi)存使用的緩沖。

但當(dāng)內(nèi)存中的數(shù)據(jù)被換到磁盤上后,訪問這些數(shù)據(jù)就需要從磁盤中讀取,這個(gè)速度要比內(nèi)存慢太多!

尤其是針對Redis這種高性能的內(nèi)存數(shù)據(jù)庫來說,如果Redis中的內(nèi)存被換到磁盤上,對于Redis這種性能極其敏感的數(shù)據(jù)庫,這個(gè)操作時(shí)間是無法接受的。

我們需要檢查機(jī)器的內(nèi)存使用情況,確認(rèn)是否確實(shí)是因?yàn)閮?nèi)存不足導(dǎo)致使用到了Swap。

如果確實(shí)使用到了Swap,要及時(shí)整理內(nèi)存空間,釋放出足夠的內(nèi)存供Redis使用,然后釋放Redis的Swap,讓Redis重新使用內(nèi)存。

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

可見,當(dāng)Redis使用到Swap后,此時(shí)的Redis的高性能基本被廢掉,所以我們需要提前預(yù)防這種情況。

我們需要對Redis機(jī)器的內(nèi)存和Swap使用情況進(jìn)行監(jiān)控,在內(nèi)存不足和使用到Swap時(shí)及時(shí)報(bào)警出來,及時(shí)進(jìn)行相應(yīng)的處理。

網(wǎng)卡負(fù)載過高

如果以上產(chǎn)生性能問題的場景,你都規(guī)避掉了,而且Redis也穩(wěn)定運(yùn)行了很長時(shí)間,但在某個(gè)時(shí)間點(diǎn)之后開始,訪問Redis開始變慢了,而且一直持續(xù)到現(xiàn)在,這種情況是什么原因?qū)е碌模?/span>

之前我們就遇到這種問題,特點(diǎn)就是從某個(gè)時(shí)間點(diǎn)之后就開始變慢,并且一直持續(xù)。這時(shí)你需要檢查一下機(jī)器的網(wǎng)卡流量,是否存在網(wǎng)卡流量被跑滿的情況。

網(wǎng)卡負(fù)載過高,在網(wǎng)絡(luò)層和TCP層就會出現(xiàn)數(shù)據(jù)發(fā)送延遲、數(shù)據(jù)丟包等情況。Redis的高性能除了內(nèi)存之外,就在于網(wǎng)絡(luò)IO,請求量突增會導(dǎo)致網(wǎng)卡負(fù)載變高。

如果出現(xiàn)這種情況,你需要排查這個(gè)機(jī)器上的哪個(gè)Redis實(shí)例的流量過大占滿了網(wǎng)絡(luò)帶寬,然后確認(rèn)流量突增是否屬于業(yè)務(wù)正常情況,如果屬于那就需要及時(shí)擴(kuò)容或遷移實(shí)例,避免這個(gè)機(jī)器的其他實(shí)例受到影響。

運(yùn)維層面,我們需要對機(jī)器的各項(xiàng)指標(biāo)增加監(jiān)控,包括網(wǎng)絡(luò)流量,在達(dá)到閾值時(shí)提前報(bào)警,及時(shí)與業(yè)務(wù)確認(rèn)并擴(kuò)容。

以上我們總結(jié)了Redis中常見的可能導(dǎo)致延遲增大甚至阻塞的場景,這其中既涉及到了業(yè)務(wù)的使用問題,也涉及到Redis的運(yùn)維問題。

可見,要想保證Redis高性能的運(yùn)行,其中涉及到CPU、內(nèi)存、網(wǎng)絡(luò),甚至磁盤的方方面面,其中還包括操作系統(tǒng)的相關(guān)特性的使用。

作為開發(fā)人員,我們需要了解Redis的運(yùn)行機(jī)制,例如各個(gè)命令的執(zhí)行時(shí)間復(fù)雜度、數(shù)據(jù)過期策略、數(shù)據(jù)淘汰策略等,使用合理的命令,并結(jié)合業(yè)務(wù)場景進(jìn)行優(yōu)化。

作為DBA運(yùn)維人員,需要了解數(shù)據(jù)持久化、操作系統(tǒng)fork原理、Swap機(jī)制等,并對Redis的容量進(jìn)行合理規(guī)劃,預(yù)留足夠的機(jī)器資源,對機(jī)器做好完善的監(jiān)控,才能保證Redis的穩(wěn)定運(yùn)行。

Redis的最佳實(shí)踐方式:業(yè)務(wù)層面和運(yùn)維層面

在上文中,主要講解了 Redis 常見的導(dǎo)致變慢的場景以及問題定位和分析,主要是由業(yè)務(wù)使用不合理和運(yùn)維不當(dāng)導(dǎo)致的。

我們在了解了導(dǎo)致Redis變慢的原因之后,針對性地優(yōu)化,就可以讓Redis穩(wěn)定發(fā)揮出更高性能。

接著就來總結(jié)一下,在使用Redis時(shí)的最佳實(shí)踐方式,主要包含兩個(gè)層面:業(yè)務(wù)層面、運(yùn)維層面。

由于我之前寫過很多UGC后端服務(wù),在大量場景下用到了Redis,這個(gè)過程中也踩過很多坑,所以在使用過程中也總結(jié)了一套合理的使用方法。

后來做基礎(chǔ)架構(gòu),開發(fā)Codis、Redis相關(guān)的中間件,在這個(gè)階段關(guān)注領(lǐng)域從使用層面下沉到Redis的開發(fā)和運(yùn)維,更多聚焦在Redis的內(nèi)部實(shí)現(xiàn)和運(yùn)維過程中產(chǎn)生的各種問題,在這塊也積累了一些經(jīng)驗(yàn)。

下面就針對這兩塊,分享一下我認(rèn)為比較合理的Redis使用和運(yùn)維方法,不一定最全面,也可能與你使用Redis的方法不同,但以下這些方法都是我在踩坑之后總結(jié)的實(shí)際經(jīng)驗(yàn),供你參考。

業(yè)務(wù)層面

業(yè)務(wù)層面主要是開發(fā)人員需要關(guān)注,也就是開發(fā)人員在寫業(yè)務(wù)代碼時(shí),如何合理地使用Redis。開發(fā)人員需要對Redis有基本的了解,才能在合適的業(yè)務(wù)場景使用Redis,從而避免業(yè)務(wù)層面導(dǎo)致的延遲問題。

在開發(fā)過程中,業(yè)務(wù)層面的優(yōu)化建議如下:

  • key的長度盡量要短,在數(shù)據(jù)量非常大時(shí),過長的key名會占用更多的內(nèi)存;

  • 一定避免存儲過大的數(shù)據(jù)(大value),過大的數(shù)據(jù)在分配內(nèi)存和釋放內(nèi)存時(shí)耗時(shí)嚴(yán)重,會阻塞主線程;

  • Redis 4.0以上建議開啟lazy-free機(jī)制,釋放大value時(shí)異步操作,不阻塞主線程;

  • 建議設(shè)置過期時(shí)間,把Redis當(dāng)做緩存使用,尤其在數(shù)量很大的時(shí),不設(shè)置過期時(shí)間會導(dǎo)致內(nèi)存的無限增長;

  • 不使用復(fù)雜度過高的命令,例如SORT、SINTER、SINTERSTORE、ZUNIONSTORE、ZINTERSTORE,使用這些命令耗時(shí)較久,會阻塞主線程;

  • 查詢數(shù)據(jù)時(shí),一次盡量獲取較少的數(shù)據(jù),在不確定容器元素個(gè)數(shù)的情況下,避免使用LRANGE key 0 -1,ZRANGE key 0 -1這類操作,應(yīng)該設(shè)置具體查詢的元素個(gè)數(shù),推薦一次查詢100個(gè)以下元素;

  • 寫入數(shù)據(jù)時(shí),一次盡量寫入較少的數(shù)據(jù),例如HSET key value1 value2 value3...,控制一次寫入元素的數(shù)量,推薦在100以下,大數(shù)據(jù)量分多個(gè)批次寫入;

  • 批量操作數(shù)據(jù)時(shí),用MGET/MSET替換GET/SET、HMGET/MHSET替換HGET/HSET,減少請求來回的網(wǎng)絡(luò)IO次數(shù),降低延遲,對于沒有批量操作的命令,推薦使用pipeline,一次性發(fā)送多個(gè)命令到服務(wù)端;

  • 禁止使用KEYS命令,需要掃描實(shí)例時(shí),建議使用SCAN,線上操作一定要控制掃描的頻率,避免對Redis產(chǎn)生性能抖動

  • 避免某個(gè)時(shí)間點(diǎn)集中過期大量的key,集中過期時(shí)推薦增加一個(gè)隨機(jī)時(shí)間,把過期時(shí)間打散,降低集中過期key時(shí)Redis的壓力,避免阻塞主線程;

  • 根據(jù)業(yè)務(wù)場景,選擇合適的淘汰策略,通常隨機(jī)過期要比LRU過期淘汰數(shù)據(jù)更快;

  • 使用連接池訪問Redis,并配置合理的連接池參數(shù),避免短連接,TCP三次握手和四次揮手的耗時(shí)也很高;

  • 只使用db0,不推薦使用多個(gè)db,使用多個(gè)db會增加Redis的負(fù)擔(dān),每次訪問不同的db都需要執(zhí)行SELECT命令,如果業(yè)務(wù)線不同,建議拆分多個(gè)實(shí)例,還能提高單個(gè)實(shí)例的性能;

  • 讀的請求量很大時(shí),推薦使用讀寫分離,前提是可以容忍從節(jié)數(shù)據(jù)更新不及時(shí)的問題;

  • 寫請求量很大時(shí),推薦使用集群,部署多個(gè)實(shí)例分?jǐn)倢憠毫Α?/span>

運(yùn)維層面

運(yùn)維層面主要是DBA需要關(guān)注的,目的是合理規(guī)劃Redis的部署和保障Redis的穩(wěn)定運(yùn)行,主要優(yōu)化如下:

  • 不同業(yè)務(wù)線部署不同的實(shí)例,各自獨(dú)立,避免混用,推薦不同業(yè)務(wù)線使用不同的機(jī)器,根據(jù)業(yè)務(wù)重要程度劃分不同的分組來部署,避免某一個(gè)業(yè)務(wù)線出現(xiàn)問題影響其他業(yè)務(wù)線;

  • 保證機(jī)器有足夠的CPU、內(nèi)存、帶寬、磁盤資源,防止負(fù)載過高影響Redis性能;

  • 以master-slave集群方式部署實(shí)例,并分布在不同機(jī)器上,避免單點(diǎn),slave必須設(shè)置為readonly;

  • master和slave節(jié)點(diǎn)所在機(jī)器,各自獨(dú)立,不要交叉部署實(shí)例,通常備份工作會在slave上做,做備份時(shí)會消耗機(jī)器資源,交叉部署會影響到master的性能;

  • 推薦部署哨兵節(jié)點(diǎn)增加可用性,節(jié)點(diǎn)數(shù)量至少3個(gè),并分布在不同機(jī)器上,實(shí)現(xiàn)故障自動故障轉(zhuǎn)移;

  • 提前做好容量規(guī)劃,一臺機(jī)器部署實(shí)例的內(nèi)存上限,最好是機(jī)器內(nèi)存的一半,主從全量同步時(shí)會占用最多額外一倍的內(nèi)存空間,防止網(wǎng)絡(luò)大面積故障引發(fā)所有master-slave的全量同步導(dǎo)致機(jī)器內(nèi)存被吃光;

  • 做好機(jī)器的CPU、內(nèi)存、帶寬、磁盤監(jiān)控,在資源不足時(shí)及時(shí)報(bào)警處理,Redis使用Swap后性能急劇下降,網(wǎng)絡(luò)帶寬負(fù)載過高訪問延遲明顯增大,磁盤IO過高時(shí)開啟AOF會拖慢Redis的性能;

  • 設(shè)置最大連接數(shù)上限,防止過多的客戶端連接導(dǎo)致服務(wù)負(fù)載過高;

  • 單個(gè)實(shí)例的使用內(nèi)存建議控制在10G以下,過大的實(shí)例會導(dǎo)致備份時(shí)間久、資源消耗多,主從全量同步數(shù)據(jù)時(shí)間阻塞時(shí)間更長;

  • 設(shè)置合理的slowlog閾值,推薦10毫秒,并對其進(jìn)行監(jiān)控,產(chǎn)生過多的慢日志需要及時(shí)報(bào)警;

  • 設(shè)置合理的復(fù)制緩沖區(qū)repl-backlog大小,適當(dāng)調(diào)大repl-backlog可以降低主從全量復(fù)制的概率;

  • 設(shè)置合理的slave節(jié)點(diǎn)client-output-buffer-limit大小,對于寫入量很大的實(shí)例,適當(dāng)調(diào)大可以避免主從復(fù)制中斷問題;

  • 備份時(shí)推薦在slave節(jié)點(diǎn)上做,不影響master性能;

  • 不開啟AOF或開啟AOF配置為每秒刷盤,避免磁盤IO消耗降低Redis性能;

  • 當(dāng)實(shí)例設(shè)置了內(nèi)存上限,需要調(diào)大內(nèi)存上限時(shí),先調(diào)整slave再調(diào)整master,否則會導(dǎo)致主從節(jié)點(diǎn)數(shù)據(jù)不一致;

  • 對Redis增加監(jiān)控,監(jiān)控采集info信息時(shí),使用長連接,頻繁的短連接也會影響Redis性能;

  • 線上掃描整個(gè)實(shí)例數(shù)時(shí),記得設(shè)置休眠時(shí)間,避免掃描時(shí)QPS突增對Redis產(chǎn)生性能抖動;

  • 做好Redis的運(yùn)行時(shí)監(jiān)控,尤其是expired_keys、evicted_keys、latest_fork_usec指標(biāo),短時(shí)間內(nèi)這些指標(biāo)值突增可能會阻塞整個(gè)實(shí)例,引發(fā)性能問題。

以上就是我在使用Redis和開發(fā)Redis相關(guān)中間件時(shí),總結(jié)出來Redis推薦的實(shí)踐方法,以上提出的這些方面,都或多或少在實(shí)際使用中遇到過。

可見,要想穩(wěn)定發(fā)揮Redis的高性能,需要在各個(gè)方面做好工作,但凡某一個(gè)方面出現(xiàn)問題,必然會影響到Redis的性能,這對我們使用和運(yùn)維提出了更高的要求。

如果你在使用Redis過程中,遇到更多的問題或者有更好的使用經(jīng)驗(yàn),可以留言一起探討!

作者丨Kaito
來源丨http:///

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

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    91欧美亚洲精品在线观看| 亚洲一区二区三区在线中文字幕 | 又色又爽又黄的三级视频| 一区二区三区欧美高清| 五月天丁香婷婷一区二区| 精品日韩视频在线观看| 日韩欧美高清国内精品| 玩弄人妻少妇一区二区桃花| 中文字幕在线五月婷婷| 色丁香一区二区黑人巨大| 91人妻人人做人碰人人九色 | 护士又紧又深又湿又爽的视频| 加勒比系列一区二区在线观看| 99久只有精品免费视频播放| 欧美成人黄色一级视频| 婷婷基地五月激情五月| 日韩欧美中文字幕人妻| 久久三级国外久久久三级| 亚洲国产av在线观看一区| 五月婷婷综合激情啪啪| 久久成人国产欧美精品一区二区| 中文字幕乱码免费人妻av| 国产精品伦一区二区三区四季| 丰满少妇高潮一区二区| 色哟哟哟在线观看视频| 东京热一二三区在线免| 亚洲欧美日韩另类第一页| 国产日韩欧美国产欧美日韩 | 欧美一区二区三区十区| 婷婷伊人综合中文字幕| 丰满人妻熟妇乱又乱精品古代| 国内欲色一区二区三区| 东京热一二三区在线免| 亚洲淫片一区二区三区| 日本黄色录像韩国黄色录像| 国产日本欧美韩国在线| 日韩欧美综合在线播放| 清纯少妇被捅到高潮免费观看| 黑鬼糟蹋少妇资源在线观看| 视频在线免费观看你懂的| 久久人妻人人澡人人妻|