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

Redis6.0為何引入多線(xiàn)程?單線(xiàn)程不香嗎?

開(kāi)發(fā) Redis
本文主要分兩部分。首先我們先聊一下Redis6.0之前為什么采用單線(xiàn)程模型。然后再詳細(xì)解釋Redis6.0的多線(xiàn)程。

[[343461]]

 Redis6.0之前為什么采用單線(xiàn)程模型

嚴(yán)格地說(shuō),從Redis 4.0之后并不是單線(xiàn)程。除了主線(xiàn)程外,還有一些后臺(tái)線(xiàn)程處理一些較為緩慢的操作,例如無(wú)用連接的釋放、大 key 的刪除等等。

單線(xiàn)程模型,為何性能那么高?

Redis作者從設(shè)計(jì)之初,進(jìn)行了多方面的考慮。最終選擇使用單線(xiàn)程模型來(lái)處理命令。之所以選擇單線(xiàn)程模型,主要有如下幾個(gè)重要原因:

Redis操作基于內(nèi)存,絕大多數(shù)操作的性能瓶頸不在CPU
單線(xiàn)程模型,避免了線(xiàn)程間切換帶來(lái)的性能開(kāi)銷(xiāo)
使用單線(xiàn)程模型也能并發(fā)的處理客戶(hù)端的請(qǐng)求(多路復(fù)用I/O)
使用單線(xiàn)程模型,可維護(hù)性更高,開(kāi)發(fā),調(diào)試和維護(hù)的成本更低
上述第三個(gè)原因是Redis最終采用單線(xiàn)程模型的決定性因素,其他的兩個(gè)原因都是使用單線(xiàn)程模型額外帶來(lái)的好處,在這里我們會(huì)按順序介紹上述的幾個(gè)原因。

性能瓶頸不在CPU

下圖是Redis官網(wǎng)對(duì)單線(xiàn)程模型的說(shuō)明。大概意思是:Redis的瓶頸并不在CPU,它的主要瓶頸在于內(nèi)存和網(wǎng)絡(luò)。在Linux環(huán)境中,Redis每秒甚至可以提交100萬(wàn)次請(qǐng)求

為什么說(shuō)Redis的瓶頸不在CPU?

首先,Redis絕大部分操作是基于內(nèi)存的,而且是純kv(key-value)操作,所以命令執(zhí)行速度非??臁N覀兛梢源蟾爬斫獬?,redis中的數(shù)據(jù)存儲(chǔ)在一張大HashMap中,HashMap的優(yōu)勢(shì)就是查找和寫(xiě)入的時(shí)間復(fù)雜度都是O(1)。Redis內(nèi)部采用這種結(jié)構(gòu)存儲(chǔ)數(shù)據(jù),就奠定了Redis高性能的基礎(chǔ)。根據(jù)Redis官網(wǎng)描述,在理想情況下Redis每秒可以提交一百萬(wàn)次請(qǐng)求,每次請(qǐng)求提交所需的時(shí)間在納秒的時(shí)間量級(jí)。既然每次的Redis操作都這么快,單線(xiàn)程就可以完全搞定了,那還何必要用多線(xiàn)程呢!

線(xiàn)程上下文切換問(wèn)題

另外,多線(xiàn)程場(chǎng)景下會(huì)發(fā)生線(xiàn)程上下文切換。線(xiàn)程是由CPU調(diào)度的,CPU的一個(gè)核在一個(gè)時(shí)間片內(nèi)只能同時(shí)執(zhí)行一個(gè)線(xiàn)程,在CPU由線(xiàn)程A切換到線(xiàn)程B的過(guò)程中會(huì)發(fā)生一系列的操作,主要過(guò)程包括保存線(xiàn)程A的執(zhí)行現(xiàn)場(chǎng),然后載入線(xiàn)程B的執(zhí)行現(xiàn)場(chǎng),這個(gè)過(guò)程就是“線(xiàn)程上下文切換”。其中涉及線(xiàn)程相關(guān)指令的保存和恢復(fù)。

頻繁的線(xiàn)程上下文切換可能會(huì)導(dǎo)致性能急劇下降,這會(huì)導(dǎo)致我們不僅沒(méi)有提升處理請(qǐng)求的速度,反而降低了性能,這也是 Redis 對(duì)于多線(xiàn)程技術(shù)持謹(jǐn)慎態(tài)度的原因之一。

在Linux系統(tǒng)中可以使用vmstat命令來(lái)查看上下文切換的次數(shù),下面是vmstat查看上下文切換次數(shù)的示例:

vmstat 1 表示每秒統(tǒng)計(jì)一次, 其中cs列就是指上下文切換的數(shù)目. 一般情況下, 空閑系統(tǒng)的上下文切換每秒在1500以下。

