偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Valkey 單點性能比肩 Redis 集群了?Valkey8.0 新特性分析

數(shù)據(jù)庫 Redis
本文分析了在 Valkey8.0 中通過引入異步 IO 線程、內(nèi)存預(yù)?。≒refetch)、內(nèi)存訪問分?jǐn)偅∕AA)等新特性,極大的提升了 Valkey 單節(jié)點性能,這些技術(shù)手段和算法思想也值得我們在實際業(yè)務(wù)開發(fā)中借鑒和使用。

一、背景

二、異步 IO 線程

    1. Redis 6.0 多線程 IO

    2. Valkey 8.0 異步 IO 線程

    3. 卸載更多任務(wù)到 IO 線程

三、數(shù)據(jù)預(yù)取(Prefetch)與內(nèi)存訪問分?jǐn)?MAA)

    1. 數(shù)據(jù)預(yù)?。≒refetch)

    2. 內(nèi)存訪問分?jǐn)偅∕AA)

    3. Valkey8.0 預(yù)取數(shù)據(jù)應(yīng)用

四、總結(jié)

一、背景

Valkey 社區(qū)于 2024 年 09 月發(fā)布了 Valkey8.0 正式版,在之前的文章《Redis 是單線程模型?》中,我們提到,Redis 社區(qū)在 Redis6.0 中引入了多線程 IO 特性,將 Redis 單節(jié)點訪問請求從 10W/s 提升到 20W/s,而在 Valkey8.0 版本中,通過引入異步 IO 線程、內(nèi)存預(yù)?。≒refetch)、內(nèi)存訪問分?jǐn)偅∕AA)等新特性,并且除了將讀寫網(wǎng)絡(luò)數(shù)據(jù)卸載到 IO 線程執(zhí)行外,還會將 event 事件循環(huán)、對象內(nèi)存釋放等耗時動作也卸載到 IO 線程執(zhí)行,使得 Valkey 單節(jié)點訪問請求可以提升到 100W/s,大幅提升 Valkey 單節(jié)點性能。

Valkey 8.0中引入的異步 IO 與 Redis 6.0 中的多線程 IO 有什么區(qū)別?Valkey8.0 中如何應(yīng)用內(nèi)存預(yù)取和內(nèi)存訪問分?jǐn)偧夹g(shù)進一步來提升性能的?本篇文章讓我們來一起看看。

  • 2024 年,Redis 商業(yè)支持公司 Redis Labs 宣布 Redis 核心代碼的許可證從 BSD 變更為 RSALv2 ,明確禁止云廠商提供 Redis 托管服務(wù),這一決定直接導(dǎo)致社區(qū)分裂。
  • 為維護開源自由,Linux 基金會聯(lián)合多家科技公司(包括 AWS、Google Cloud、Oracle 等)宣布支持 Valkey ,Valkey 基于 Redis 7.2.4 開發(fā),作為 Redis 的替代分支。
  • Valkey8.0 為 Valkey 社區(qū)發(fā)布的首個主要大版本。
  • 最新消息,在 Redis 項目創(chuàng)始人 antirez 今年加入 Redis 商業(yè)公司 5 個月后,Redis 宣傳從 Redis8 開始,Redis 項目重新開源。

二、異步 IO 線程背景

Redis6.0多線程IO

在 Redis 6.0 中引入了多線程 IO 特性,用來處理網(wǎng)絡(luò)數(shù)據(jù)的讀寫和協(xié)議解析,讀寫數(shù)據(jù)執(zhí)行流程如下所示:

圖片圖片

在 Redis6.0 中,讀數(shù)據(jù)流程是主線程先將所有可讀客戶端加入一個隊列,全部處理完后,再通過 RR 算法將這些可讀客戶端分配給 IO 線程,由 IO 線程執(zhí)行讀數(shù)據(jù);寫數(shù)據(jù)流程類似處理。

