柚子快報(bào)邀請(qǐng)碼778899分享:緩存 Redis6 多線程模型
柚子快報(bào)邀請(qǐng)碼778899分享:緩存 Redis6 多線程模型
優(yōu)質(zhì)博文:IT-BLOG-CN
一、單線程的優(yōu)缺點(diǎn)
對(duì)于一個(gè)請(qǐng)求操作Redis主要做3件事情:從客戶端讀取數(shù)據(jù)/解析、執(zhí)行Redis命令、回寫數(shù)據(jù)給客戶端。所以主線程其實(shí)就是把所有操作的這3件事情串行一起執(zhí)行,因?yàn)槭腔趦?nèi)存,所以執(zhí)行速度非??臁?/p>
優(yōu)點(diǎn)&缺點(diǎn): 【1】?jī)?yōu)點(diǎn):不存在鎖的競(jìng)爭(zhēng)問(wèn)題和避免線程間CPU的切換。 【2】缺點(diǎn):?jiǎn)尉€程無(wú)法利用多CPU和串行操作,某個(gè)操作“出問(wèn)題”會(huì)“阻塞”后續(xù)操作。
Redis6.0之前為什么一直不使用多線程? 使用Redis時(shí),幾乎不存在CPU成為瓶頸的情況,Redis主要受限于內(nèi)存和網(wǎng)絡(luò)。例如:在一個(gè)普通的Linux系統(tǒng)上,Redis通過(guò)使用pipelining每秒可以處理100W個(gè)請(qǐng)求,如果應(yīng)該程序主要使用O(N)或O(log(N))的命令,它幾乎不會(huì)占用太多的CPU。如果要用到多核CPU,可以搭建多個(gè)Redis實(shí)例來(lái)解決。
為什么說(shuō)Redis的瓶頸不在CPU? Redis絕大部分操作是基于內(nèi)存的,而且是存KV(key-value)操作,命令執(zhí)行的速度非???。我們可以這么理解:Redis的數(shù)據(jù)存儲(chǔ)在一個(gè)大的HashMap中,而HashMap的優(yōu)勢(shì)就是查找和寫入的時(shí)間復(fù)雜度都是O(1),Redis內(nèi)部采用這種結(jié)構(gòu)存儲(chǔ)數(shù)據(jù),就奠定了Redis高性能的基礎(chǔ)。
二、Redis 的多線程
Redis基于內(nèi)存操作,內(nèi)存的響應(yīng)時(shí)長(zhǎng)大約為100納秒,單線程的Redis處理數(shù)據(jù)的極限是80,000到100,000 QPS,對(duì)于80%的公司來(lái)說(shuō),單線程的Redis已經(jīng)足夠使用了。
因?yàn)榫W(wǎng)絡(luò)I/O在Redis執(zhí)行期間占用了大部分CPU時(shí)間,所以把網(wǎng)絡(luò)I/O部分單獨(dú)抽離出來(lái),做成多線程的方式。這里所說(shuō)的多線程,其實(shí)就是將Redis單線程中做的這兩件事情“從客戶端讀取數(shù)據(jù)、回寫數(shù)據(jù)給客戶端”(也可以稱為網(wǎng)絡(luò)I/O),處理成多線程的方式,但是“執(zhí)行Redis命令”還是在主線程中串行執(zhí)行,這個(gè)邏輯保持不變。
主線程和多個(gè)I/O線程,都同時(shí)處理圖中的“隊(duì)列”,是不是會(huì)存在鎖競(jìng)爭(zhēng)的關(guān)系尼?這里有個(gè)巧妙的設(shè)計(jì),就是當(dāng)epoll獲取socket鏈接時(shí),會(huì)將該事件先全部扔進(jìn)隊(duì)列中,比如扔了N個(gè)事件,這時(shí)主線程就會(huì)處于忙碌狀態(tài)。然后多個(gè)I/O線程開(kāi)始去并行進(jìn)行網(wǎng)絡(luò)I/O,并對(duì)數(shù)據(jù)進(jìn)行協(xié)議解析,當(dāng)隊(duì)列全部處理完畢后,主線程會(huì)對(duì)隊(duì)列中請(qǐng)求串行“執(zhí)行Redis命令”,然后清空該隊(duì)列。所以整個(gè)執(zhí)行流程總結(jié)下來(lái):主線程執(zhí)行請(qǐng)求入隊(duì)列 -> I/O線程并行進(jìn)行網(wǎng)絡(luò)讀 -> 主線程串行執(zhí)行Redis命令 -> I/O線程并行進(jìn)行網(wǎng)絡(luò)寫 ->主線程清空隊(duì)列,并接收下一批請(qǐng)求。
Redis 6.0的多線程是禁用的,默認(rèn)使用是主線程。官方建議:只在機(jī)器至少有4個(gè)內(nèi)核時(shí)才啟用多線程模型,且至少留下一個(gè)備用內(nèi)核。如果需要開(kāi)啟多線程需修改redis.conf配置文件:
io-threads-do-reads yes
開(kāi)啟多線程后,還需要設(shè)置線程數(shù),否則是不生效的。同樣修改redis.conf配置文件:
io-threads 3
Redis官方建議:只在機(jī)器至少有4個(gè)內(nèi)核時(shí)才啟用多線程模型,且至少留下一個(gè)備用內(nèi)核。4核的機(jī)器建議設(shè)置為2或3個(gè)線程,8核的建議設(shè)置為6個(gè)線程,線程數(shù)一定要小于機(jī)器核數(shù)。還需要注意的是,線程數(shù)并不是越大越好,官方認(rèn)為超過(guò)了8個(gè)基本就沒(méi)什么意義了。
三、Redis6 多線程原理解析
近年來(lái)底層網(wǎng)絡(luò)硬件性能越來(lái)越好,Redis的性能瓶頸逐漸體現(xiàn)在網(wǎng)絡(luò)I/O的讀寫上,單個(gè)線程處理網(wǎng)絡(luò)I/O讀寫的速度跟不上底層網(wǎng)絡(luò)硬件執(zhí)行的速度。
Redis6.0多線程是把主線程處理網(wǎng)絡(luò)IO和協(xié)議解析這兩件事給了一組獨(dú)立的線程處理,使得多個(gè)socket讀寫可以并?化,但Redis命令還是主線程串?執(zhí)?。
主要流程如下: 【1】主線程負(fù)責(zé)接收并建立(多個(gè))連接請(qǐng)求,獲取socket后放入全局等待處理隊(duì)列; 【2】主線程處理完這些事件之后,通過(guò)RR(Round Robin輪詢)將可讀socket分配給這些IO線程; 【3】主線程阻塞,等待IO線程完成命令的讀取、解析;
繼續(xù)使?單線程執(zhí)?讀寫命令,不需要為了保證Lua腳本、事務(wù)、等開(kāi)發(fā)多線程安全機(jī)制,實(shí)現(xiàn)更簡(jiǎn)單。
【4】主線程執(zhí)?IO線程讀取和解析出來(lái)的Redis請(qǐng)求命令,并將結(jié)果寫到輸出緩沖區(qū); 【5】主線程阻塞,等待IO線程將命令執(zhí)?結(jié)果寫回socket(客戶端); 【6】主線程執(zhí)行所有命令并清空整個(gè)等待隊(duì)列,等待客戶端后續(xù)的請(qǐng)求隊(duì)列;
三、性能對(duì)比
壓測(cè)配置: Redis Server: 阿里云Ubuntu 18.04,8 CPU 2.5 GHZ, 8G內(nèi)存,主機(jī)型號(hào)ecs.ic5.2xlarge Redis Benchmark Client: 阿里云Ubuntu 18.04,8 2.5 GHZ CPU, 8G內(nèi)存,主機(jī)型號(hào)ecs.ic5.2xlarge
壓測(cè)命令: redis-benchmark -h 192.168.0.49 -a foobared -t set,get -n 1000000 -r 100000000 --threads 4 -d ${datasize} -c 256
從上面可以看到GET/SET命令在4線程IO時(shí)性能相比單線程是幾乎是翻倍了。另外,這些數(shù)據(jù)只是為了簡(jiǎn)單驗(yàn)證多線程IO是否真正帶來(lái)性能優(yōu)化,并沒(méi)有針對(duì)嚴(yán)謹(jǐn)?shù)难訒r(shí)控制和不同并發(fā)的場(chǎng)景進(jìn)行壓測(cè)。數(shù)據(jù)僅供驗(yàn)證參考而不能作為線上指標(biāo),且只是目前的unstble分支的性能,不排除后續(xù)發(fā)布的正式版本的性能會(huì)更好。
柚子快報(bào)邀請(qǐng)碼778899分享:緩存 Redis6 多線程模型
推薦鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。