并行處理客戶(hù)端的請(qǐng)求(I/O多路復(fù)用)

如上所述:Redis的瓶頸并不在CPU,它的主要瓶頸在于內(nèi)存和網(wǎng)絡(luò)。所謂內(nèi)存瓶頸很好理解,Redis做為緩存使用時(shí)很多場(chǎng)景需要緩存大量數(shù)據(jù),所以需要大量?jī)?nèi)存空間,這可以通過(guò)集群分片去解決,例如Redis自身的無(wú)中心集群分片方案以及Codis這種基于代理的集群分片方案。

對(duì)于網(wǎng)絡(luò)瓶頸,Redis在網(wǎng)絡(luò)I/O模型上采用了多路復(fù)用技術(shù),來(lái)減少網(wǎng)絡(luò)瓶頸帶來(lái)的影響。很多場(chǎng)景中使用單線(xiàn)程模型并不意味著程序不能并發(fā)的處理任務(wù)。Redis 雖然使用單線(xiàn)程模型處理用戶(hù)的請(qǐng)求,但是它卻使用 I/O 多路復(fù)用技術(shù)“并行”處理來(lái)自客戶(hù)端的多個(gè)連接,同時(shí)等待多個(gè)連接發(fā)送的請(qǐng)求。使用 I/O多路復(fù)用技術(shù)能極大地減少系統(tǒng)的開(kāi)銷(xiāo),系統(tǒng)不再需要為每個(gè)連接創(chuàng)建專(zhuān)門(mén)的監(jiān)聽(tīng)線(xiàn)程,避免了由于大量的線(xiàn)程創(chuàng)建帶來(lái)的巨大性能開(kāi)銷(xiāo)。

下面我們?cè)敿?xì)解釋一下多路復(fù)用I/O模型。為了能更充分理解,我們先了解幾個(gè)基本概念。

Socket(套接字):Socket可以理解成,在兩個(gè)應(yīng)用程序進(jìn)行網(wǎng)絡(luò)通信時(shí),分別在兩個(gè)應(yīng)用程序中的通信端點(diǎn)。通信時(shí),一個(gè)應(yīng)用程序?qū)?shù)據(jù)寫(xiě)入Socket,然后通過(guò)網(wǎng)卡把數(shù)據(jù)發(fā)送到另外一個(gè)應(yīng)用程序的Socket中。我們平常所說(shuō)的HTTP和TCP協(xié)議的遠(yuǎn)程通信,底層都是基于Socket實(shí)現(xiàn)的。5種網(wǎng)絡(luò)IO模型也都要基于Socket實(shí)現(xiàn)網(wǎng)絡(luò)通信。

阻塞與非阻塞:所謂阻塞,就是發(fā)出一個(gè)請(qǐng)求不能立刻返回響應(yīng),要等所有的邏輯全處理完才能返回響應(yīng)。非阻塞反之,發(fā)出一個(gè)請(qǐng)求立刻返回應(yīng)答,不用等處理完所有邏輯。

內(nèi)核空間與用戶(hù)空間:在Linux中,應(yīng)用程序穩(wěn)定性遠(yuǎn)遠(yuǎn)比不上操作系統(tǒng)程序,為了保證操作系統(tǒng)的穩(wěn)定性,Linux區(qū)分了內(nèi)核空間和用戶(hù)空間??梢赃@樣理解,內(nèi)核空間運(yùn)行操作系統(tǒng)程序和驅(qū)動(dòng)程序,用戶(hù)空間運(yùn)行應(yīng)用程序。Linux以這種方式隔離了操作系統(tǒng)程序和應(yīng)用程序,避免了應(yīng)用程序影響到操作系統(tǒng)自身的穩(wěn)定性。這也是Linux系統(tǒng)超級(jí)穩(wěn)定的主要原因。所有的系統(tǒng)資源操作都在內(nèi)核空間進(jìn)行,比如讀寫(xiě)磁盤(pán)文件,內(nèi)存分配和回收,網(wǎng)絡(luò)接口調(diào)用等。所以在一次網(wǎng)絡(luò)IO讀取過(guò)程中,數(shù)據(jù)并不是直接從網(wǎng)卡讀取到用戶(hù)空間中的應(yīng)用程序緩沖區(qū),而是先從網(wǎng)卡拷貝到內(nèi)核空間緩沖區(qū),然后再?gòu)膬?nèi)核拷貝到用戶(hù)空間中的應(yīng)用程序緩沖區(qū)。對(duì)于網(wǎng)絡(luò)IO寫(xiě)入過(guò)程,過(guò)程則相反,先將數(shù)據(jù)從用戶(hù)空間中的應(yīng)用程序緩沖區(qū)拷貝到內(nèi)核緩沖區(qū),再?gòu)膬?nèi)核緩沖區(qū)把數(shù)據(jù)通過(guò)網(wǎng)卡發(fā)送出去。

