国产日韩欧美一区二区三区三州_亚洲少妇熟女av_久久久久亚洲av国产精品_波多野结衣网站一区二区_亚洲欧美色片在线91_国产亚洲精品精品国产优播av_日本一区二区三区波多野结衣 _久久国产av不卡

?

高性能高可用Redis 客戶端的設(shè)計(jì)與實(shí)現(xiàn)*

2022-04-28 10:36劉世超楊斌劉衛(wèi)國
電子技術(shù)應(yīng)用 2022年1期
關(guān)鍵詞:批處理線程隊(duì)列

劉世超 ,楊斌 ,劉衛(wèi)國

(1.山東大學(xué) 軟件學(xué)院,山東 濟(jì)南 250101;2.國家超級(jí)計(jì)算無錫中心,江蘇 無錫 214072)

0 引言

隨著互聯(lián)網(wǎng)飛速發(fā)展以及大規(guī)模應(yīng)用的不斷涌現(xiàn),目前已經(jīng)步入了大數(shù)據(jù)時(shí)代。非結(jié)構(gòu)化數(shù)據(jù)逐漸替代了傳統(tǒng)結(jié)構(gòu)化數(shù)據(jù)并迅速占據(jù)了主導(dǎo)地位,為了管理形式多樣的非結(jié)構(gòu)化數(shù)據(jù),涌現(xiàn)了諸如MongoDB[1]、InfluxDB[2]、Elasticsearch[3]等十分有代表性的數(shù)據(jù)庫。這些數(shù)據(jù)庫雖然針對非結(jié)構(gòu)化數(shù)據(jù)的存取做了很多優(yōu)化,但是受限于硬盤(Hard Disk Drive,HDD)等底層存儲(chǔ)介質(zhì),往往無法滿足高性能場景的需求。

為了提高性能,以Redis 為代表的內(nèi)存數(shù)據(jù)庫應(yīng)運(yùn)而生。Redis 是一個(gè)非結(jié)構(gòu)化數(shù)據(jù)庫,支持使用非結(jié)構(gòu)化語言(Not-only Structured Query Language,NoSQL)查詢。同時(shí),Redis 通過I/O(Input/Output)多路復(fù)用和DRAM(Dynamic Random Access Memory)提供了高吞吐、高并發(fā)和低時(shí)延的服務(wù),在數(shù)據(jù)緩沖、消息隊(duì)列、Key-Value 存儲(chǔ)等場景都發(fā)揮了重要的作用。

但是隨著大規(guī)模計(jì)算集群的算力逐漸增大,應(yīng)用的數(shù)據(jù)規(guī)模也隨之變大,計(jì)算和I/O 之間的“存儲(chǔ)墻”也變得愈發(fā)明顯?,F(xiàn)有的Redis 也遇到一些網(wǎng)絡(luò)和存儲(chǔ)方面的問題。因此如何改進(jìn)Redis 也受到了廣泛的重視,隨之出現(xiàn)了很多Redis 優(yōu)化的相關(guān)工作,它們從各種角度對Redis 服務(wù)端或客戶端做了改進(jìn)。

