日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

redis 是一款高性能的內(nèi)存數(shù)據(jù)存儲(chǔ)系統(tǒng),它被廣泛應(yīng)用于各種實(shí)時(shí)數(shù)據(jù)處理場(chǎng)景,如緩存、消息隊(duì)列、實(shí)時(shí)統(tǒng)計(jì)等。為了最大化 Redis 的性能,我們應(yīng)該針對(duì)具體應(yīng)用場(chǎng)景,對(duì)其配置參數(shù)進(jìn)行優(yōu)化。在本文中,我們將介紹一些優(yōu)化 Redis 配置的技巧,以提高其性能。

 

Redis慢在哪里?

  • 對(duì) Redis 進(jìn)行基準(zhǔn)性能測(cè)試

例如,我的機(jī)器配置比較低,當(dāng)延遲為 2ms 時(shí),我就認(rèn)為 Redis 變慢了,但是如果你的硬件配置比較高,那么在你的運(yùn)行環(huán)境下,可能延遲是 0.5ms 時(shí)就可以認(rèn)為 Redis 變慢了。所以,你只有了解了你的 Redis 在生產(chǎn)環(huán)境服務(wù)器上的基準(zhǔn)性能,才能進(jìn)一步評(píng)估,當(dāng)其延遲達(dá)到什么程度時(shí),才認(rèn)為 Redis 確實(shí)變慢了。

為了避免業(yè)務(wù)服務(wù)器到 Redis 服務(wù)器之間的網(wǎng)絡(luò)延遲,你需要直接在 Redis 服務(wù)器上測(cè)試實(shí)例的響應(yīng)延遲情況。執(zhí)行以下命令,就可以測(cè)試出這個(gè)實(shí)例 60 秒內(nèi)的最大響應(yīng)延遲:

./redis-cli --intrinsic-latency 120
Max 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)的最大響應(yīng)延遲為 119 微秒(0.119毫秒)。你還可以使用以下命令,查看一段時(shí)間內(nèi) Redis 的最小、最大、平均訪問(wèn)延遲。

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

如果你觀察到的 Redis 運(yùn)行時(shí)延遲是其基線性能的 2 倍及以上,就可以認(rèn)定 Redis 變慢了。?網(wǎng)絡(luò)對(duì) Redis 性能的影響,一個(gè)簡(jiǎn)單的方法是用 iPerf 這樣的工具測(cè)試網(wǎng)絡(luò)極限帶寬。

服務(wù)器端


# iperf -s -p 12345 -i 1 -M
iperf: option requires an argument -- M
------------------------------------------------------------
Server listening on TCP port 12345
TCP 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 服務(wù)器端IP -p 12345 -i 1 -t 10 -w 20K
------------------------------------------------------------
Client connecting to 172.20.0.113, TCP port 12345
TCP 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.使用復(fù)雜度過(guò)高的命令

首先,第一步,你需要去查看一下 Redis 的慢日志(slowlog)。

Redis 提供了慢日志命令的統(tǒng)計(jì)功能,它記錄了有哪些命令在執(zhí)行時(shí)耗時(shí)比較久。

查看 Redis 慢日志之前,你需要設(shè)置慢日志的閾值。例如,設(shè)置慢日志的閾值為 5 毫秒,并且保留最近 500 條慢日志記錄:

# 命令執(zhí)行耗時(shí)超過(guò) 5 毫秒,記錄慢日志
CONFIG SET slowlog-log-slower-than 5000
# 只保留最近 500 條慢日志
CONFIG SET slowlog-max-len 500

 

1)經(jīng)常使用 O(N) 以上復(fù)雜度的命令,例如 SORT、SUNION、ZUNIONSTORE 聚合類命令。

2)使用 O(N) 復(fù)雜度的命令,但 N 的值非常大。

第一種情況導(dǎo)致變慢的原因在于,Redis 在操作內(nèi)存數(shù)據(jù)時(shí),時(shí)間復(fù)雜度過(guò)高,要花費(fèi)更多的 CPU 資源。