多路復(fù)用I/O模型,建立在多路事件分離函數(shù)select,poll,epoll之上。以Redis采用的epoll為例,在發(fā)起read請(qǐng)求前,先更新epoll的socket監(jiān)控列表,然后等待epoll函數(shù)返回(此過(guò)程是阻塞的,所以說(shuō)多路復(fù)用IO本質(zhì)上也是阻塞IO模型)。當(dāng)某個(gè)socket有數(shù)據(jù)到達(dá)時(shí),epoll函數(shù)返回。此時(shí)用戶(hù)線(xiàn)程才正式發(fā)起read請(qǐng)求,讀取并處理數(shù)據(jù)。這種模式用一個(gè)專(zhuān)門(mén)的監(jiān)視線(xiàn)程去檢查多個(gè)socket,如果某個(gè)socket有數(shù)據(jù)到達(dá)就交給工作線(xiàn)程處理。由于等待Socket數(shù)據(jù)到達(dá)過(guò)程非常耗時(shí),所以這種方式解決了阻塞IO模型一個(gè)Socket連接就需要一個(gè)線(xiàn)程的問(wèn)題,也不存在非阻塞IO模型忙輪詢(xún)帶來(lái)的CPU性能損耗的問(wèn)題。多路復(fù)用IO模型的實(shí)際應(yīng)用場(chǎng)景很多,大家耳熟能詳?shù)腞edis,Java NIO,以及Dubbo采用的通信框架Netty都采用了這種模型。

下圖是基于epoll函數(shù)Socket編程的詳細(xì)流程。

可維護(hù)性

我們知道,多線(xiàn)程可以充分利用多核CPU,在高并發(fā)場(chǎng)景下,能夠減少因I/O等待帶來(lái)的CPU損耗,帶來(lái)很好的性能表現(xiàn)。不過(guò)多線(xiàn)程卻是一把雙刃劍,帶來(lái)好處的同時(shí),還會(huì)帶來(lái)代碼維護(hù)困難,線(xiàn)上問(wèn)題難于定位和調(diào)試,死鎖等問(wèn)題。多線(xiàn)程模型中代碼的執(zhí)行過(guò)程不再是串行的,多個(gè)線(xiàn)程同時(shí)訪(fǎng)問(wèn)的共享變量如果處理不當(dāng)也會(huì)帶來(lái)詭異的問(wèn)題。

我們通過(guò)一個(gè)例子,看一下多線(xiàn)程場(chǎng)景下發(fā)生的詭異現(xiàn)象??聪旅娴拇a:

flag為true時(shí),cal() 方法返回值是多少?很多人會(huì)說(shuō):這還用問(wèn)嗎!肯定返回2

結(jié)果可能會(huì)讓你大吃一驚!上面的這段代碼,由于語(yǔ)句1和語(yǔ)句2沒(méi)有數(shù)據(jù)依賴(lài)性,可能會(huì)發(fā)生指令重排序,有可能編譯器會(huì)把flag=true放到num=1的前面。此時(shí)set和cal方法分別在不同線(xiàn)程中執(zhí)行,沒(méi)有先后關(guān)系。cal方法,只要flag為true,就會(huì)進(jìn)入if的代碼塊執(zhí)行相加的操作??赡艿捻樞蚴牵?/p>

語(yǔ)句1先于語(yǔ)句2執(zhí)行,這時(shí)的執(zhí)行順序可能是:語(yǔ)句1->語(yǔ)句2->語(yǔ)句3->語(yǔ)句4。執(zhí)行語(yǔ)句4前,num = 1,所以cal的返回值是2
語(yǔ)句2先于語(yǔ)句1執(zhí)行,這時(shí)的執(zhí)行順序可能是:語(yǔ)句2->語(yǔ)句3->語(yǔ)句4->語(yǔ)句1。執(zhí)行語(yǔ)句4前,num = 0,所以cal的返回值是0
我們可以看到,在多線(xiàn)程環(huán)境下如果發(fā)生了指令重排序,會(huì)對(duì)結(jié)果造成嚴(yán)重影響。

當(dāng)然可以在第三行處,給flag加上關(guān)鍵字volatile來(lái)避免指令重排。即在flag處加上了內(nèi)存柵欄,來(lái)阻隔flag(柵欄)前后的代碼的重排序。當(dāng)然多線(xiàn)程還會(huì)帶來(lái)可見(jiàn)性問(wèn)題,死鎖問(wèn)題以及共享資源安全等問(wèn)題。

  1. boolean volatile flag = false

Redis6.0為何引入多線(xiàn)程?