絕大多數(shù)優(yōu)化工作側(cè)重于Redis 服務(wù)端的優(yōu)化。Wu[4]等人發(fā)現(xiàn)了Redis 的I/O 多路復(fù)用模型中的冗余監(jiān)聽問題,于是設(shè)計(jì)研發(fā)了新模型Flexpoll,它根據(jù)系統(tǒng)負(fù)載情況動(dòng)態(tài)地管理監(jiān)聽事件,在一定程度上降低了原有epoll模型的開銷。Tang[5]、Mitchell[6]、Kalia[7]以及Wang[8]等人利用IB(Infiniband)網(wǎng)絡(luò)下的遠(yuǎn)程內(nèi)存訪問(Remote Direct Memory Access,RDMA)技術(shù),對Redis 的網(wǎng)絡(luò)通信接口做了改進(jìn),大大提升了Redis 的網(wǎng)絡(luò)性能。Liu[9]等人洞察到Redis 內(nèi)存管理中的碎片問題,于是他們重新設(shè)計(jì)了一套更易對齊的內(nèi)存分配策略,同時(shí)他們基于xxHash 函數(shù)構(gòu)建了雙層hash 索引,提高了hash 命中率。Zhang[10]等人創(chuàng)新性地利用機(jī)器學(xué)習(xí),預(yù)測Redis 的鍵位分布以及rehash 周期,從而預(yù)見性地進(jìn)行內(nèi)存擴(kuò)展,有效避免了大規(guī)模rehash 帶來的系統(tǒng)癱瘓風(fēng)險(xiǎn)。同樣著眼于淘汰策略優(yōu)化的還有Hyperbolic Caching(HC)[11]和pRedis[12],它們將“未命中代價(jià)”納入置換算法的考慮范疇,大大降低了緩存未命中的開銷。

與此同時(shí),只有少部分工作側(cè)重于Redis 客戶端的優(yōu)化。其中,Chen[13]等人發(fā)現(xiàn)了Redis 集群訪問的二次鏈接開銷,通過在客戶端緩存Key-to-Node 映射表的方式,提升了近1 倍I/O 性能。

經(jīng)過調(diào)研可以看出,關(guān)于Redis 服務(wù)端的優(yōu)化工作層出不窮,而著眼于客戶端的工作卻少之又少。但是用戶往往都需要通過客戶端來與Reids 進(jìn)行交互,客戶端的性能決定了應(yīng)用最終的性能。在眾多Redis 客戶端中,Hiredis 是應(yīng)用最為廣泛的Redis 客戶端。本文以Hiredis 為例對Redis 的客戶端展開深入的分析,并結(jié)合部署于E 級(jí)超級(jí)計(jì)算機(jī)上的I/O 性能監(jiān)控與分析診斷系統(tǒng)Beacon[14]的業(yè)務(wù)情景,發(fā)現(xiàn)了Hiredis 的管道功能存在高開銷、指令存儲(chǔ)不當(dāng)以及內(nèi)存混淆問題。基于此,本文在32 邏輯核的X86 架構(gòu)處理器以及64 GB 內(nèi)存的Linux服務(wù)器上進(jìn)行研發(fā),設(shè)計(jì)并實(shí)現(xiàn)了一個(gè)面向C/C++的高性能高可用Redis 客戶端,通過內(nèi)存預(yù)分配以及內(nèi)存隔離的方法提高了處理大量指令的性能,并解決了復(fù)雜場景下的內(nèi)存混淆問題。

1 背景介紹

1.1 Redis

Redis 是一個(gè)使用C 語言實(shí)現(xiàn)的輕量級(jí)內(nèi)存數(shù)據(jù)庫,廣泛應(yīng)用于數(shù)據(jù)緩沖、消息隊(duì)列、Key-Value 存儲(chǔ)等情景,且相較于傳統(tǒng)的數(shù)據(jù)庫擁有許多優(yōu)勢。一方面,Redis 以內(nèi)存訪問代替了傳統(tǒng)磁盤訪問,提升了I/O 性能;另一方面,Redis 提供Hash 訪問的機(jī)制,降低了數(shù)據(jù)檢索的復(fù)雜度。為了保證容災(zāi)和數(shù)據(jù)安全,Redis 提供了以磁盤為輔的數(shù)據(jù)備份功能。當(dāng)數(shù)據(jù)量達(dá)到設(shè)定的內(nèi)存閾值后,Redis 會(huì)依據(jù)設(shè)置的淘汰策略進(jìn)行置換。此外,Redis 還提供了完善的集群管理功能來提高大規(guī)模集群管理的效率。用戶可根據(jù)自己的需求搭建Redis 集群,并設(shè)置主從節(jié)點(diǎn)間的數(shù)據(jù)復(fù)制策略等。