盡管引入多線程 IO 大幅提升了 Redis 性能,但是 Redis6.0 的多線程 IO 仍然存在一些不足:

  • 主線程在處理客戶端命令時,IO 線程會均處于空閑狀態(tài);由于主線程會阻塞等待所有 IO 線程完成讀寫數(shù)據(jù),主線程在執(zhí)行 IO 相關(guān)任務(wù)期間的性能受到最慢 IO 線程速度的限制
  • 由于主線程同步等待 IO 線程,IO 線程僅執(zhí)行讀取解析和寫入操作,主線程仍然承擔(dān)大部分 IO 任務(wù)

Valkey 8.0 異步 IO 線程

Valkey8.0 通過使用任務(wù)隊列使主線程向 IO 線程發(fā)送任務(wù),IO 線程異步并行執(zhí)行任務(wù)提升整體性能。Valkey 8.0 異步 IO 線程工作流程整體設(shè)計圖如下所示:

圖片圖片

IO 線程初始化

在 Valkey 啟動時進行初始化的時候,根據(jù)配置的線程數(shù)量server.io_threads_num 決定是否創(chuàng)建異步 IO 線程,如果server.io_threads_num == 1表示不開啟,另外,IO 線程數(shù)量最大不超過 15 個;如果配置開啟異步 IO 線程,則初始化的時候按需創(chuàng)建異步 IO 線程。

線程間通信

Valkey 初始化創(chuàng)建 IO 線程的時候,會給每個 IO 線程創(chuàng)建一個靜態(tài)、無鎖、固定大?。ù笮?2048)的環(huán)形緩沖區(qū)作為任務(wù)隊列,用于主線程發(fā)送任務(wù),以及 IO 線程接收任務(wù)。

環(huán)形緩沖區(qū)是從主線程到 IO 線程的單向通道。當(dāng)發(fā)生讀/寫事件時,主線程會發(fā)送一個讀/寫任務(wù),然后在進入 event 事件監(jiān)測休眠之前,它會遍歷所有待處理的讀/寫客戶端,檢查每個客戶端的 IO 線程是否已經(jīng)處理完畢。IO 線程通過切換客戶端結(jié)構(gòu)體上的原子標(biāo)志 read_state / write_state 來表示它已經(jīng)處理完一個客戶端的讀/寫操作。

讀數(shù)據(jù)流程

讀數(shù)據(jù)流程如下圖所示:

圖片圖片

主線程監(jiān)測到有讀事件時,檢查是否開啟 IO 線程,如果開啟了 IO 線程,會根據(jù)算法選擇一個 IO 線程,檢查選中的 IO 線程任務(wù)隊列是否已滿,如果任務(wù)隊列未滿,則將該待讀事件客戶端加入IO 線程的任務(wù)隊列。

如果未開啟 IO 線程,或者選中的 IO 線程任務(wù)隊列已滿,則由主線程完成讀數(shù)據(jù)操作并執(zhí)行命令。

IO 線程循環(huán)從任務(wù)隊列獲取任務(wù),如果是讀數(shù)據(jù)任務(wù),則執(zhí)行讀數(shù)據(jù)流程。先讀取數(shù)據(jù),然后解析命令,并從命令列表中查找命令并保存在指定字段(這里也是把本來由主線程在執(zhí)行命令時執(zhí)行的動作卸載到 IO 線程完成)。

主線程在進入 event 事件監(jiān)聽睡眠前,循環(huán)遍歷所有在等待 IO 線程讀數(shù)據(jù)的客戶端,檢查數(shù)據(jù)是否讀取完成,如果是則加入批量預(yù)取數(shù)據(jù)數(shù)組,當(dāng)全部客戶端都檢查完成或者批量預(yù)取數(shù)據(jù)數(shù)組存滿,則批量執(zhí)行命令。

在 Redis6.0 中,需要先將所有可讀客戶端存入一個隊列,再遍歷可讀客戶端列表通過 RR 算法將可讀事件分配到不同的 IO 線程中,然后主線程設(shè)置 IO 線程開啟讀數(shù)據(jù),在主線程執(zhí)行這些操作期間,IO 線程均處于空閑狀態(tài)。

在 Valkey 8.0 中,每監(jiān)測到一個可讀事件,立即通過任務(wù)隊列發(fā)送到一個 IO 線程,IO 線程立即可以開始讀數(shù)據(jù)操作,主線程遍歷后續(xù)可讀事件期間,IO 線程異步在執(zhí)行讀取操作。

