淺談Redis的異步機(jī)制
前言
命令操作、系統(tǒng)配置、關(guān)鍵機(jī)制、硬件配置等會(huì)影響 Redis 的性能,不僅要知道具體的機(jī)制,盡可能避免性能異常的情況出現(xiàn),還要提前準(zhǔn)備好應(yīng)對(duì)異常的方案。
Redis 內(nèi)部的阻塞式操作:
- CPU 核和 NUMA 架構(gòu)的影響;
- Redis 關(guān)鍵系統(tǒng)配置;
- Redis 內(nèi)存碎片;
- Redis 緩沖區(qū)。
一、Redis 的阻塞點(diǎn)
和 Redis 實(shí)例交互的對(duì)象,以及交互時(shí)會(huì)發(fā)生的操作:
- 客戶端:網(wǎng)絡(luò) IO,鍵值對(duì)增刪改查操作,數(shù)據(jù)庫(kù)操作;
- 磁盤:生成 RDB 快照,記錄 AOF 日志,AOF 日志重寫;
- 主從節(jié)點(diǎn):主庫(kù)生成、傳輸 RDB 文件,從庫(kù)接收 RDB 文件、清空數(shù)據(jù)庫(kù)、加載 RDB 文件;
- 切片集群實(shí)例:向其他實(shí)例傳輸哈希槽信息,數(shù)據(jù)遷移。
4 類交互對(duì)象和具體的操作之間的關(guān)系:
和客戶端交互時(shí)的阻塞點(diǎn):
網(wǎng)絡(luò) IO 有時(shí)候會(huì)比較慢,但是 Redis 使用了 IO 多路復(fù)用機(jī)制,避免了主線程一直處在等待網(wǎng)絡(luò)連接或請(qǐng)求到來(lái)的狀態(tài),所以網(wǎng)絡(luò) IO 不是導(dǎo)致 Redis 阻塞的因素。
鍵值對(duì)的增刪改查操作是 Redis 和客戶端交互的主要部分,也是 Redis 主線程執(zhí)行的主要任務(wù)。復(fù)雜度高的增刪改查操作肯定會(huì)阻塞 Redis。
判斷操作復(fù)雜度高低的標(biāo)準(zhǔn):看操作的復(fù)雜度是否為 O(N)。
Redis 的第一個(gè)阻塞點(diǎn):集合全量查詢和聚合操作:
Redis 中涉及集合的操作復(fù)雜度通常為 O(N),使用時(shí)需重視起來(lái)。
例如集合元素全量查詢操作 HGETALL、SMEMBERS,以及集合的聚合統(tǒng)計(jì)操作,例如求交、并和差集。
Redis 的第二個(gè)阻塞點(diǎn) :bigkey 刪除操作
集合自身的刪除操作同樣也有潛在的阻塞風(fēng)險(xiǎn)。刪除操作的本質(zhì)是要釋放鍵值對(duì)占用的內(nèi)存空間。 釋放內(nèi)存只是第一步,為了更加高效地管理內(nèi)存空間,在應(yīng)用程序釋放內(nèi)存時(shí),操作系統(tǒng)需要把釋放掉的內(nèi)存塊插入一個(gè)空閑內(nèi)存塊的鏈表,以便后續(xù)進(jìn)行管理和再分配。
這個(gè)過(guò)程本身需要一定時(shí)間,而且會(huì)阻塞當(dāng)前釋放內(nèi)存的應(yīng)用程序,如果一下子釋放了大量?jī)?nèi)存,空閑內(nèi)存塊鏈表操作時(shí)間就會(huì)增加,相應(yīng)地就會(huì)造成 Redis 主線程的阻塞。
釋放大量?jī)?nèi)存的時(shí)機(jī):在刪除大量鍵值對(duì)數(shù)據(jù)的時(shí)候,刪除包含了大量元素的集合,也稱為 bigkey 刪除。
不同元素?cái)?shù)量的集合在進(jìn)行刪除操作時(shí)所消耗的時(shí)間:
得出三個(gè)結(jié)論:
- 當(dāng)元素?cái)?shù)量從 10 萬(wàn)增加到 100 萬(wàn)時(shí),4 大集合類型的刪除時(shí)間的增長(zhǎng)幅度從 5 倍上升到了近 20 倍;
- 集合元素越大,刪除所花費(fèi)的時(shí)間就越長(zhǎng);
- 當(dāng)刪除有 100 萬(wàn)個(gè)元素的集合時(shí),最大的刪除時(shí)間絕對(duì)值已經(jīng)達(dá)到了 1.98s(Hash 類 型)。Redis 的響應(yīng)時(shí)間一般在微秒級(jí)別,一個(gè)操作達(dá)到了近 2s,不可避免地會(huì)阻塞主線程。
Redis 的第三個(gè)阻塞點(diǎn):清空數(shù)據(jù)庫(kù)
既然頻繁刪除鍵值對(duì)都是潛在的阻塞點(diǎn)了,在 Redis 的數(shù)據(jù)庫(kù)級(jí)別操作中,清空數(shù)據(jù)庫(kù)(例如 FLUSHDB 和 FLUSHALL 操作)也是一個(gè)潛在的阻塞風(fēng)險(xiǎn),因?yàn)樗婕暗絼h除和釋放所有的鍵值對(duì)。
Redis 的第四個(gè)阻塞點(diǎn):AOF 日志同步寫
磁盤 IO 一般都是比較費(fèi)時(shí)費(fèi)力的,需要重點(diǎn)關(guān)注。 Redis 開發(fā)者早已認(rèn)識(shí)到磁盤 IO 會(huì)帶來(lái)阻塞,所以把 Redis 設(shè)計(jì)為采用子進(jìn)程的方式生成 RDB 快照文件、執(zhí)行 AOF 日志重寫操作。由子進(jìn)程負(fù)責(zé)執(zhí)行,慢速的磁盤 IO 就不會(huì)阻塞主線程了。
Redis 直接記錄 AOF 日志時(shí),會(huì)根據(jù)不同的寫回策略對(duì)數(shù)據(jù)做落盤保存。一個(gè)同步寫磁盤的操作的耗時(shí)大約是 1~2ms,如果有大量的寫操作需要記錄在 AOF 日志中,并同步寫回的話,會(huì)阻塞主線程。
Redis 的第五個(gè)阻塞點(diǎn):從庫(kù)加載 RDB 文件
在主從集群中,主庫(kù)需要生成 RDB 文件,并傳輸給從庫(kù)。
主庫(kù)在復(fù)制的過(guò)程中,創(chuàng)建和傳輸 RDB 文件都是由子進(jìn)程來(lái)完成的,不會(huì)阻塞主線程。
但是從庫(kù)在接收了 RDB 文件后,需要使用 FLUSHDB 命令清空當(dāng)前數(shù)據(jù)庫(kù),正好撞上了第三個(gè)阻塞點(diǎn)。
從庫(kù)在清空當(dāng)前數(shù)據(jù)庫(kù)后,需要把 RDB 文件加載到內(nèi)存,這個(gè)過(guò)程的快慢和 RDB 文件的大小密切相關(guān),RDB 文件越大,加載過(guò)程越慢。
切片集群實(shí)例交互時(shí)的阻塞點(diǎn)
部署 Redis 切片集群時(shí),每個(gè) Redis 實(shí)例上分配的哈希槽信息需要在不同實(shí)例間進(jìn)行傳遞,當(dāng)需要進(jìn)行負(fù)載均衡或者有實(shí)例增刪時(shí),數(shù)據(jù)會(huì)在不同的實(shí)例間進(jìn)行遷移。不過(guò)哈希槽的信息量不大,而數(shù)據(jù)遷移是漸進(jìn)式執(zhí)行的,這兩類操作對(duì) Redis 主線程的阻塞風(fēng)險(xiǎn)不大。
如果使用了 Redis Cluster 方案,而且同時(shí)正好遷移的是 bigkey 的話,就會(huì)造成主線程的阻塞,因?yàn)?Redis Cluster 使用了同步遷移。
五個(gè)阻塞點(diǎn):
- 集合全量查詢和聚合操作;
- bigkey 刪除;
- 清空數(shù)據(jù)庫(kù);
- AOF 日志同步寫;
- 從庫(kù)加載 RDB 文件。
二、可以異步執(zhí)行的阻塞點(diǎn)
為了避免阻塞式操作,Redis 提供了異步線程機(jī)制:
Redis 會(huì)啟動(dòng)一些子線程,然后把一些任務(wù)交給這些子線程,讓它們?cè)诤笈_(tái)完成,而不再由主線程來(lái)執(zhí)行這些任務(wù)。可以避免阻塞主線程。
異步執(zhí)行對(duì)操作的要求:
一個(gè)能被異步執(zhí)行的操作并不是 Redis 主線程的關(guān)鍵路徑上的操作(客戶端把請(qǐng)求發(fā)送給 Redis 后,等著 Redis 返回?cái)?shù)據(jù)結(jié)果的操作)。
主線程接收到操作 1 后,操作 1 并不用給客戶端返回具體的數(shù)據(jù),主線程可以把它交給后臺(tái)子線程來(lái)完成,同時(shí)只要給客戶端返回一個(gè)“OK”結(jié)果就行。
在子線程執(zhí)行操作 1 的時(shí)候,客戶端又向 Redis 實(shí)例發(fā)送了操作 2,客戶端是需要使用操作 2 返回的數(shù)據(jù)結(jié)果的,如果操作 2 不返回結(jié)果,那么客戶端將一直處于等待狀態(tài)。
操作 1 就不算關(guān)鍵路徑上的操作,因?yàn)樗挥媒o客戶端返回具體數(shù)據(jù),所以可以由后臺(tái)子線程異步執(zhí)行。
操作 2 需要把結(jié)果返回給客戶端,它就是關(guān)鍵路徑上的操作,所以主線程必須立即把這個(gè)操作執(zhí)行完。
- Redis 讀操作是典型的關(guān)鍵路徑操作,因?yàn)榭蛻舳税l(fā)送了讀操作之后,就會(huì)等待讀取的數(shù)據(jù)返回,以便進(jìn)行后續(xù)的數(shù)據(jù)處理。而 Redis 的第一個(gè)阻塞點(diǎn)“集合全量查詢 和聚合操作”都涉及到了讀操作,不能進(jìn)行異步操作。
- 刪除操作并不需要給客戶端返回具體的數(shù)據(jù)結(jié)果,不算是關(guān)鍵路徑操作。“bigkey 刪除”和“清空數(shù)據(jù)庫(kù)”都是對(duì)數(shù)據(jù)做刪除,并不在關(guān)鍵路徑上??梢允褂煤笈_(tái)子線程來(lái)異步執(zhí)行刪除操作。
- “AOF 日志同步寫”,為了保證數(shù)據(jù)可靠性,Redis 實(shí)例需要保證 AOF 日志中的操作記錄已經(jīng)落盤,這個(gè)操作雖然需要實(shí)例等待,但它并不會(huì)返回具體的數(shù)據(jù)結(jié)果給實(shí)例。所以可以啟動(dòng)一個(gè)子線程來(lái)執(zhí)行 AOF 日志的同步寫。
- “從庫(kù)加載 RDB 文件”要想對(duì)客戶端提供數(shù)據(jù)存取服務(wù),就必須把 RDB 文件加載完成。這個(gè)操作也屬于關(guān)鍵路徑上的操作,必須讓從庫(kù)的主線程來(lái)執(zhí)行。
除了“集合全量查詢和聚合操作”和“從庫(kù)加載 RDB 文 件”,其他三個(gè)阻塞點(diǎn)涉及的操作都不在關(guān)鍵路徑上,可以使用 Redis 的異步子線程機(jī)制來(lái)實(shí)現(xiàn) bigkey 刪除,清空數(shù)據(jù)庫(kù),以及 AOF 日志同步寫。
三、異步的子線程機(jī)制
Redis 主線程啟動(dòng)后,會(huì)使用操作系統(tǒng)提供的 pthread_create 函數(shù)創(chuàng)建 3 個(gè)子線程,負(fù)責(zé)AOF 日志寫操作、鍵值對(duì)刪除、文件關(guān)閉的異步執(zhí)行。
主線程通過(guò)一個(gè)鏈表形式的任務(wù)隊(duì)列和子線程進(jìn)行交互。
當(dāng)收到鍵值對(duì)刪除和清空數(shù)據(jù)庫(kù)的操作時(shí),主線程會(huì)把這個(gè)操作封裝成一個(gè)任務(wù),放入到任務(wù)隊(duì)列中,然后給客戶端返回一個(gè)完成信息,表明刪除已經(jīng)完成。
但實(shí)際上,這個(gè)時(shí)候刪除還沒(méi)有執(zhí)行,等到后臺(tái)子線程從任務(wù)隊(duì)列中讀取任務(wù)后,才開始實(shí)際刪除鍵值對(duì),并釋放相應(yīng)的內(nèi)存空間。這種異步刪除也稱為惰性刪除 (lazy free)。
當(dāng) AOF 日志配置成 everysec 選項(xiàng)后,主線程會(huì)把 AOF 寫日志操作封裝成一個(gè)任務(wù),也放到任務(wù)隊(duì)列中。后臺(tái)子線程讀取任務(wù)后,開始自行寫入 AOF 日志,主線程就不用一直等待 AOF 日志寫完了。
Redis 中的異步子線程執(zhí)行機(jī)制:
異步的鍵值對(duì)刪除和數(shù)據(jù)庫(kù)清空操作是 Redis 4.0 后提供的功能,Redis 也提供了新的命令來(lái)執(zhí)行這兩個(gè)操作:
- 鍵值對(duì)刪除:集合類型中有大量元素(例如有百萬(wàn)級(jí)別或千萬(wàn)級(jí)別元素)需要?jiǎng)h除時(shí),建議使用 UNLINK 命令;
- 清空數(shù)據(jù)庫(kù):可以在 FLUSHDB 和 FLUSHALL 命令后加上 ASYNC 選項(xiàng),讓后臺(tái)子線程異步地清空數(shù)據(jù)庫(kù)。
FLUSHDB ASYNC FLUSHALL AYSNC
總結(jié)
Redis 實(shí)例運(yùn)行時(shí)的 4 大類交互對(duì)象:客戶端、磁盤、主從庫(kù)實(shí)例、 切片集群實(shí)例。
基于這 4 大類交互對(duì)象導(dǎo)致 Redis 性能受損的 5 大阻塞點(diǎn):集合全量查詢和聚合操作、bigkey 刪除、清空數(shù)據(jù)庫(kù)、AOF 日志同步寫、從庫(kù)加載 RDB 文件。
bigkey 刪除、清空數(shù)據(jù)庫(kù)、AOF 日志同步寫不屬于關(guān)鍵路徑操作, 可以使用異步子線程機(jī)制來(lái)完成。
Redis 在運(yùn)行時(shí)會(huì)創(chuàng)建三個(gè)子線程,主線程會(huì)通過(guò)一個(gè)任務(wù)隊(duì)列和三個(gè)子線程進(jìn)行交互。子線程會(huì)根據(jù)任務(wù)的具體類型,來(lái)執(zhí)行相應(yīng)的異步操作。
異步刪除操作是 Redis 4.0 以后才有的功能,如果使用的是 4.0 之前的版本,遇到 bigkey 刪除時(shí),建議:先使用集合類型提供的 SCAN 命令讀取數(shù)據(jù), 然后再進(jìn)行刪除。因?yàn)橛?SCAN 命令可以每次只讀取一部分?jǐn)?shù)據(jù)并進(jìn)行刪除,這樣可以避免一次性刪除大量 key 給主線程帶來(lái)的阻塞。
例如,對(duì)于 Hash 類型的 bigkey 刪除,使用 HSCAN 命令,每次從 Hash 集合中獲取一部分鍵值對(duì)(例如 200 個(gè)),再使用 HDEL 刪除這些鍵值對(duì),可以把刪除壓力分?jǐn)偟蕉啻尾僮髦?,每次刪除操作的耗時(shí)就不會(huì)太長(zhǎng),也就不會(huì)阻塞主線程了。
集合全量查詢和聚合操作、從庫(kù)加載 RDB 文件是在關(guān)鍵路徑上,無(wú)法使用異步操作來(lái)完成。
建議:
- 集合全量查詢和聚合操作:使用 SCAN 命令,分批讀取數(shù)據(jù),再在客戶端進(jìn)行聚合計(jì)算;
- 從庫(kù)加載 RDB 文件:把主庫(kù)的數(shù)據(jù)量大小控制在 2~4GB 左右,以保證 RDB 文件能以較快的速度加載。
到此這篇關(guān)于淺談Redis的異步機(jī)制的文章就介紹到這了,更多相關(guān)Redis 異步機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
redis 數(shù)據(jù)刪除策略和逐出算法的問(wèn)題小結(jié)
這篇文章主要介紹了redis 數(shù)據(jù)刪除策略和逐出算法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06Redis隊(duì)列和阻塞隊(duì)列的實(shí)現(xiàn)
本文主要介紹了Redis隊(duì)列和阻塞隊(duì)列的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-11-11Redis 緩存實(shí)現(xiàn)存儲(chǔ)和讀取歷史搜索關(guān)鍵字的操作方法
這篇文章主要介紹了Redis 緩存實(shí)現(xiàn)存儲(chǔ)和讀取歷史搜索關(guān)鍵字,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12Redis 數(shù)據(jù)遷移的項(xiàng)目實(shí)踐
本文主要介紹了Redis 數(shù)據(jù)遷移的項(xiàng)目實(shí)踐,通過(guò)Redis-shake的sync(同步)模式,可以將Redis的數(shù)據(jù)實(shí)時(shí)遷移至另一套R(shí)edis環(huán)境,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09Redis未授權(quán)訪問(wèn)配合SSH key文件利用詳解
這篇文章主要給大家介紹了關(guān)于Redis未授權(quán)訪問(wèn)配合SSH key文件利用的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-09-09深入了解Redis連接數(shù)問(wèn)題的現(xiàn)象和解法
一般情況?Redis?連接數(shù)問(wèn)題并不常見(jiàn),但是當(dāng)你業(yè)務(wù)服務(wù)增加、對(duì)?Redis?的依賴持續(xù)增強(qiáng)的過(guò)程中,可能會(huì)遇到很多?Redis?的問(wèn)題,這個(gè)時(shí)候,Redis?連接數(shù)可能就成了一個(gè)常見(jiàn)的問(wèn)題,在本章節(jié),希望能夠帶大家了解Redis連接數(shù)問(wèn)題的現(xiàn)象和解法,需要的朋友可以參考下2023-12-12基于Redis實(shí)現(xiàn)分布式鎖以及任務(wù)隊(duì)列
這篇文章主要介紹了基于Redis實(shí)現(xiàn)分布式鎖以及任務(wù)隊(duì)列,需要的朋友可以參考下2015-11-11詳解Redis中key的命名規(guī)范和值的命名規(guī)范
這篇文章主要介紹了詳解Redis中key的命名規(guī)范和值的命名規(guī)范,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12