1.2 管道

Redis 是一個(gè)CS(Client/Server)模式的服務(wù),客戶端需要與服務(wù)端建立鏈接才可以發(fā)送請求。在傳統(tǒng)的CS 模式中,往往采用了阻塞式的交互流程,如圖1 所示??蛻舳嗣看伟l(fā)送請求后,需要等待請求結(jié)果返回才可以發(fā)送下一個(gè)請求。在需要執(zhí)行大量沒有依賴關(guān)系的指令的場合,這種阻塞式交互無疑是效率低下的。

圖1 傳統(tǒng)阻塞式C/S 請求流程示意圖

為了應(yīng)對指令批處理的情景,Redis 引入了管道功能,它的實(shí)現(xiàn)邏輯如圖2 所示。在管道中,Redis 首先將客戶端請求的批處理指令緩存起來,待執(zhí)行完畢后,再將結(jié)果以隊(duì)列形式統(tǒng)一返還。這種非阻塞的I/O 多路復(fù)用模型的引入,有力保障了Redis 高性能、高并發(fā)和低延遲的特性。

圖2 管道非阻塞式C/S 請求流程示意圖

1.3 Redis 客戶端

通常說的Redis 往往指Redis 服務(wù)端,提供數(shù)據(jù)存儲(chǔ)和查詢服務(wù)。而Redis 客戶端在整個(gè)服務(wù)體系中也是極其重要的一環(huán),例如:常見的Jedis、Redisson 以及Lettuce。應(yīng)用往往需要通過客戶端來向服務(wù)端請求數(shù)據(jù)的存取服務(wù)。

其中,Hiredis 是應(yīng)用廣泛的Redis 客戶端之一,為C/C++操作Redis 提供了許多便捷的接口,同時(shí)也是一些其他客戶端的下層依賴,例如:Python 中的Hiredis-py,C++中的Redis-plus-plus 等。Hiredis通過redisConnect 函數(shù)與Redis 服務(wù)端建立鏈接,之后,若需執(zhí)行某條指令,用戶只需將指令字符串作為參數(shù)傳入函數(shù)redisCommand即可,而指令的執(zhí)行結(jié)果包含于返回值redisReply 對象中。與此同時(shí),Hiredis 客戶端也支持管道功能。用戶可通過redisAppendCommand 函數(shù)將指令暫存至緩存區(qū)(obuf),最后調(diào)用redisGetReply 函數(shù)將緩存區(qū)中的指令集發(fā)送至服務(wù)端,并可獲取到首條指令的執(zhí)行結(jié)果,至于剩余指令的返回值,只需依次調(diào)用redisGetReply 獲取即可。

2 Hiredis 問題分析

由于Hiredis 的應(yīng)用較為廣泛,且功能較為完善,故本文以Hiredis 為例,進(jìn)行了深入的測試分析。研究發(fā)現(xiàn)Hiredis 在高并發(fā)和復(fù)雜場景下仍然存在明顯的性能及準(zhǔn)確性問題。

2.1 大量批處理指令執(zhí)行的場景

redisAppendCommand(context,cmd_str) 函數(shù)是Hiredis實(shí)現(xiàn)管道功能的核心函數(shù)。其中參數(shù)context 是由redis-Connect 返回的redisContext 對象,它代表著一次Redis 鏈接的上下文,其中所含的一塊連續(xù)內(nèi)存obuf 作為批處理指令的緩存池使用。這塊內(nèi)存并非用戶管理,而是由函數(shù)內(nèi)部負(fù)責(zé)分配和釋放。當(dāng)若干指令依次寫入緩存區(qū)時(shí),每條指令之間會(huì)以符合Redis 協(xié)議的特定符號(hào)分隔。