寫數(shù)據(jù)流程

主線程執(zhí)行完每個命令時,將客戶端加入等待等寫隊列clients_pending_write,將響應(yīng)客戶端的數(shù)據(jù)寫入到響應(yīng)緩存 buf 或者 reply 鏈表。

主線程處理完所有命令后,循環(huán)遍歷等待寫隊列clients_pending_write,將通過算法選擇一個 IO 線程,如果選中的 IO 線程任務(wù)隊列未滿,將該客戶端寫數(shù)據(jù)任務(wù)加入 IO 線程的任務(wù)隊列。

IO 線程循環(huán)從任務(wù)隊列獲取任務(wù),如果是寫數(shù)據(jù)任務(wù),則執(zhí)行寫數(shù)據(jù)流,將數(shù)據(jù)寫回給用戶。

動態(tài)調(diào)整 IO 線程數(shù)量

每次在有可讀事件或者可寫事件需要執(zhí)行前,Valkey 會根據(jù)可讀/寫事件數(shù)量,動態(tài)調(diào)整活躍 IO 線程數(shù)量,最大活躍 IO 線程數(shù)量不超過設(shè)置的允許 IO 線程數(shù)量(固定為 15)。

根據(jù)可讀/寫事件數(shù)量、每個 IO 線程可執(zhí)行事件數(shù)量(可配置)、以及最大允許活躍 IO 線程數(shù)量,計算需要的目標(biāo)活躍 IO 線程數(shù)量,當(dāng)前活躍 IO 線程數(shù)量小于目標(biāo)數(shù)量時,可增加活躍 IO 線程,當(dāng)前活躍 IO 線程數(shù)量大于目標(biāo)數(shù)量時,可減少活躍 IO 線程。

動態(tài)增加或者減少活躍 IO 線程數(shù)量,減少活躍 IO 線程并不會直接關(guān)閉創(chuàng)建出來的 IO 線程,而是通過加鎖使當(dāng)前沒有任務(wù)可執(zhí)行的 IO 線程暫停輪詢查找任務(wù),避免 IO 線程不必要的空輪詢;同樣增加活躍 IO 線程只需要主線程釋放鎖即可,IO 線程獲取到鎖后,開始輪詢獲取是否有可執(zhí)行任務(wù)需要執(zhí)行。

  • 盡管 I/O 線程數(shù)量可動態(tài)調(diào)整,具有動態(tài)特性,但主線程仍保持線程親和性,確保在可能的情況下由同一個 I/O 線程處理同一客戶端的 I/O 請求,從而提高內(nèi)存訪問的局部性。

卸載更多任務(wù)到 IO 線程

在 Valkey 8.0 中,除了讀取解析數(shù)據(jù)/寫入操作之外,還將很多額外的工作卸載到 I/O 線程,以便更好地利用 I/O 線程并減少主線程的負載。

事件輪詢卸載到 IO 線程

在 Valkey 中使用了 IO 多路復(fù)用模型實現(xiàn)在主線程中來高效處理所有來自客戶端的連接讀寫訪問,而套接字輪詢系統(tǒng)調(diào)用(例如epoll_wait)是開銷很大的過程,僅由主線程來執(zhí)行會消耗大量主線程時間。

在 Valkey8.0 中,當(dāng)主線程有待處理的 I/O 操作或要執(zhí)行的命令時,主線程都會將套接字輪詢系統(tǒng)調(diào)用調(diào)度到 IO 線程執(zhí)行,否則由主線程自身來執(zhí)行。

為避免競爭條件,在任何給定時間,最多只有一個線程(io_thread 或主線程)執(zhí)行epoll_wait,當(dāng)主線程將事件輪詢系統(tǒng)調(diào)用分配給一個 IO 線程執(zhí)行后,主線程執(zhí)行完命令處理后,不再執(zhí)行事件輪詢系統(tǒng)調(diào)用,而是直接檢查 IO 線程的輪詢等待結(jié)果,查看是否有可讀寫事件。

對象釋放卸載到 IO 線程