第二種情況導(dǎo)致變慢的原因在于,Redis 一次需要返回給客戶端的數(shù)據(jù)過(guò)多,更多時(shí)間花費(fèi)在數(shù)據(jù)協(xié)議的組裝和網(wǎng)絡(luò)傳輸過(guò)程中。

另外,我們還可以從資源使用率層面來(lái)分析,如果你的應(yīng)用程序操作 Redis 的 OPS 不是很大,但 Redis 實(shí)例的 CPU 使用率卻很高,那么很有可能是使用了復(fù)雜度過(guò)高的命令導(dǎo)致的。

3.操作bigkey

如果你查詢慢日志發(fā)現(xiàn),并不是復(fù)雜度過(guò)高的命令導(dǎo)致的,而都是 SET / DEL 這種簡(jiǎn)單命令出現(xiàn)在慢日志中,那么你就要懷疑你的實(shí)例否寫入了 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 bytes
Biggest   list found 'mylist:004' has 40 items
Biggest    set found 'myset:2386' has 38 members
Biggest   hash found 'myhash:3574' has 37 fields
Biggest   zset found 'myzset:2704' has 42 members
36313 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)

這里我需要提醒你的是,當(dāng)執(zhí)行這個(gè)命令時(shí),要注意 2 個(gè)問(wèn)題:

1)對(duì)線上實(shí)例進(jìn)行 bigkey 掃描時(shí),Redis 的 OPS 會(huì)突增,為了降低掃描過(guò)程中對(duì) Redis 的影響,最好控制一下掃描的頻率,指定 -i 參數(shù)即可,它表示掃描過(guò)程中每次掃描后休息的時(shí)間間隔,單位是秒。

2)掃描結(jié)果中,對(duì)于容器類型(List、Hash、Set、ZSet)的 key,只能掃描出元素最多的 key。但一個(gè) key 的元素多,不一定表示占用內(nèi)存也多,你還需要根據(jù)業(yè)務(wù)情況,進(jìn)一步評(píng)估內(nèi)存占用情況。

4.集中過(guò)期

如果你發(fā)現(xiàn),平時(shí)在操作 Redis 時(shí),并沒(méi)有延遲很大的情況發(fā)生,但在某個(gè)時(shí)間點(diǎn)突然出現(xiàn)一波延時(shí),其現(xiàn)象表現(xiàn)為:變慢的時(shí)間點(diǎn)很有規(guī)律,例如某個(gè)整點(diǎn),或者每間隔多久就會(huì)發(fā)生一波延遲。

如果是出現(xiàn)這種情況,那么你需要排查一下,業(yè)務(wù)代碼中是否存在設(shè)置大量 key 集中過(guò)期的情況。

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

Redis 的過(guò)期數(shù)據(jù)采用被動(dòng)過(guò)期 + 主動(dòng)過(guò)期兩種策略:

1)被動(dòng)過(guò)期:只有當(dāng)訪問(wèn)某個(gè) key 時(shí),才判斷這個(gè) key 是否已過(guò)期,如果已過(guò)期,則從實(shí)例中刪除。

2)主動(dòng)過(guò)期:Redis 內(nèi)部維護(hù)了一個(gè)定時(shí)任務(wù),默認(rèn)每隔 100 毫秒(1秒10次)就會(huì)從全局的過(guò)期哈希表中隨機(jī)取出 20 個(gè) key,然后刪除其中過(guò)期的 key,如果過(guò)期 key 的比例超過(guò)了 25%,則繼續(xù)重復(fù)此過(guò)程,直到過(guò)期 key 的比例下降到 25% 以下,或者這次任務(wù)的執(zhí)行耗時(shí)超過(guò)了 25 毫秒,才會(huì)退出循環(huán)。

注意,這個(gè)主動(dòng)過(guò)期 key 的定時(shí)任務(wù),是在 Redis 主線程中執(zhí)行的。