該方案問題在于,每次向緩存區(qū)追加指令時(shí),都面臨著內(nèi)存的擴(kuò)展問題。如圖3 所示,當(dāng)?shù)贜 條指令加入時(shí),則需要先分配一塊足以包含這N 條指令的內(nèi)存,再將原來的N-1 條指令拷貝到新區(qū)域,最后釋放原來的內(nèi)存,并把第N 條指令追加上去。不難看出,內(nèi)存拷貝的復(fù)雜度為O(N2)。

圖3 Hiredis 批處理指令內(nèi)存分配示意圖

在Beacon 的業(yè)務(wù)場景中,需要使用HMSET 指令向Redis 頻繁寫入文件I/O 信息,其中包含長達(dá)MB 級(jí)別的文件信息,代表特定時(shí)間內(nèi)的所有文件描述符(File Descriptor,F(xiàn)D)及其文件名。在這樣的長指令面前,Hiredis 客戶端的內(nèi)存開銷可想而知,時(shí)間復(fù)雜度亟待優(yōu)化。

2.2 批處理與即時(shí)指令混合執(zhí)行的場景

上文提到的是簡單的批處理場景,只需一次性地將若干指令提交即可。而實(shí)際應(yīng)用場景往往比較復(fù)雜,存在批處理指令中穿插使用即時(shí)指令的情況,即在批量添加指令的過程中需臨時(shí)向Redis 請求一條指令,并立即返回處理結(jié)果。

這時(shí),首先使用redisAppendCommand 添加若干指令,然后穿插使用redisCommand 提交即時(shí)指令,但后者實(shí)際上是調(diào)用了redisAppendCommand 并立即使用redisGetReply獲取返回值。如此一來,便會(huì)提前地把緩存區(qū)中的指令提交出去,并得到錯(cuò)位的返回結(jié)果,如圖4 所示。

圖4 Hiredis 批處理與即時(shí)指令內(nèi)存混淆示意圖

首先添加3 條批處理指令C1、C2、C3,之后再提交一條即時(shí)指令T。此時(shí)Hiredis 會(huì)先將指令T 追加到緩存區(qū)中,然后自動(dòng)調(diào)用redisGetReply 函數(shù),并期望獲得指令T的對應(yīng)結(jié)果Rt。但實(shí)際上,它卻在不知情的情況下將指令M1、M2、M3、Mt 一起提交出去,并返回首條指令的結(jié)果。如此一來,指令T 便誤收了指令C1 的結(jié)果R1。而剩余的指令結(jié)果R2、R3、Rt 此時(shí)仍存儲(chǔ)在返回隊(duì)列中。于是當(dāng)再追加指令C4,C5,…,Cn,最終提交并依次遍歷返回隊(duì)列時(shí),卻收到了錯(cuò)誤的R2,R3,Rt,R4,…,Rn,而不是預(yù)期的R1,R2,R3,R4,…,Rn,即T 指令之前的批處理指令所獲得的返回值全部錯(cuò)位。

2.3 多線程批處理指令執(zhí)行的場景

雖然Redis 是單線程處理模型,但在客戶端編程時(shí)往往會(huì)使用多線程的模式。在使用Hiredis 客戶端向同一個(gè)redisContext 添加指令時(shí),目前的Hiredis 無法返回各線程所期望的結(jié)果,如圖5 所示。

圖5 Hiredis 多線程批處理指令內(nèi)存混淆示意圖

可以看出,即使線程T1、T2、T3 通過加鎖解決了線程安全問題,并成功將自己的指令Tx-1、Tx-2 寫入了緩存,卻難以保證指令在緩存區(qū)中的順序。而且同理,返回值隊(duì)列也存在著同樣的順序混淆問題。如此一來,各線程的指令與其結(jié)果的一致性便得不到保證。

3 高性能高可用的Redis 客戶端設(shè)計(jì)與實(shí)現(xiàn)

