Redis 单线程如何处理那么多的并发客户端连接?
為什么Redis是單線程的
1.官方答案
因?yàn)镽edis是基于內(nèi)存的操作,CPU不是Redis的瓶頸,Redis的瓶頸最有可能是機(jī)器內(nèi)存的大小或者網(wǎng)絡(luò)帶寬。既然單線程容易實(shí)現(xiàn),而且CPU不會(huì)成為瓶頸,那就順理成章地采用單線程的方案了。
IO多路復(fù)用技術(shù)
redis 采用網(wǎng)絡(luò)IO多路復(fù)用技術(shù)來(lái)保證在多連接的時(shí)候, 系統(tǒng)的高吞吐量。
多路-指的是多個(gè)socket連接,復(fù)用-指的是復(fù)用一個(gè)線程。多路復(fù)用主要有三種技術(shù):select,poll,epoll。epoll是最新的也是目前最好的多路復(fù)用技術(shù)。
這里“多路”指的是多個(gè)網(wǎng)絡(luò)連接,“復(fù)用”指的是復(fù)用同一個(gè)線程。采用多路
I/O
復(fù)用技術(shù)可以讓單個(gè)線程高效的處理多個(gè)連接請(qǐng)求(盡量減少網(wǎng)絡(luò)IO的時(shí)間消耗),且Redis在內(nèi)存中操作數(shù)據(jù)的速度非常快(內(nèi)存內(nèi)的操作不會(huì)成為這里的性能瓶頸),主要以上兩點(diǎn)造就了Redis具有很高的吞吐量。
epoll有諸多優(yōu)點(diǎn):
1. epoll 沒(méi)有最大并發(fā)連接的限制,上限是最大可以打開(kāi)文件的數(shù)目,這個(gè)數(shù)字一般遠(yuǎn)大于 2048,一般來(lái)說(shuō)這個(gè)數(shù)目和系統(tǒng)內(nèi)存關(guān)系很大,具體數(shù)目可以 cat /proc/sys/fs/file-max 察看。
2. 效率提升, Epoll 最大的優(yōu)點(diǎn)就在于它只管你“活躍”的連接,而跟連接總數(shù)無(wú)關(guān),因此在實(shí)際的網(wǎng)絡(luò)環(huán)境中, Epoll 的效率就會(huì)遠(yuǎn)遠(yuǎn)高于 select 和 poll 。
3. 內(nèi)存拷貝, Epoll 在這點(diǎn)上使用了“共享內(nèi)存”,這個(gè)內(nèi)存拷貝也省略了。
epoll IO多路復(fù)用模型實(shí)現(xiàn)機(jī)制
由于epoll的實(shí)現(xiàn)機(jī)制與select/poll機(jī)制完全不同,上面所說(shuō)的 select的缺點(diǎn)在epoll上不復(fù)存在。
epoll沒(méi)有這個(gè)限制,它所支持的FD上限是最大可以打開(kāi)文件的數(shù)目,這個(gè)數(shù)字一般遠(yuǎn)大于2048,舉個(gè)例子,在1GB內(nèi)存的機(jī)器上大約是10萬(wàn)左右
設(shè)想一下如下場(chǎng)景:有100萬(wàn)個(gè)客戶端同時(shí)與一個(gè)服務(wù)器進(jìn)程保持著TCP連接。而每一時(shí)刻,通常只有幾百上千個(gè)TCP連接是活躍的(事實(shí)上大部分場(chǎng)景都是這種情況)。如何實(shí)現(xiàn)這樣的高并發(fā)?
在select/poll時(shí)代,服務(wù)器進(jìn)程每次都把這100萬(wàn)個(gè)連接告訴操作系統(tǒng)(從用戶態(tài)復(fù)制句柄數(shù)據(jù)結(jié)構(gòu)到內(nèi)核態(tài)),讓操作系統(tǒng)內(nèi)核去查詢這些套接字上是否有事件發(fā)生,輪詢完后,再將句柄數(shù)據(jù)復(fù)制到用戶態(tài),讓服務(wù)器應(yīng)用程序輪詢處理已發(fā)生的網(wǎng)絡(luò)事件,這一過(guò)程資源消耗較大,因此,select/poll一般只能處理幾千的并發(fā)連接。
如果沒(méi)有I/O事件產(chǎn)生,我們的程序就會(huì)阻塞在select處。但是依然有個(gè)問(wèn)題,我們從select那里僅僅知道了,有I/O事件發(fā)生了,但卻并不知道是那幾個(gè)流(可能有一個(gè),多個(gè),甚至全部),我們只能無(wú)差別輪詢所有流,找出能讀出數(shù)據(jù),或者寫(xiě)入數(shù)據(jù)的流,對(duì)他們進(jìn)行操作。
但是使用select,我們有O(n)的無(wú)差別輪詢復(fù)雜度,同時(shí)處理的流越多,每一次無(wú)差別輪詢時(shí)間就越長(zhǎng)
epoll的設(shè)計(jì)和實(shí)現(xiàn)與select完全不同。epoll通過(guò)在Linux內(nèi)核中申請(qǐng)一個(gè)簡(jiǎn)易的文件系統(tǒng)(文件系統(tǒng)一般用什么數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)?B+樹(shù))。把原先的select/poll調(diào)用分成了3個(gè)部分:
1)調(diào)用epoll_create()建立一個(gè)epoll對(duì)象(在epoll文件系統(tǒng)中為這個(gè)句柄對(duì)象分配資源)
2)調(diào)用epoll_ctl向epoll對(duì)象中添加這100萬(wàn)個(gè)連接的套接字
3)調(diào)用epoll_wait收集發(fā)生的事件的連接
如此一來(lái),要實(shí)現(xiàn)上面說(shuō)是的場(chǎng)景,只需要在進(jìn)程啟動(dòng)時(shí)建立一個(gè)epoll對(duì)象,然后在需要的時(shí)候向這個(gè)epoll對(duì)象中添加或者刪除連接。同時(shí),epoll_wait的效率也非常高,因?yàn)檎{(diào)用epoll_wait時(shí),并沒(méi)有一股腦的向操作系統(tǒng)復(fù)制這100萬(wàn)個(gè)連接的句柄數(shù)據(jù),內(nèi)核也不需要去遍歷全部的連接。
epoll與select/poll的區(qū)別
select,poll,epoll都是IO多路復(fù)用的機(jī)制。I/O多路復(fù)用就通過(guò)一種機(jī)制,可以監(jiān)視多個(gè)描述符,一旦某個(gè)描述符就緒,能夠通知程序進(jìn)行相應(yīng)的操作。
select的本質(zhì)是采用32個(gè)整數(shù)的32位,即32*32= 1024來(lái)標(biāo)識(shí),fd值為1-1024。當(dāng)fd的值超過(guò)1024限制時(shí),就必須修改FD_SETSIZE的大小。這個(gè)時(shí)候就可以標(biāo)識(shí)32*max值范圍的fd。
poll與select不同,通過(guò)一個(gè)pollfd數(shù)組向內(nèi)核傳遞需要關(guān)注的事件,故沒(méi)有描述符個(gè)數(shù)的限制,pollfd中的events字段和revents分別用于標(biāo)示關(guān)注的事件和發(fā)生的事件,故pollfd數(shù)組只需要被初始化一次。
epoll還是poll的一種優(yōu)化,返回后不需要對(duì)所有的fd進(jìn)行遍歷,在內(nèi)核中維持了fd的列表。select和poll是將這個(gè)內(nèi)核列表維持在用戶態(tài),然后傳遞到內(nèi)核中。與poll/select不同,epoll不再是一個(gè)單獨(dú)的系統(tǒng)調(diào)用,而是由epoll_create/epoll_ctl/epoll_wait三個(gè)系統(tǒng)調(diào)用組成,后面將會(huì)看到這樣做的好處。epoll在2.6以后的內(nèi)核才支持。
select/poll的幾大缺點(diǎn):
1、每次調(diào)用select/poll,都需要把fd集合從用戶態(tài)拷貝到內(nèi)核態(tài),這個(gè)開(kāi)銷(xiāo)在fd很多時(shí)會(huì)很大
2、同時(shí)每次調(diào)用select/poll都需要在內(nèi)核遍歷傳遞進(jìn)來(lái)的所有fd,這個(gè)開(kāi)銷(xiāo)在fd很多時(shí)也很大
3、針對(duì)select支持的文件描述符數(shù)量太小了,默認(rèn)是1024
4.select返回的是含有整個(gè)句柄的數(shù)組,應(yīng)用程序需要遍歷整個(gè)數(shù)組才能發(fā)現(xiàn)哪些句柄發(fā)生了事件;
5.select的觸發(fā)方式是水平觸發(fā),應(yīng)用程序如果沒(méi)有完成對(duì)一個(gè)已經(jīng)就緒的文件描述符進(jìn)行IO操作,那么之后每次select調(diào)用還是會(huì)將這些文件描述符通知進(jìn)程。
相比select模型,poll使用鏈表保存文件描述符,因此沒(méi)有了監(jiān)視文件數(shù)量的限制,但其他三個(gè)缺點(diǎn)依然存在。
原文:https://blog.csdn.net/wxy941011/article/details/80274233
Redis高并發(fā)快總結(jié)
1. Redis是純內(nèi)存數(shù)據(jù)庫(kù),一般都是簡(jiǎn)單的存取操作,線程占用的時(shí)間很多,時(shí)間的花費(fèi)主要集中在IO上,所以讀取速度快。
2. 再說(shuō)一下IO,Redis使用的是非阻塞IO,IO多路復(fù)用,使用了單線程來(lái)輪詢描述符,將數(shù)據(jù)庫(kù)的開(kāi)、關(guān)、讀、寫(xiě)都轉(zhuǎn)換成了事件,減少了線程切換時(shí)上下文的切換和競(jìng)爭(zhēng)。
3. Redis采用了單線程的模型,保證了每個(gè)操作的原子性,也減少了線程的上下文切換和競(jìng)爭(zhēng)。
4. 另外,數(shù)據(jù)結(jié)構(gòu)也幫了不少忙,Redis全程使用hash結(jié)構(gòu),讀取速度快,還有一些特殊的數(shù)據(jù)結(jié)構(gòu),對(duì)數(shù)據(jù)存儲(chǔ)進(jìn)行了優(yōu)化,如壓縮表,對(duì)短數(shù)據(jù)進(jìn)行壓縮存儲(chǔ),再如,跳表,使用有序的數(shù)據(jù)結(jié)構(gòu)加快讀取的速度。
5. 還有一點(diǎn),Redis采用自己實(shí)現(xiàn)的事件分離器,效率比較高,內(nèi)部采用非阻塞的執(zhí)行方式,吞吐能力比較大。
套接字
TCP用主機(jī)的IP地址加上主機(jī)上的端口號(hào)作為T(mén)CP連接的端點(diǎn),這種端點(diǎn)就叫做套接字(socket)或插口。
套接字用(IP地址:端口號(hào))表示。
它是網(wǎng)絡(luò)通信過(guò)程中端點(diǎn)的抽象表示,包含進(jìn)行網(wǎng)絡(luò)通信必需的五種信息:連接使用的協(xié)議,本地主機(jī)的IP地址,本地進(jìn)程的協(xié)議端口,遠(yuǎn)地主機(jī)的IP地址,遠(yuǎn)地進(jìn)程的協(xié)議端口。
總結(jié)
以上是生活随笔為你收集整理的Redis 单线程如何处理那么多的并发客户端连接?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 计算机总体水平情况,学生信息技术起点水平
- 下一篇: 计算机安全相关的会议和期刊,中国计算机学