也就是說(shuō)如果在執(zhí)行主動(dòng)過(guò)期的過(guò)程中,出現(xiàn)了需要大量刪除過(guò)期 key 的情況,那么此時(shí)應(yīng)用程序在訪問(wèn) Redis 時(shí),必須要等待這個(gè)過(guò)期任務(wù)執(zhí)行結(jié)束,Redis 才可以服務(wù)這個(gè)客戶端請(qǐng)求。

如果此時(shí)需要過(guò)期刪除的是一個(gè) bigkey,那么這個(gè)耗時(shí)會(huì)更久。而且,這個(gè)操作延遲的命令并不會(huì)記錄在慢日志中。

因?yàn)槁罩局?strong>只記錄一個(gè)命令真正操作內(nèi)存數(shù)據(jù)的耗時(shí),而 Redis 主動(dòng)刪除過(guò)期 key 的邏輯,是在命令真正執(zhí)行之前執(zhí)行的。

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

當(dāng)我們把 Redis 當(dāng)做純緩存使用時(shí),通常會(huì)給這個(gè)實(shí)例設(shè)置一個(gè)內(nèi)存上限 maxmemory,然后設(shè)置一個(gè)數(shù)據(jù)淘汰策略。

當(dāng) Redis 內(nèi)存達(dá)到 maxmemory 后,每次寫入新的數(shù)據(jù)之前,Redis 必須先從實(shí)例中踢出一部分?jǐn)?shù)據(jù),讓整個(gè)實(shí)例的內(nèi)存維持在 maxmemory 之下,然后才能把新數(shù)據(jù)寫進(jìn)來(lái)。

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

  • allkeys-lru:不管 key 是否設(shè)置了過(guò)期,淘汰最近最少訪問(wèn)的 key
  • volatile-lru:只淘汰最近最少訪問(wèn)、并設(shè)置了過(guò)期時(shí)間的 key
  • allkeys-random:不管 key 是否設(shè)置了過(guò)期,隨機(jī)淘汰 key
  • volatile-random:只隨機(jī)淘汰設(shè)置了過(guò)期時(shí)間的 key
  • allkeys-ttl:不管 key 是否設(shè)置了過(guò)期,淘汰即將過(guò)期的 key
  • noeviction:不淘汰任何 key,實(shí)例內(nèi)存達(dá)到 maxmeory 后,再寫入新數(shù)據(jù)直接返回錯(cuò)誤
  • allkeys-lfu:不管 key 是否設(shè)置了過(guò)期,淘汰訪問(wèn)頻率最低的 key(4.0+版本支持)
  • volatile-lfu:只淘汰訪問(wèn)頻率最低、并設(shè)置了過(guò)期時(shí)間 key(4.0+版本支持)

 

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

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

如何進(jìn)行Redis性能優(yōu)化?這一篇就夠了

 

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

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

當(dāng) Redis 開(kāi)啟了后臺(tái) RDB 和 AOF rewrite 后,在執(zhí)行時(shí),它們都需要主進(jìn)程創(chuàng)建出一個(gè)子進(jìn)程進(jìn)行數(shù)據(jù)的持久化。主進(jìn)程創(chuàng)建子進(jìn)程,會(huì)調(diào)用操作系統(tǒng)提供的 fork 函數(shù)。

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

而且這個(gè) fork 過(guò)程會(huì)消耗大量的 CPU 資源,在完成 fork 之前,整個(gè) Redis 實(shí)例會(huì)被阻塞住,無(wú)法處理任何客戶端請(qǐng)求。

如果此時(shí)你的 CPU 資源本來(lái)就很緊張,那么 fork 的耗時(shí)會(huì)更長(zhǎng),甚至達(dá)到秒級(jí),這會(huì)嚴(yán)重影響 Redis 的性能。

那如何確認(rèn)確實(shí)是因?yàn)?fork 耗時(shí)導(dǎo)致的 Redis 延遲變大呢?

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