為了解決上述問題,本文設(shè)計(jì)并實(shí)現(xiàn)了一個(gè)高性能高可用的Redis 客戶端,通過批處理指令的內(nèi)存預(yù)分配,大大降低了內(nèi)存拷貝的開銷;同時(shí)隔離了批處理指令與即時(shí)指令的緩存區(qū),避免了兩者的混淆;最后,也針對多線程混淆的情景,設(shè)計(jì)了合適的內(nèi)存分配與隔離策略,保證了多線程的安全性和準(zhǔn)確性。

3.1 內(nèi)存預(yù)分配策略

對于redisAppendCommand 帶來的頻繁內(nèi)存拷貝問題,采取的策略如圖6 所示。首先,將指令緩存區(qū)提取出來,允許用戶根據(jù)自己的預(yù)期在調(diào)用前預(yù)分配一塊連續(xù)的、足以裝若干條指令的內(nèi)存;其次,通過游標(biāo)(pos)去跟蹤字符串尾的位置,這樣若需后續(xù)的指令追加,只需從游標(biāo)處繼續(xù)拼接即可。

圖6 優(yōu)化后的批處理指令內(nèi)存預(yù)分配示意圖

如此一來,不僅將緩存區(qū)的遍歷復(fù)雜度降為O(N),而且有效地避免了內(nèi)存的被動(dòng)擴(kuò)展所帶來的拷貝開銷,真正實(shí)現(xiàn)了“零拷貝”。

在Beacon 中,以一分鐘的I/O 日志為一個(gè)處理批次,每批次的日志理論峰值為GB 級(jí)別,顯然,在程序中一次性分配如此多的堆內(nèi)存是不合理的,因?yàn)橄到y(tǒng)不會(huì)一直保持全機(jī)峰值狀態(tài)。于是每次分配15 MB 的緩存空間,保證其足夠容納單條指令;同時(shí)為了避免指令拼接過程中的內(nèi)存越界問題,每一次追加前都會(huì)進(jìn)行預(yù)判,判斷緩存區(qū)的剩余空間是否足夠容納本條指令。若溢出,則首先將現(xiàn)有指令集提交出去,將緩存清空且游標(biāo)回歸原點(diǎn),再繼續(xù)重復(fù)上述操作。權(quán)衡之下,只需在處理高密度日志時(shí)多提交幾次,犧牲一點(diǎn)網(wǎng)絡(luò)上的性能,便可解決內(nèi)存分配過多時(shí)開銷過大與分配過少時(shí)越界溢出之間的矛盾。

3.2 批處理與即時(shí)指令的內(nèi)存隔離策略

在批處理指令與即時(shí)指令混合使用的情景下,為了避免混淆,考慮過以下幾種解決方案。

首先,嘗試仍然使用同一塊緩存區(qū)來存儲(chǔ)批處理與即時(shí)指令,但為了同時(shí)保證前者的順序性與后者的即時(shí)性,需要使用隊(duì)列與堆棧復(fù)合的數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)。即當(dāng)面對批處理指令時(shí),仍然使用隊(duì)列的方式向隊(duì)尾添加指令,而面對即時(shí)指令時(shí),會(huì)將其插入到隊(duì)首,這樣,該即時(shí)指令便可在不影響批處理指令順序的情況下獲得自己期望的結(jié)果。但是,如果頻繁地向連續(xù)內(nèi)存的頭部插入數(shù)據(jù),勢必造成大量的內(nèi)存移動(dòng),這有違初衷。其次,嘗試使用不同的Redis 上下文對象去隔離,即可避免緩存干擾問題。但該對象是與特定Redis 鏈接一一對應(yīng)的,無形中增加一倍的網(wǎng)絡(luò)鏈接負(fù)載也并非我們所期望的。最終權(quán)衡之下,設(shè)計(jì)了如圖7 所示的策略。

圖7 優(yōu)化后的批處理與即時(shí)指令內(nèi)存隔離示意圖