Redis6.0引入的多線(xiàn)程部分,實(shí)際上只是用來(lái)處理網(wǎng)絡(luò)數(shù)據(jù)的讀寫(xiě)和協(xié)議解析,執(zhí)行命令仍然是單一工作線(xiàn)程。

Redis6.0為何引入多線(xiàn)程?單線(xiàn)程不香嗎?

從上圖我們可以看到Redis在處理網(wǎng)絡(luò)數(shù)據(jù)時(shí),調(diào)用epoll的過(guò)程是阻塞的,也就是說(shuō)這個(gè)過(guò)程會(huì)阻塞線(xiàn)程,如果并發(fā)量很高,達(dá)到幾萬(wàn)的QPS,此處可能會(huì)成為瓶頸。一般我們遇到此類(lèi)網(wǎng)絡(luò)IO瓶頸的問(wèn)題,可以增加線(xiàn)程數(shù)來(lái)解決。開(kāi)啟多線(xiàn)程除了可以減少由于網(wǎng)絡(luò)I/O等待造成的影響,還可以充分利用CPU的多核優(yōu)勢(shì)。Redis6.0也不例外,在此處增加了多線(xiàn)程來(lái)處理網(wǎng)絡(luò)數(shù)據(jù),以此來(lái)提高Redis的吞吐量。當(dāng)然相關(guān)的命令處理還是單線(xiàn)程運(yùn)行,不存在多線(xiàn)程下并發(fā)訪(fǎng)問(wèn)帶來(lái)的種種問(wèn)題。

性能對(duì)比
壓測(cè)配置:

Redis Server: 阿里云 Ubuntu 18.04,8 CPU 2.5 GHZ, 8G 內(nèi)存,主機(jī)型號(hào) ecs.ic5.2xlargeRedis Benchmark Client: 阿里云 Ubuntu 18.04,8 2.5 GHZ CPU, 8G 內(nèi)存,主機(jī)型號(hào) ecs.ic5.2xlarge

多線(xiàn)程版本Redis 6.0,單線(xiàn)程版本是 Redis 5.0.5。多線(xiàn)程版本需要新增以下配置:

io-threads 4 # 開(kāi)啟 4 個(gè) IO 線(xiàn)程io-threads-do-reads yes # 請(qǐng)求解析也是用 IO 線(xiàn)程

壓測(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 命令在多線(xiàn)程版本中性能相比單線(xiàn)程幾乎翻了一倍。另外,這些數(shù)據(jù)只是為了簡(jiǎn)單驗(yàn)證多線(xiàn)程 I/O 是否真正帶來(lái)性能優(yōu)化,并沒(méi)有針對(duì)具體的場(chǎng)景進(jìn)行壓測(cè),數(shù)據(jù)僅供參考。本次性能測(cè)試基于 unstble 分支,不排除后續(xù)發(fā)布的正式版本的性能會(huì)更好。

最后

可見(jiàn)單線(xiàn)程有單線(xiàn)程的好處,多線(xiàn)程有多線(xiàn)程的優(yōu)勢(shì),只有充分理解其中的本質(zhì)原理,才能靈活運(yùn)用于生產(chǎn)實(shí)踐當(dāng)中。

 

 

責(zé)任編輯:姜華 來(lái)源: 今日頭條
相關(guān)推薦

2023-12-01 08:18:24

Redis網(wǎng)絡(luò)

2020-11-17 10:20:53

Redis多線(xiàn)程單線(xiàn)程

2019-11-25 10:13:52

Redis單線(xiàn)程I

2024-09-27 11:51:33

Redis多線(xiàn)程單線(xiàn)程

2020-11-09 09:33:37

多線(xiàn)程

2021-06-15 09:20:08

Redis數(shù)據(jù)類(lèi)型

2025-06-17 00:22:00

2023-03-21 08:02:36

Redis6.0IO多線(xiàn)程

2024-02-26 00:00:00

JavaScript單線(xiàn)程高效

2023-08-17 14:12:17

2019-10-29 20:13:43

Java技術(shù)程序員

2022-01-04 11:11:32

Redis單線(xiàn)程Reactor

2021-06-11 11:28:22

多線(xiàn)程fork單線(xiàn)程

2009-07-10 09:05:20

SwingWorker

2025-04-24 08:15:00

Redis單線(xiàn)程線(xiàn)程

2021-01-28 11:17:49

Python爬蟲(chóng)單線(xiàn)程

2021-03-15 09:40:59

Redis單線(xiàn)程效率

2025-01-17 08:23:33

2019-06-17 14:20:51

Redis數(shù)據(jù)庫(kù)Java

2022-07-18 13:59:43

Redis單線(xiàn)程進(jìn)程
點(diǎn)贊
收藏

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