# 上一次 fork 耗時(shí),單位微秒
latest_fork_usec:59477

這個(gè)時(shí)間就是主進(jìn)程在 fork 子進(jìn)程期間,整個(gè)實(shí)例阻塞無(wú)法處理客戶端請(qǐng)求的時(shí)間。

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

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

如何進(jìn)行Redis性能優(yōu)化?這一篇就夠了

 

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

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

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

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

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

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

這對(duì) Redis 會(huì)有什么影響呢?

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

 

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

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

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

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

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

8.開(kāi)啟AOF

前面我們分析了 RDB 和 AOF rewrite 對(duì) Redis 性能的影響,主要關(guān)注點(diǎn)在 fork 上。

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

如果你的 AOF 配置不合理,還是有可能會(huì)導(dǎo)致性能問(wèn)題。

當(dāng) Redis 開(kāi)啟 AOF 后,其工作原理如下:

1)Redis 執(zhí)行寫命令后,把這個(gè)命令寫入到 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 種刷盤機(jī)制:

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

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

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

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

 

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

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

但是,這里我要給你潑一盆冷水了,采用這種方案你也要警惕一下,因?yàn)檫@種方案還是存在導(dǎo)致 Redis 延遲變大的情況發(fā)生,甚至?xí)枞麄€(gè) Redis。

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

 

此時(shí)的主線程依舊會(huì)接收寫請(qǐng)求,緊接著,主線程又需要把數(shù)據(jù)寫到文件內(nèi)存中(write 系統(tǒng)調(diào)用),當(dāng)主線程使用后臺(tái)子線程執(zhí)行了一次 fsync,需要再次把新接收的操作記錄寫回磁盤時(shí),如果主線程發(fā)現(xiàn)上一次的 fsync 還沒(méi)有執(zhí)行完,那么它就會(huì)阻塞。

 

所以,如果后臺(tái)子線程執(zhí)行的 fsync 頻繁阻塞的話(比如 AOF 重寫占用了大量的磁盤 IO 帶寬),主線程也會(huì)阻塞,導(dǎo)致 Redis 性能變慢。

如何進(jìn)行Redis性能優(yōu)化?這一篇就夠了

 

看到了么?在這個(gè)過(guò)程中,主線程依舊有阻塞的風(fēng)險(xiǎn)。

所以,盡管你的 AOF 配置為 appendfsync everysec,也不能掉以輕心,要警惕磁盤壓力過(guò)大導(dǎo)致的 Redis 有性能問(wèn)題。

那什么情況下會(huì)導(dǎo)致磁盤 IO 負(fù)載過(guò)大?以及如何解決這個(gè)問(wèn)題呢?

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

1)進(jìn)程正在執(zhí)行 AOF rewrite,這個(gè)過(guò)程會(huì)占用大量的磁盤 IO 資源

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

 

對(duì)于情況1,說(shuō)白了就是,Redis 的 AOF 后臺(tái)子線程刷盤操作,撞上了子進(jìn)程 AOF rewrite!

9.綁定CPU

很多時(shí)候,我們?cè)诓渴鸱?wù)時(shí),為了提高服務(wù)性能,降低應(yīng)用程序在多個(gè) CPU 核心之間的上下文切換帶來(lái)的性能損耗,通常采用的方案是進(jìn)程綁定 CPU 的方式提高性能。

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

而 Redis Server 除了主線程服務(wù)客戶端請(qǐng)求之外,還會(huì)創(chuàng)建子進(jìn)程、子線程。

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

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

而此時(shí)的子進(jìn)程會(huì)消耗大量的 CPU 資源進(jìn)行數(shù)據(jù)持久化(把實(shí)例數(shù)據(jù)全部掃描出來(lái)需要耗費(fèi)CPU),這就會(huì)導(dǎo)致子進(jìn)程會(huì)與主進(jìn)程發(fā)生 CPU 爭(zhēng)搶,進(jìn)而影響到主進(jìn)程服務(wù)客戶端請(qǐng)求,訪問(wèn)延遲變大。