本文在同一個(gè)鏈接的上下文中增加一個(gè)臨時(shí)緩存區(qū)(obuf_tmp),當(dāng)有臨時(shí)指令請求時(shí),使用新的臨時(shí)緩存區(qū)來存儲(chǔ)它們,而對于批處理指令,仍然將它們存儲(chǔ)到原本的緩存區(qū)(obuf)中去。這樣便實(shí)現(xiàn)了兩者的內(nèi)存隔離,且不會(huì)增加額外的網(wǎng)絡(luò)開銷。

3.3 多線程批處理指令的內(nèi)存隔離策略

在解決多線程的內(nèi)存混淆問題時(shí),首先考慮到的仍是使用同一塊緩存區(qū)(obuf)的情況,這時(shí)為了區(qū)分各線程的指令集,需要在每條指令中增加線程編號(hào)字段,并且一直關(guān)聯(lián)到返回的結(jié)果集中去。

這樣實(shí)現(xiàn)的問題在于,當(dāng)獲取指令執(zhí)行結(jié)果時(shí),需要以線程編號(hào)作為參考去遍歷結(jié)果隊(duì)列,直到匹配到對應(yīng)的返回值為止。例如有T 個(gè)線程,每個(gè)線程各添加N 條指令,這時(shí)緩存區(qū)的大小為T×N 條指令的大小,返回隊(duì)列中共有T×N 條記錄。當(dāng)X 線程期望獲得自己的執(zhí)行結(jié)果時(shí),需要遍歷這T×N 條記錄,直到匹配到線程編號(hào)字段為X 停止。不難看出,在最壞的情況下某線程的對應(yīng)結(jié)果集沉底,那么它每次調(diào)用獲取結(jié)果的函數(shù),都需要進(jìn)行(T-1)×N次遍歷,最終平均復(fù)雜度為O(T×N)。所以,這樣設(shè)計(jì)反而增加了開銷,故舍棄。再者,使用多個(gè)鏈接上下文來避免緩存區(qū)混淆的策略也行不通,同理于前文,此方案會(huì)增加T 倍的Redis 鏈接開銷。

經(jīng)過以上分析,本文決定仍然在同一個(gè)Redis 鏈接上下文中做改進(jìn),策略如圖8 所示。新設(shè)計(jì)的緩存區(qū)是一個(gè)字符串的一維數(shù)組,下標(biāo)索引與線程的相對編號(hào)對應(yīng),數(shù)組中每個(gè)元素存儲(chǔ)著對應(yīng)線程的指令隊(duì)列。

圖8 優(yōu)化后的多線程批處理指令內(nèi)存隔離示意圖

當(dāng)某線程在提交批處理指令時(shí),首先以自己的線程相對編號(hào)為索引,找到自己的緩存區(qū)下標(biāo)位置,經(jīng)過二次尋址后,再向其中追加對應(yīng)的指令。同理返回結(jié)果的隊(duì)列也相應(yīng)增加一維,通過線程編號(hào)索引自己的結(jié)果集起始地址,再從中依次獲取首條指令的結(jié)果。如此一來,在不增加額外網(wǎng)絡(luò)開銷的情況下,便實(shí)現(xiàn)了各線程緩存區(qū)之間的安全隔離。

4 高性能高可用Redis 客戶端測試分析

本文的測試平臺(tái)是Linux 服務(wù)器,操作系統(tǒng)為CentOS 7.7,X86_64核心,內(nèi)存為64GB。同時(shí),使用V4.8.5 的GCC 編譯器以及fPIC 和shared 選項(xiàng),對Redis 客戶端進(jìn)行動(dòng)態(tài)庫編譯。最后,Redis 服務(wù)端的IP 綁定為本地(127.0.0.1),端口號(hào)為5555,設(shè)置Redis 的最大內(nèi)存容量為1 GB。

測試工作主要分兩個(gè)部分,第一是性能測試,目的是驗(yàn)證內(nèi)存預(yù)分配策略帶來的執(zhí)行效率提升;第二是準(zhǔn)確性測試,驗(yàn)證內(nèi)存隔離策略的有效性。