在 Valkey 讀取客戶端數(shù)據(jù)后,命令解析過程中會分配大量命令參數(shù)對象,在命令處理完成后,需要釋放為這些命令參數(shù)分配的內(nèi)存空間,在 Valkey8.0 中,將這些命令參數(shù)內(nèi)存空間釋放分配給 IO 線程執(zhí)行,并且會分配給執(zhí)行該參數(shù)解析(內(nèi)存分配)的同一個 IO 線程來執(zhí)行(通過客戶端 ID 進行標(biāo)識)。

命令查找卸載

如前面在讀數(shù)據(jù)流程中提到的,當(dāng) IO 線程解析來自客戶端的 Querybuf 的命令時,它可以在命令字典中執(zhí)行命令查找,并且 IO 線程會將查找到的命令存儲在客戶端的指定字段中,后續(xù)主線程執(zhí)行命令時直接使用即可,可以節(jié)省主線程執(zhí)行命令的時間。

三、數(shù)據(jù)預(yù)取(Prefetch)與內(nèi)存訪問分?jǐn)偅∕AA)

在 Valkey8.0 中引入異步 IO 線程提高并行度,并且將更多的工作轉(zhuǎn)移到 IO 線程,將主線程執(zhí)行的 I/O 操作量降至最低,此時,經(jīng)過測試,單個 Valkey 節(jié)點每秒處理請求可達 80W。

通過分析開啟 IO 線程后 Valkey 性能,主線程大部分時間都花銷在訪問內(nèi)存查找 key,這是因為 Valkey 字典是一個簡單但低效的鏈?zhǔn)焦崿F(xiàn),在遍歷哈希鏈表時,每次訪問 dictEntry 結(jié)構(gòu)體、指向鍵的指針或值對象,都很可能需要進行昂貴的外部內(nèi)存訪問。

于是在 Valkey8.0 中引入了數(shù)據(jù)預(yù)?。≒refetch)和內(nèi)存訪問分?jǐn)偅∕AA)技術(shù),進一步提升 Valkey 單節(jié)點訪問性能。

數(shù)據(jù)預(yù)?。≒refetch)

隨著摩爾定律在過去 30 年間的持續(xù)生效,CPU 的運算速度大幅提升,而存儲器(主要是內(nèi)存)的速度提升相對較慢,這導(dǎo)致了存儲器與 CPU 之間的速度差異。當(dāng) CPU 執(zhí)行指令時,如果需要從內(nèi)存中讀取數(shù)據(jù)或指令,由于存儲器速度的限制,CPU 可能需要等待訪問存儲器操作完成,從而導(dǎo)致性能瓶頸。

圖片圖片

為了解決訪問存儲器瓶頸這一問題,現(xiàn)代計算機系統(tǒng)采用了多級緩存及內(nèi)存層次結(jié)構(gòu),包括 L1、L2、L3 緩存以及主存等。盡管高速緩存(Cache)能夠提供更快的訪問速度,但其容量有限,當(dāng) CPU 訪問的數(shù)據(jù)無法在高速緩存中找到時,就需要從更慢的內(nèi)存層級中獲取數(shù)據(jù),這會導(dǎo)致較高的訪問延遲,并降低整體性能。

數(shù)據(jù)預(yù)取(Prefetching)技術(shù)可以在一定程度上解決訪問存儲器成為 CPU 性能瓶頸的問題。數(shù)據(jù)預(yù)取是一種提前將數(shù)據(jù)或指令從內(nèi)存中預(yù)先加載到高速緩存中的技術(shù)。通過預(yù)取,CPU 可以在實際使用之前將數(shù)據(jù)預(yù)先加載到緩存中,從而減少對內(nèi)存的訪問延遲。這樣可以提高訪問存儲器的效率,減少 CPU 等待訪問存儲器的時間,從而提升整體性能。

__builtin_prefetch() 是 gcc 編輯器提供的一個內(nèi)置函數(shù),它通過對數(shù)據(jù)手工預(yù)取到 CPU 的緩存中,減少了讀取延遲,從而提高程序的執(zhí)行效率。