這就是 Redis 綁定 CPU 帶來(lái)的性能問(wèn)題。

10.使用Swap

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

什么是 Swap?為什么使用 Swap 會(huì)導(dǎo)致 Redis 的性能下降?

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

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

尤其是針對(duì) Redis 這種對(duì)性能要求極高、性能極其敏感的數(shù)據(jù)庫(kù)來(lái)說(shuō),這個(gè)操作延時(shí)是無(wú)法接受的。

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

你可以通過(guò)以下方式來(lái)查看 Redis 進(jìn)程是否使用到了 Swap:

# 先找到 Redis 的進(jìn)程 ID
$ ps -aux | grep redis-server
# 查看 Redis Swap 使用情況
$ cat /proc/$pid/smaps | egrep '^(Swap|Size)'

輸出結(jié)果如下:

Size:               1256 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  0 kB
Size:                132 kB
Swap:                  0 kB
Size:              63488 kB
Swap:                  0 kB
Size:                132 kB
Swap:                  0 kB
Size:              65404 kB
Swap:                  0 kB
Size:            1921024 kB
Swap:                  0 kB

 

每一行 Size 表示 Redis 所用的一塊內(nèi)存大小,Size 下面的 Swap 就表示這塊 Size 大小的內(nèi)存,有多少數(shù)據(jù)已經(jīng)被換到磁盤上了,如果這兩個(gè)值相等,說(shuō)明這塊內(nèi)存的數(shù)據(jù)都已經(jīng)完全被換到磁盤上了。

 

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

11.碎片整理

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

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

# Memory
used_memory:5709194824
used_memory_human:5.32G
used_memory_rss:8264855552
used_memory_rss_human:7.70G
...
mem_fragmentation_ratio:1.45

 

這個(gè)內(nèi)存碎片率是怎么計(jì)算的?

很簡(jiǎn)單,mem_fragmentation_ratio = used_memory_rss / used_memory。

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

 

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

 

解決的方案一般如下:

1)如果你使用的是 Redis 4.0 以下版本,只能通過(guò)重啟實(shí)例來(lái)解決

2)如果你使用的是 Redis 4.0 版本,它正好提供了自動(dòng)碎片整理的功能,可以通過(guò)配置開(kāi)啟碎片自動(dòng)整理。

 

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

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

所以,當(dāng)你需要開(kāi)啟這個(gè)功能時(shí),最好提前測(cè)試評(píng)估它對(duì) Redis 的影響。

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

# 開(kāi)啟自動(dòng)內(nèi)存碎片整理(總開(kāi)關(guān))
activedefrag yes


# 內(nèi)存使用 100MB 以下,不進(jìn)行碎片整理
active-defrag-ignore-bytes 100mb


# 內(nèi)存碎片率超過(guò) 10%,開(kāi)始碎片整理
active-defrag-threshold-lower 10
# 內(nèi)存碎片率超過(guò) 100%,盡最大努力碎片整理
active-defrag-threshold-upper 100


# 內(nèi)存碎片整理占用 CPU 資源最小百分比
active-defrag-cycle-min 1
# 內(nèi)存碎片整理占用 CPU 資源最大百分比
active-defrag-cycle-max 25


# 碎片整理期間,對(duì)于 List/Set/Hash/ZSet 類型元素一次 Scan 的數(shù)量
active-defrag-max-scan-fields 1000

 

Redis如何優(yōu)化

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

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

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

 

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

一般有兩種方案來(lái)規(guī)避這個(gè)問(wèn)題:

1.集中過(guò)期 key 增加一個(gè)隨機(jī)過(guò)期時(shí)間,把集中過(guò)期的時(shí)間打散,降低 Redis 清理過(guò)期 key 的壓力

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