4.1 性能測試

本測試的主要目的是用來驗(yàn)證內(nèi)存預(yù)分配策略所帶來的性能提升,圖9 展示了測試結(jié)果。測試流程如下,首先初始化一個(gè)Redis 服務(wù),然后分別使用Hiredis 客戶端與本文設(shè)計(jì)的高性能高可用Redis 客戶端進(jìn)行批處理指令請求。實(shí)驗(yàn)總共分為4 組,每組均傳入10 條大小相等的指令,各組的指令大小分別為100 KB、1 MB、10 MB、100 MB,同時(shí)進(jìn)行毫秒級(jí)別的計(jì)時(shí)。每組實(shí)驗(yàn)均重復(fù)5次,去掉最高最低值后計(jì)算平均值,即得到最終的實(shí)驗(yàn)結(jié)果。

圖9 中的橫軸表示測試指令的大小,縱軸表示執(zhí)行時(shí)間(毫秒),深、淺兩條柱子分別代表優(yōu)化前、后的性能??梢钥闯觯瑑?yōu)化前的時(shí)耗隨著指令字節(jié)數(shù)的增大,基本呈指數(shù)爆炸式增長,當(dāng)指令為100 MB 時(shí)其時(shí)耗高達(dá)16 s;相對的,優(yōu)化后的時(shí)間呈線性增長態(tài)勢,隨著指令大小以10 倍速度擴(kuò)增,其執(zhí)行時(shí)間分別按9 倍、7 倍和5 倍的速度增大,時(shí)耗增加的速度逐漸變緩,與優(yōu)化前的性能表現(xiàn)出了強(qiáng)烈的對比。當(dāng)指令達(dá)100 MB 時(shí),優(yōu)化后的策略只需2.5 s,相對于優(yōu)化前的16 s 有了高達(dá)6.4倍的提升。

圖9 優(yōu)化前后的批處理指令性能比較

本文并沒有繼續(xù)測試更高規(guī)模的指令,一方面,考慮到Hiredis 的開銷已經(jīng)不足以運(yùn)行更大規(guī)模的用例;另一方面,100 MB 的單條指令大小已經(jīng)足夠覆蓋絕大多數(shù)的應(yīng)用場景。但通過以上的分析可以看出,在面向大量指令時(shí),本文的Redis 客戶端可以為系統(tǒng)性能帶來質(zhì)的飛躍。

4.2 準(zhǔn)確性測試

本測試的主要目的是驗(yàn)證內(nèi)存隔離的準(zhǔn)確性。測試前,首先在Redis 中寫入6 條數(shù)據(jù),如表1 所示。

表1 測試前Redis 中Key-Value 數(shù)據(jù)信息

為驗(yàn)證批處理與即時(shí)指令的內(nèi)存隔離策略,依次添加批處理指令Get K1、Get K2、Get K3、Get K4,并在其中穿插兩條即時(shí)指令Get T1、Get T2,最后連續(xù)4 次調(diào)用redisGetReply,觀察指令結(jié)果的返回情況。

表2 展示了該情景下隔離前、后的結(jié)果,前兩列表示指令及其提交方式,后兩列分別表示隔離前、后的返回值??梢钥闯觯瑑?nèi)存隔離前的返回結(jié)果是異?;靵y的,而在隔離后,結(jié)果隊(duì)列Kv1、Kv2、Kv3、Kv4 與批處理指令Get K1、Get K2、Get K3、Get K4的順序完全一致,且兩條即時(shí)指令Get T1、Get T2 也如期獲得了對應(yīng)的結(jié)果Tv1、Tv2。

表2 優(yōu)化前后批處理與即時(shí)指令混用返回值情況

如表3 所示,為驗(yàn)證多線程批處理提交指令時(shí)的內(nèi)存隔離策略,在測試用例中使用了3 個(gè)線程,共同向同一次的鏈接上下文中提交批處理指令。每個(gè)線程提交兩條,并期望獲得與之對應(yīng)的返回結(jié)果。