在 Valkey8.0 中,主線程在執(zhí)行命令之前,通過使用 __builtin_prefetch() 命令,對所有即將操作的命令參數(shù)、key 及對應(yīng)的 value 進行批量預(yù)取,提高主線程執(zhí)行命令的效率。

內(nèi)存訪問分?jǐn)偅∕AA)

內(nèi)存訪問攤銷 (MAA) 是一種旨在通過降低內(nèi)存訪問延遲的影響來優(yōu)化動態(tài)數(shù)據(jù)結(jié)構(gòu)性能的技術(shù)。它適用于需要并發(fā)執(zhí)行多個操作的情況。其背后的原理是,對于某些動態(tài)數(shù)據(jù)結(jié)構(gòu),批量執(zhí)行操作比單獨執(zhí)行每個操作更高效。

這種方法并非按順序執(zhí)行操作,而是將所有操作交錯執(zhí)行。具體做法是,每當(dāng)某個操作需要訪問內(nèi)存時,程序都會預(yù)取必要的內(nèi)存并切換到另一個操作。這確保了當(dāng)一個操作因等待內(nèi)存訪問而被阻塞時,其他內(nèi)存訪問可以并行執(zhí)行,從而降低平均訪問延遲。

Valkey8.0 預(yù)取數(shù)據(jù)應(yīng)用

Valkey 是一個鍵值對數(shù)據(jù)庫,在 Valkey 中的鍵值對是由字典(也稱為 hash 表)保存的,如下圖所示的鏈?zhǔn)焦1怼?/p>

圖片圖片

在 Valkey8.0 之前,在哈希表中查找一個 key 及對應(yīng)的 value 步驟如下描述:

  1. 計算 key 的 hash 值,找到對應(yīng)的 bucket
  2. 遍歷存儲在 bucket 中通過鏈表連接的 entry,直到找到需要的 key
  3. 如果找到 key,再訪問 key 映射的 RedisObj(也就是存儲的 value),如果存儲的 value 是OBJ_ENCODING_RAW類型,還需要進一步訪問內(nèi)存地址獲取真正的數(shù)據(jù)

每一步操作都需要等待前面的步驟完成內(nèi)存數(shù)據(jù)讀取,整個訪問過程是一個串行步驟,這種動態(tài)數(shù)據(jù)結(jié)構(gòu)會阻礙處理器推測未來可以并行執(zhí)行的內(nèi)存加載指令的能力,因此訪問內(nèi)存成為 Valkey 處理數(shù)據(jù)的性能瓶頸。

在 Valkey8.0 中,對于具有可執(zhí)行命令的客戶端(即 IO 線程已解析命令的客戶端),主線程將創(chuàng)建一個最多包含 16 條命令的批次,批量處理這些命令。并且執(zhí)行命令前,先將命令參數(shù)預(yù)取到主線程的一級緩存中,再將所有命令所需的字典條目 entry 和值 value 都從字典中預(yù)取。

同時,預(yù)取命令所需的字典條目 entry 和值 value 時遍歷字典的方式與上述查找 key 過程類似,不同的是,每個 key 每次只執(zhí)行一步,然后不等待從內(nèi)存中完成讀取數(shù)據(jù),而只是預(yù)取數(shù)據(jù),然后繼續(xù)執(zhí)行下一個 key 的下一次預(yù)取動作。這樣當(dāng)所有 key 都遍歷完成第一步后,開始執(zhí)行第二步的時候,執(zhí)行第二步所需的第一步數(shù)據(jù)已經(jīng)預(yù)取到了 L1 高速緩存。這樣通過交錯執(zhí)行所有 key,并且結(jié)合預(yù)取,達到分?jǐn)傇L問內(nèi)存的效果。

單個 key 預(yù)取流程如下所示:

圖片圖片

每批次多個 key 預(yù)取流程則是循環(huán)遍歷每個 key 交錯執(zhí)行上述步驟,先預(yù)取其中一個 key 的 bucket,然后不會執(zhí)行預(yù)取該 key 的 entry,因為此時如果接著流程預(yù)取該 key 的 entry,需要等待將該 key 的 bucket 內(nèi)存讀取出來;而是執(zhí)行下一個 key 的預(yù)取動作。也就是達成所有 key 的預(yù)取動作一直在并行執(zhí)行效果,分?jǐn)們?nèi)存訪問時間。