第一種方案,在設(shè)置 key 的過(guò)期時(shí)間時(shí),增加一個(gè)隨機(jī)時(shí)間,偽代碼可以這么寫:

# 在過(guò)期時(shí)間點(diǎn)之后的 5 分鐘內(nèi)隨機(jī)過(guò)期掉
redis.expireat(key, expire_time + random(300))

 

第二種方案,Redis 4.0 以上版本,開(kāi)啟 lazy-free 機(jī)制:

# 釋放過(guò)期 key 的內(nèi)存,放到后臺(tái)線程執(zhí)行
lazyfree-lazy-expire yes

運(yùn)維層面,你需要把 Redis 的各項(xiàng)運(yùn)行狀態(tài)數(shù)據(jù)監(jiān)控起來(lái),在 Redis 上執(zhí)行 INFO 命令就可以拿到這個(gè)實(shí)例所有的運(yùn)行狀態(tài)數(shù)據(jù)。

在這里我們需要重點(diǎn)關(guān)注 expired_keys 這一項(xiàng),它代表整個(gè)實(shí)例到目前為止,累計(jì)刪除過(guò)期 key 的數(shù)量。

你需要把這個(gè)指標(biāo)監(jiān)控起來(lái),當(dāng)這個(gè)指標(biāo)在很短時(shí)間內(nèi)出現(xiàn)了突增,需要及時(shí)報(bào)警出來(lái),然后與業(yè)務(wù)應(yīng)用報(bào)慢的時(shí)間點(diǎn)進(jìn)行對(duì)比分析,確認(rèn)時(shí)間是否一致,如果一致,則可以確認(rèn)確實(shí)是因?yàn)榧羞^(guò)期 key 導(dǎo)致的延遲變大。

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

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

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

3)拆分實(shí)例,把淘汰 key 的壓力分?jǐn)偟蕉鄠€(gè)實(shí)例上

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

 

4.fork耗時(shí)嚴(yán)重優(yōu)化

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

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

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

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

從建立同步時(shí),優(yōu)先檢測(cè)是否可以嘗試只同步部分?jǐn)?shù)據(jù),這種情況就是針對(duì)于之前已經(jīng)建立好了復(fù)制鏈路,只是因?yàn)楣收蠈?dǎo)致臨時(shí)斷開(kāi),故障恢復(fù)后重新建立同步時(shí),為了避免全量同步的資源消耗,Redis會(huì)優(yōu)先嘗試部分?jǐn)?shù)據(jù)同步,如果條件不符合,才會(huì)觸發(fā)全量同步。

這個(gè)判斷依據(jù)就是在master上維護(hù)的復(fù)制緩沖區(qū)大小,如果這個(gè)緩沖區(qū)配置的過(guò)小,很有可能在主從斷開(kāi)復(fù)制的這段時(shí)間內(nèi),master產(chǎn)生的寫入導(dǎo)致復(fù)制緩沖區(qū)的數(shù)據(jù)被覆蓋,重新建立同步時(shí)的slave需要同步的offset位置在master的緩沖區(qū)中找不到,那么此時(shí)就會(huì)觸發(fā)全量同步。

如何避免這種情況?解決方案就是適當(dāng)調(diào)大復(fù)制緩沖區(qū)repl-backlog-size的大小,這個(gè)緩沖區(qū)的大小默認(rèn)為1MB,如果實(shí)例寫入量比較大,可以針對(duì)性調(diào)大此配置。

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

那如何解決這個(gè)問(wèn)題呢?

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

當(dāng)然,即便我們把 Redis 綁定在多個(gè)邏輯核心上,也只能在一定程度上緩解主線程、子進(jìn)程、后臺(tái)線程在 CPU 資源上的競(jìng)爭(zhēng)。

因?yàn)檫@些子進(jìn)程、子線程還是會(huì)在這多個(gè)邏輯核心上進(jìn)行切換,存在性能損耗。?

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

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

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

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

 

  • Redis6.0 前綁定CPU核