從表3 中不難看出,緩存隔離前的返回值異?;靵y:線程0 的Get T1、Get T2 指令得到的結(jié)果是Kv1、Tv2;線程1 的Get K1、Get K2 得到的結(jié)果是Kv3、Kv4;而線程2 的Get K3、Get K4 卻得 到了Kv2、Tv1的返回值。這其中只有線程0 的Get T2 指令收到了正確的反饋,且每次實(shí)驗(yàn)結(jié)果幾乎各不相同,這是由于指令和結(jié)果集的內(nèi)存混淆所致。當(dāng)使用本文設(shè)計(jì)的Redis 客戶端進(jìn)行測試時(shí),線程0、1、2分別得到了Tv1、Tv2、Kv1、Kv2、Kv3、Kv4 的返回值,與各自的指令提交順序完全一致。

表3 優(yōu)化前后的多線程批處理指令返回值情況

5 結(jié)論

以Redis 為代表的內(nèi)存數(shù)據(jù)庫由于高性能、低延遲、非結(jié)構(gòu)化存儲(chǔ)等特性,已成為當(dāng)今大數(shù)據(jù)時(shí)代的寵兒。如何用好這些數(shù)據(jù)庫也成為了大家關(guān)注的重點(diǎn)。本文以Redis 客戶端中應(yīng)用最為廣泛的Hiredis 為例開展了深入分析,發(fā)現(xiàn)其在高并發(fā)高性能應(yīng)用中存在一些問題,包括:面向大型指令時(shí)的高內(nèi)存開銷問題,在復(fù)雜情景下的內(nèi)存混淆問題等。為了解決這些問題,本文設(shè)計(jì)并實(shí)現(xiàn)了一個(gè)高性能高可用的Redis 客戶端,并且借助于真實(shí)的應(yīng)用場景做了深入驗(yàn)證。

雖然本文的工作主要以Beacon 為例展開,但是也具有通用性,同樣也可推廣到其他使用Redis 的業(yè)務(wù)中去,例如常見的日志處理系統(tǒng)、電商交易平臺(tái)以及社交文娛業(yè)務(wù)等??傊诿嫦虼髷?shù)據(jù)的高性能處理情景時(shí),本文的工作可保證系統(tǒng)的高性能、高并發(fā)、低開銷。下一步計(jì)劃將該客戶端應(yīng)用到其他基于Redis 服務(wù)的高性能系統(tǒng)中去。

猜你喜歡
批處理線程隊(duì)列
實(shí)時(shí)操作系統(tǒng)mbedOS 互斥量調(diào)度機(jī)制剖析
基于C#線程實(shí)驗(yàn)探究
惡意批處理文件導(dǎo)致電腦黑屏、反復(fù)重啟、無響應(yīng)的原因分析及應(yīng)對思路
隊(duì)列隊(duì)形體育教案
隊(duì)列里的小秘密
基于多隊(duì)列切換的SDN擁塞控制*
基于國產(chǎn)化環(huán)境的線程池模型研究與實(shí)現(xiàn)
不裝軟件批處理為文件夾加鎖
在隊(duì)列里
借助批處理 讓Cortana變聰明
乐昌市| 江阴市| 长沙县| 岑溪市| 乾安县| 亚东县| 奉化市| 宜宾市| 唐山市| 缙云县| 西华县| 毕节市| 盐津县| 芒康县| 萨迦县| 班玛县| 宁安市| 库伦旗| 赣榆县| 满洲里市| 鄂托克旗| 木里| 罗江县| 长垣县| 天祝| 内江市| 长治县| 安顺市| 哈尔滨市| 澎湖县| 美姑县| 南和县| 常德市| 马边| 六安市| 南靖县| 兴宁市| 嘉黎县| 大关县| 潮安县| 三门峡市|