多個 key 批量預(yù)取流程如下所示:

圖片圖片

循環(huán)遍歷每個 key 交錯執(zhí)行上述步驟,先執(zhí)行一個 key 的預(yù)取動作,然后交錯執(zhí)行另一個 key 的預(yù)取動作,所有 key 的預(yù)取動作并行執(zhí)行,降低所有 key 訪問內(nèi)存總時間。

同一批次所有 key 和 value 都完成預(yù)取后,主線程開始批量執(zhí)行命令。相比在 Valkey8.0 之前的版本中,主線程逐個處理每個客戶端命令,批量預(yù)取數(shù)據(jù)加上批量處理,大幅提升單節(jié)點 Valkey 服務(wù)器性能,社區(qū)測試單節(jié)點 Valkey 訪問請求可以達到每秒 120W。

四、總結(jié)

本文分析了在 Valkey8.0 中通過引入異步 IO 線程、內(nèi)存預(yù)?。≒refetch)、內(nèi)存訪問分?jǐn)偅∕AA)等新特性,極大的提升了 Valkey 單節(jié)點性能,這些技術(shù)手段和算法思想也值得我們在實際業(yè)務(wù)開發(fā)中借鑒和使用。

Valkey8.0 中以上性能提升特性由亞馬遜貢獻,亞馬遜也做了一系列壓測對比,在增強 IO 多路復(fù)用的加持下,Valkey 單節(jié)點 QPS 最大可以超過 100W,壓測數(shù)據(jù)可以參考《推陳出新 – Valkey 性能測試:探索版本變遷與云托管的效能提升》(https://aws.amazon.com/cn/blogs/china/valkey-performance-testing-exploring-version-changes-and-cloud-hosting-performance-improvements/),單節(jié)點性能完全可以比肩 Redis 低版本中等規(guī)模集群了。

在 Valkey8.0 版本中,除了以上重大性能提升優(yōu)化以外,還在提升內(nèi)存利用率、加快主從復(fù)制效率、增強 resharding 過程中高可用性、實驗性支持 RDMA,以及提升集群的觀測性等方面都進行了多項優(yōu)化。我們后續(xù)再詳細介紹。

Valkey8.0 正式版發(fā)布至今時間還不算太長,經(jīng)過一段時間的驗證后,我們也會考慮將自建 Redis server 版本逐步升級到新版本,為業(yè)務(wù)提供性能更優(yōu)的緩存服務(wù)。

責(zé)任編輯:武曉燕 來源: 得物技術(shù)
相關(guān)推薦

2009-12-04 19:28:25

FreeBSD 8.0Ubuntu 9.10性能比較

2018-09-19 16:15:18

MySQL直方圖數(shù)據(jù)庫

2025-02-17 12:30:00

2025-09-18 09:56:47

2021-07-20 10:16:24

人工智能自然語言技術(shù)

2025-04-16 08:50:00

模型AI數(shù)據(jù)

2018-05-30 08:38:24

數(shù)據(jù)庫MySQL 8.0新特性

2018-05-31 12:52:01

數(shù)據(jù)庫MySQL 8.0新特性

2018-06-01 15:41:21

2017-01-05 20:00:49

大數(shù)據(jù)技術(shù)HPE Vertica

2024-12-26 00:23:46

2021-05-19 15:06:44

MySQL數(shù)據(jù)庫命令

2025-01-15 12:27:11

2020-04-13 17:17:28

MySQL8.0功能

2023-04-28 15:24:06

模型研究

2012-08-20 11:03:42

IBMdW

2017-11-01 15:50:38

數(shù)據(jù)庫MySQL 8.0新特性

2025-10-28 15:40:01

AI模型訓(xùn)練

2021-03-06 08:10:16

Redis6 Java架構(gòu)分布式框架

2025-03-10 07:00:00

阿里開源QwQ-32B
點贊
收藏

51CTO技術(shù)棧公眾號