taskset -c 0 ./redis-server
  • Redis6.0 后綁定CPU核
# Redis Server 和 IO 線程綁定到 CPU核心 0,2,4,6
server_cpulist 0-7:2
# 后臺(tái)子線程綁定到 CPU核心 1,3
bio_cpulist 1,3
# 后臺(tái) AOF rewrite 進(jìn)程綁定到 CPU 核心 8,9,10,11
aof_rewrite_cpulist 8-11
# 后臺(tái) RDB 進(jìn)程綁定到 CPU 核心 1,10,11
# bgsave_cpulist 1,10-1

 

如果你使用的正好是 Redis 6.0 版本,就可以通過(guò)以上配置,來(lái)進(jìn)一步提高 Redis 性能。

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

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

$ redis-cli info | grep process_id
process_id: 5332

然后,進(jìn)入 Redis 所在機(jī)器的 /proc 目錄下的該進(jìn)程目錄中:

$ cd /proc/5332

最后,運(yùn)行下面的命令,查看該 Redis 進(jìn)程的使用情況。在這兒,我只截取了部分結(jié)果:

$cat smaps | egrep '^(Swap|Size)'
Size: 584 kB
Swap: 0 kB
Size: 4 kB
Swap: 4 kB
Size: 4 kB
Swap: 0 kB
Size: 462044 kB
Swap: 462008 kB
Size: 21392 kB
Swap: 0 kB

一旦發(fā)生內(nèi)存 swap,最直接的解決方法就是增加機(jī)器內(nèi)存。如果該實(shí)例在一個(gè) Redis 切片集群中,可以增加 Redis 集群的實(shí)例個(gè)數(shù),來(lái)分?jǐn)偯總€(gè)實(shí)例服務(wù)的數(shù)據(jù)量,進(jìn)而減少每個(gè)實(shí)例所需的內(nèi)存量。

7.內(nèi)存大頁(yè)

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

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

$ cat /sys/kernel/mm/transparent_hugepage/enabled
[always] madvise never

如果執(zhí)行結(jié)果是 always,就表明內(nèi)存大頁(yè)機(jī)制被啟動(dòng)了;如果是 never,就表示,內(nèi)存大頁(yè)機(jī)制被禁止。

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

echo never /sys/kernel/mm/transparent_hugepage/enabled

其實(shí),操作系統(tǒng)提供的內(nèi)存大頁(yè)機(jī)制,其優(yōu)勢(shì)是,可以在一定程序上降低應(yīng)用程序申請(qǐng)內(nèi)存的次數(shù)。

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

8.刪除使用Lazy Free

支持版本:Redis 4.0+

1)主動(dòng)刪除鍵使用lazy free

  • UNLINK命令

 

127.0.0.1:7000> LLEN mylist
(integer) 2000000
127.0.0.1:7000> UNLINK mylist
(integer) 1
127.0.0.1:7000> SLOWLOG get
1) 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) 1812295
127.0.0.1:7000> flushall  //同步清理實(shí)例數(shù)據(jù),180萬(wàn)個(gè)key耗時(shí)1020毫秒
OK
(1.02s)
127.0.0.1:7000> DBSIZE
(integer) 1812637
127.0.0.1:7000> flushall async  //異步清理實(shí)例數(shù)據(jù),180萬(wàn)個(gè)key耗時(shí)約9毫秒
OK
127.0.0.1:7000> SLOWLOG get
 1) 1) (integer) 2996109
    2) (integer) 1505465989
    3) (integer) 9274       //指令運(yùn)行耗時(shí)9.2毫秒
    4) 1) "flushall"
       2) "async"
    5) "127.0.0.1:20110"
    6) ""

 

2)被動(dòng)刪除鍵使用lazy free

 

lazy free應(yīng)用于被動(dòng)刪除中,目前有4種場(chǎng)景,每種場(chǎng)景對(duì)應(yīng)一個(gè)配置參數(shù);默認(rèn)都是關(guān)閉。

lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
slave-lazy-flush no

 

  • lazyfree-lazy-eviction

針對(duì)redis內(nèi)存使用達(dá)到maxmeory,并設(shè)置有淘汰策略時(shí);在被動(dòng)淘汰鍵時(shí),是否采用lazy free機(jī)制;因?yàn)榇藞?chǎng)景開(kāi)啟lazy free, 可能使用淘汰鍵的內(nèi)存釋放不及時(shí),導(dǎo)致redis內(nèi)存超用,超過(guò)maxmemory的限制。此場(chǎng)景使用時(shí),請(qǐng)結(jié)合業(yè)務(wù)測(cè)試。(生產(chǎn)環(huán)境不建議設(shè)置yes)

  • lazyfree-lazy-expire

針對(duì)設(shè)置有TTL的鍵,達(dá)到過(guò)期后,被redis清理刪除時(shí)是否采用lazy free機(jī)制;此場(chǎng)景建議開(kāi)啟,因TTL本身是自適應(yīng)調(diào)整的速度。

 

  • lazyfree-lazy-server-del

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

 

  • slave-lazy-flush

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

3)lazy free的監(jiān)控

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

# info memory


# Memory
lazyfree_pending_objects:0

 

注意事項(xiàng):unlink命令入口函數(shù)unlinkCommand()和del調(diào)用相同函數(shù)delGenericCommand()進(jìn)行刪除KEY操作,使用lazy標(biāo)識(shí)是否為lazyfree調(diào)用。如果是lazyfree,則調(diào)用dbAsyncDelete()函數(shù)。

但并非每次unlink命令就一定啟用lazy free,redis會(huì)先判斷釋放KEY的代價(jià)(cost),當(dāng)cost大于LAZYFREE_THRESHOLD(64)才進(jìn)行l(wèi)azy free.

釋放key代價(jià)計(jì)算函數(shù)lazyfreeGetFreeEffort(),集合類型鍵,且滿足對(duì)應(yīng)編碼,cost就是集合鍵的元數(shù)個(gè)數(shù),否則cost就是1。

 

舉例:

  • 一個(gè)包含100元素的list key, 它的free cost就是100
  • 一個(gè)512MB的string key, 它的free cost是1 所以可以看出,redis的lazy free的cost計(jì)算主要時(shí)間復(fù)雜度相關(guān)。

 

9.AOF優(yōu)化

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

這相當(dāng)于在 AOF rewrite 期間,臨時(shí)把 appendfsync 設(shè)置為了 none,配置如下:

# AOF rewrite 期間,AOF 后臺(tái)子線程不進(jìn)行刷盤操作
# 相當(dāng)于在這期間,臨時(shí)把 appendfsync 設(shè)置為了 none
no-appendfsync-on-rewrite yes

 

當(dāng)然,開(kāi)啟這個(gè)配置項(xiàng),在 AOF rewrite 期間,如果實(shí)例發(fā)生宕機(jī),那么此時(shí)會(huì)丟失更多的數(shù)據(jù),性能和數(shù)據(jù)安全性,你需要權(quán)衡后進(jìn)行選擇。

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

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

10.Swap優(yōu)化

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

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

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

預(yù)防的辦法就是,你需要對(duì) Redis 機(jī)器的內(nèi)存和 Swap 使用情況進(jìn)行監(jiān)控,在內(nèi)存不足或使用到 Swap 時(shí)報(bào)警出來(lái),及時(shí)處理。

 

Redis變慢了排查步驟

1.獲取 Redis 實(shí)例在當(dāng)前環(huán)境下的基線性能。

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

3.是否對(duì)過(guò)期 key 設(shè)置了相同的過(guò)期時(shí)間?對(duì)于批量刪除的 key,可以在每個(gè) key 的過(guò)期時(shí)間上加一個(gè)隨機(jī)數(shù),避免同時(shí)刪除。

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

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

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

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

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

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

分享到:
標(biāo)簽:Redis
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定