Redis --- 概述淀歇、線程模型與基本使用

什么是redis潘懊?

redis簡介(Remote dictionary Server 遠(yuǎn)程字典服務(wù)器):

Redis是開源免費(fèi)的姚糊,由C語言編寫的,數(shù)據(jù)存放在內(nèi)存授舟,并支持持久化的Nosql數(shù)據(jù)庫救恨,是一種內(nèi)存數(shù)據(jù)庫,所以它讀寫速度非呈褪鳎快肠槽,主要應(yīng)用在數(shù)據(jù)緩存方向。Redis 也經(jīng)常用來做分布式鎖奢啥,甚至是消息隊列秸仙。

Redis 提供了多種數(shù)據(jù)類型來支持不同的業(yè)務(wù)場景。Redis 還支持事務(wù) 桩盲、持久化寂纪、Lua 腳本、多種集群方案赌结。

ps:緩存數(shù)據(jù)處理流程

大致流程:

  • 命中緩存就返回
  • 緩存沒命中捞蛋,去數(shù)據(jù)庫檢查數(shù)據(jù)是否存在
  • 數(shù)據(jù)庫存在的話,更新緩存柬姚,返回數(shù)據(jù)
  • 數(shù)據(jù)庫不存在拟杉,返回空數(shù)據(jù)

redis具備的基本特征:

  • 支持多數(shù)據(jù)類型
  • 持久化機(jī)制
  • 主從同步

redis關(guān)于key-value的三大特點(diǎn):

  1. redis是支持持久化的!我們可以控制數(shù)據(jù)何時量承、用何種方式保存到磁盤中搬设,每次重啟再次加載該文件穴店,可以完成數(shù)據(jù)恢復(fù)。
  2. redis不單單支持簡單的key-value數(shù)據(jù)類型焕梅,同時還可以提供list迹鹅、set、hash贞言、zset等數(shù)據(jù)類型的存儲
  3. 支持?jǐn)?shù)據(jù)的備份斜棚。mater-slave模式,即,當(dāng)下時髦的主從復(fù)制讀寫分離该窗。

應(yīng)用場景:

  • 內(nèi)存存儲的持久化: 雖然redis是單線程實現(xiàn)的弟蚀,但是支持異步將數(shù)據(jù)持久化到硬盤上,同時不影響繼續(xù)服務(wù)酗失。
  • 發(fā)布訂閱:Pub/Sub 從字面上理解就是發(fā)布(Publish)與訂閱(Subscribe)义钉,在Redis中,你可以設(shè)定對某一個key值進(jìn)行消息發(fā)布及消息訂閱规肴,當(dāng)一個key值上進(jìn)行了消息發(fā)布后捶闸,所有訂閱它的客戶端都會收到相應(yīng)的消息。
  • 定時器拖刃、計時器: 我們可以把這個特性用到發(fā)短信的服務(wù)中删壮,每次發(fā)送完短信后,就進(jìn)入倒計時兑牡。在指定的時間內(nèi)拒絕發(fā)送第二次央碟,可以有效的緩解短信服務(wù)的壓力,節(jié)流均函。
  • 取出最新的N個數(shù)據(jù)的操作: 比如新浪微博的評論系統(tǒng)亿虽,他要展示最新的10條評論,就是把最新的10條評論的id放到redis的list里面苞也。

redis為什么這么快洛勉?

  • 完全基于內(nèi)存,絕大部分請求是純粹的內(nèi)存操作墩朦,非撑魅希快速;
  • 數(shù)據(jù)結(jié)構(gòu)簡單氓涣,對數(shù)據(jù)操作也簡單,比如SDS陋气;
  • 采用單線程劳吠,避免了不必要的上下文切換和競爭條件,也不存在多進(jìn)程或者多線程導(dǎo)致的切換而消耗 CPU巩趁,不用去考慮各種鎖的問題痒玩,不存在加鎖釋放鎖操作淳附,沒有因為可能出現(xiàn)死鎖而導(dǎo)致的性能消耗;
  • 使用多路 I/O 復(fù)用模型蠢古,非阻塞 IO奴曙。

分布式緩存常見的技術(shù)選型? Memcache 與 Redis區(qū)別
分布式緩存的話草讶,使用的比較多的主要是 Memcached 和 Redis洽糟。不過,現(xiàn)在基本沒有看過還有項目使用 Memcached 來做緩存堕战,都是直接用 Redis坤溃。

Memcached 是分布式緩存最開始興起的那會,比較常用的嘱丢。后來薪介,隨著 Redis 的發(fā)展,大家慢慢都轉(zhuǎn)而使用更加強(qiáng)大的 Redis 了越驻。

分布式緩存主要解決的是單機(jī)緩存的容量受服務(wù)器限制并且無法保存通用的信息汁政。因為,本地緩存只在當(dāng)前服務(wù)里有效缀旁,比如如果你部署了兩個相同的服務(wù)记劈,他們兩者之間的緩存數(shù)據(jù)是無法共同的。

共同點(diǎn):

  • 都是基于內(nèi)存的數(shù)據(jù)庫诵棵,一般都用來當(dāng)做緩存使用抠蚣。
    都有過期策略。
    兩者的性能都非常高履澳。

區(qū)別:

  • 存儲方式不同(持久化): Memcache 把數(shù)據(jù)全部存在內(nèi)存之中嘶窄,斷電后會丟失,滿了會報異常距贷。Redis 所有數(shù)據(jù)加載在內(nèi)存柄冲,但也會持久化到磁盤,保證數(shù)據(jù)的持久性(優(yōu)勢)忠蝗,滿了會將不用的數(shù)據(jù)存放在磁盤上现横。
  • 支持?jǐn)?shù)據(jù)類型不同: Memcache 對數(shù)據(jù)類型支持相對簡單,只支持 key-value 結(jié)構(gòu)阁最。Redis 有復(fù)雜的數(shù)據(jù)類型(功能更強(qiáng))戒祠。
  • Memcached 是多線程,非阻塞 IO 復(fù)用的網(wǎng)絡(luò)模型速种;Redis 使用單線程的多路 IO 復(fù)用模型姜盈。 (Redis 6.0 引入了多線程 IO )
  • 過期數(shù)據(jù)刪除策略不同:Memcached 過期數(shù)據(jù)的刪除策略只用了惰性刪除,而 Redis 同時使用了惰性刪除與定期刪除配阵。
  • Redis 支持發(fā)布訂閱模型馏颂、Lua 腳本示血、事務(wù)等功能,而 Memcached 不支持救拉。并且难审,Redis 支持更多的編程語言。

redis和mongodb區(qū)別

MongoDB和Redis都是NoSQL亿絮,采用結(jié)構(gòu)型數(shù)據(jù)存儲告喊。二者在使用場景中,存在一定的區(qū)別壹无,這也主要由于二者在內(nèi)存映射的處理過程葱绒,持久化的處理方法不同。MongoDB建議集群部署斗锭,更多的考慮到集群方案地淀,Redis更偏重于進(jìn)程順序?qū)懭耄m然支持集群岖是,也僅限于主-從模式帮毁。

MongoDB 更類似 MySQL,支持字段索引豺撑、游標(biāo)操作烈疚,其優(yōu)勢在于查詢功能比較強(qiáng)大,擅長查詢 JSON 數(shù)據(jù)聪轿,能存儲海量數(shù)據(jù)爷肝,但是不支持事務(wù)。

MySQL 在大數(shù)據(jù)量時效率顯著下降陆错,MongoDB 更多時候作為關(guān)系數(shù)據(jù)庫的一種替代灯抛。

Redis 是一個開源(BSD許可)的,內(nèi)存中的數(shù)據(jù)結(jié)構(gòu)存儲系統(tǒng)音瓷,它可以用作數(shù)據(jù)庫对嚼、緩存和消息中間件。它支持多種類型的數(shù)據(jù)結(jié)構(gòu)绳慎。

主要區(qū)別:

  • 內(nèi)存的管理機(jī)制:Redis 數(shù)據(jù)全部存在內(nèi)存纵竖,定期寫入磁盤,當(dāng)內(nèi)存不夠時杏愤,可以選擇指定的 LRU 算法刪除數(shù)據(jù)靡砌;MongoDB 數(shù)據(jù)存在內(nèi)存,由 linux系統(tǒng) mmap 實現(xiàn)珊楼,當(dāng)內(nèi)存不夠時乏奥,只將熱點(diǎn)數(shù)據(jù)放入內(nèi)存,其他數(shù)據(jù)存在磁盤亥曹。

  • 持久化方式:mongodb的所有數(shù)據(jù)實際上是存放在硬盤的邓了,所有要操作的數(shù)據(jù)通過mmap的方式映射到內(nèi)存某個區(qū)域內(nèi)。然后媳瞪,mongodb就在這塊區(qū)域里面進(jìn)行數(shù)據(jù)修改骗炉,避免了零碎的硬盤操作。至于mmap上的內(nèi)容flush到硬盤就是操作系統(tǒng)的事情了蛇受,所以句葵,如果,mongodb在內(nèi)存中修改了數(shù)據(jù)后兢仰,mmap數(shù)據(jù)flush到硬盤之前乍丈,系統(tǒng)宕機(jī)了,數(shù)據(jù)就會丟失把将。

mmap詳解鏈接:http://www.cnblogs.com/techdoc/archive/2010/12/22/1913521.html

(mmap系統(tǒng)調(diào)用并不是完全為了用于共享內(nèi)存而設(shè)計的轻专。它本身提供了不同于一般對普通文件的訪問方式,進(jìn)程可以像讀寫內(nèi)存一樣對普通文件進(jìn)行操作察蹲。
mmap 系統(tǒng)調(diào)用使得進(jìn)程之間通過映射同一個普通文件實現(xiàn)共享內(nèi)存请垛。普通文件被映射到進(jìn)程地址空間后,進(jìn)程可以像訪問普通內(nèi)存一樣對文件進(jìn)行訪問洽议,不必再調(diào)用宗收。 read(),write()等操作亚兄。mmap并不分配空間, 只是將文件映射到調(diào)用進(jìn)程的地址空間里, 然后你就可以用memcpy等操作寫文件, 而不用write()了.寫完后用msync()同步一下, 你所寫的內(nèi)容就保存到文件里了. 不過這種方式?jīng)]辦法增加文件的長度, 因為要映射的長度在調(diào)用mmap()的時候就決定了混稽。)

redis所有數(shù)據(jù)都是放在內(nèi)存中的,持久化是使用RDB方式或者aof方式审胚。
mysql:無論數(shù)據(jù)還是索引都存放在硬盤中匈勋。到要使用的時候才交換到內(nèi)存中。能夠處理遠(yuǎn)超過內(nèi)存總量的數(shù)據(jù)菲盾。

  • 支持的數(shù)據(jù)結(jié)構(gòu)不同:Redis 支持的數(shù)據(jù)結(jié)構(gòu)豐富颓影,包括hash、set懒鉴、list等诡挂。MongoDB 數(shù)據(jù)結(jié)構(gòu)比較單一,但是支持豐富的數(shù)據(jù)表達(dá)临谱,索引璃俗,最類似關(guān)系型數(shù)據(jù)庫,支持的查詢語言非常豐富悉默。

  • 數(shù)據(jù)量和性能:當(dāng)物理內(nèi)存夠用的時候城豁,性能:redis>mongodb>mysql。當(dāng)物理內(nèi)存不夠用的時候抄课,redis和mongodb都會使用虛擬內(nèi)存唱星。實際上如果redis要開始虛擬內(nèi)存雳旅,那很明顯要么加內(nèi)存條,要么你換個數(shù)據(jù)庫了间聊。但是攒盈,mongodb不一樣,只要哎榴,業(yè)務(wù)上能保證型豁,冷熱數(shù)據(jù)的讀寫比,使得熱數(shù)據(jù)在物理內(nèi)存中尚蝌,mmap的交換較少迎变。mongodb還是能夠保證性能。

  • 事務(wù)支持:Redis 事務(wù)支持比較弱飘言,只能保證事務(wù)中的每個操作連續(xù)執(zhí)行衣形,mongodb不支持事務(wù)

redis安裝與啟動

安裝redis

去官網(wǎng)下載壓縮包:https://redis.io/download 或者通過 redis 官網(wǎng)提供的在線 redis 環(huán)境

安裝依賴热凹、編譯泵喘、安裝:

1[root@instance-lynj0v9k-19 redis-5.0.9]# yum install gcc-c++
2[root@instance-lynj0v9k-19 redis-5.0.9]# make
3[root@instance-lynj0v9k-19 redis-5.0.9]# make install

我們自己安裝的程序默認(rèn)都在 /usr/local/bin目錄下

啟動redis

將redis的配置文件拷貝到/usr/local/bin目錄下,之后使用這個配置文件啟動般妙。

修改配置文件纪铺,讓redis后臺啟動。

1daemonize no # 修改成 yes

指定配置文件碟渺,啟動redis鲜锚。

1redis-server /絕對路徑/redis.conf

使用redis-client連接redis。

1redis-cli -h 地址 -p 端口號

退出

1# 關(guān)閉redis-server
2127.0.0.1:16379> shutdown
3
4# 退出redis-cli
5not connected> exit
6
7# 重新嘗試連接苫拍,因為redis-server被關(guān)閉了芜繁,所以會被拒絕
8[root@instance-lynj0v9k-19 bin]# redis-cli -p 16379
9Could not connect to Redis at 127.0.0.1:16379: Connection refused
10not connected> exit
11
12# 查看redis-server是否還在
13[root@instance-lynj0v9k-19 bin]# netstat -luntp | grep 16379
14[root@instance-lynj0v9k-19 bin]#

redis常用基礎(chǔ)命令

點(diǎn)擊查看所有命令

1# redis默認(rèn)存在16個數(shù)據(jù)庫,默認(rèn)一開始使用的是第0個
2[root@instance-lynj0v9k-19 bin]# cat redis.conf | grep  'databases'
3databases 16
4
5# 切換數(shù)據(jù)庫
6127.0.0.1:16379> select 2
7OK
8127.0.0.1:16379[2]>
9
10# 查看數(shù)據(jù)庫容量
11127.0.0.1:16379[2]> DBSIZE
12(integer) 0
13
14# 查看所有的key
15127.0.0.1:16379[2]> keys *
16(empty list or set)
17
18# 清空當(dāng)前數(shù)據(jù)庫中的值
19127.0.0.1:16379[2]> flushdb
20OK
21
22# 清空所有庫
23127.0.0.1:16379[2]> flushall
24OK
數(shù)據(jù)庫的相關(guān)命令 指令
關(guān)閉redis shutdown
選擇數(shù)據(jù)庫(共16個庫绒极,默認(rèn)使用第一個骏令,索引為0) select 庫索引
查看當(dāng)前數(shù)據(jù)庫中key的數(shù)量 Dbsize
清空當(dāng)前庫 Flushdb
通殺所有庫 Flushall
key常用命令 指令
查看所有key keys *
判斷某個key是否存在 exists key
把key移動到別的庫 move key db
為key 設(shè)定過期時間 expire key 秒鐘
查看還有多少秒過期 ttl key
查看當(dāng)前key是什么類型的 type key

補(bǔ)充問題:為什么用redis?

簡單說:提升用戶體驗和應(yīng)對更多的用戶

下面我們主要從“高性能”和“高并發(fā)”這兩點(diǎn)來看待這個問題垄提。

高性能 :

對照上面 ?? 我畫的圖榔袋。我們設(shè)想這樣的場景:

假如用戶第一次訪問數(shù)據(jù)庫中的某些數(shù)據(jù)的話,這個過程是比較慢铡俐,畢竟是從硬盤中讀取的凰兑。但是,如果說审丘,用戶訪問的數(shù)據(jù)屬于高頻數(shù)據(jù)并且不會經(jīng)常改變的話吏够,那么我們就可以很放心地將該用戶訪問的數(shù)據(jù)存在緩存中。

這樣有什么好處呢? 那就是保證用戶下一次再訪問這些數(shù)據(jù)的時候就可以直接從緩存中獲取了锅知。操作緩存就是直接操作內(nèi)存播急,所以速度相當(dāng)快。

不過喉镰,要保持?jǐn)?shù)據(jù)庫和緩存中的數(shù)據(jù)的一致性旅择。 如果數(shù)據(jù)庫中的對應(yīng)數(shù)據(jù)改變的之后,同步改變緩存中相應(yīng)的數(shù)據(jù)即可侣姆!

高并發(fā):

一般像 MySQL 這類的數(shù)據(jù)庫的 QPS 大概都在 1w 左右(4 核 8g) ,但是使用 Redis 緩存之后很容易達(dá)到 10w+沉噩,甚至最高能達(dá)到 30w+(就單機(jī) redis 的情況捺宗,redis 集群的話會更高)。

ps:QPS(Query Per Second):服務(wù)器每秒可以執(zhí)行的查詢次數(shù)川蒙;

所以蚜厉,直接操作緩存能夠承受的數(shù)據(jù)庫請求數(shù)量是遠(yuǎn)遠(yuǎn)大于直接訪問數(shù)據(jù)庫的,所以我們可以考慮把數(shù)據(jù)庫中的部分?jǐn)?shù)據(jù)轉(zhuǎn)移到緩存中去畜眨,這樣用戶的一部分請求會直接到緩存這里而不用經(jīng)過數(shù)據(jù)庫昼牛。進(jìn)而,我們也就提高的系統(tǒng)整體的并發(fā)康聂。

redis線程模型(單 -> 多線程)

Redis 基于 Reactor 模式來設(shè)計開發(fā)了自己的一套高效的事件處理模型 (Netty 的線程模型也基于 Reactor 模式贰健,Reactor 模式不愧是高性能 IO 的基石),這套事件處理模型對應(yīng)的是 Redis 中的文件事件處理器(file event handler)恬汁。由于文件事件處理器(file event handler)是單線程方式運(yùn)行的伶椿,所以我們一般都說 Redis 是單線程模型。

既然是單線程氓侧,那怎么監(jiān)聽大量的客戶端連接呢脊另?

  • Redis 通過IO 多路復(fù)用程序 來監(jiān)聽來自客戶端的大量連接(或者說是監(jiān)聽多個 socket),它會將感興趣的事件及類型(讀约巷、寫)注冊到內(nèi)核中并監(jiān)聽每個事件是否發(fā)生偎痛。

  • 這樣的好處非常明顯: I/O 多路復(fù)用技術(shù)的使用讓 Redis 不需要額外創(chuàng)建多余的線程來監(jiān)聽客戶端的大量連接,降低了資源的消耗(和 NIO 中的 Selector 組件很像)独郎。

  • 另外踩麦, Redis 服務(wù)器是一個事件驅(qū)動程序,服務(wù)器需要處理兩類事件: 1. 文件事件; 2. 時間事件囚聚。

  • 時間事件不需要多花時間了解靖榕,我們接觸最多的還是 文件事件(客戶端進(jìn)行讀取寫入等操作,涉及一系列網(wǎng)絡(luò)通信)顽铸。

《Redis 設(shè)計與實現(xiàn)》有一段話是如是介紹文件事件的茁计,我覺得寫得挺不錯。

  • Redis 基于 Reactor 模式開發(fā)了自己的網(wǎng)絡(luò)事件處理器:這個處理器被稱為文件事件處理器(file event handler)。文件事件處理器使用 I/O 多路復(fù)用(multiplexing)程序來同時監(jiān)聽多個套接字星压,并根據(jù) 套接字目前執(zhí)行的任務(wù)來為套接字關(guān)聯(lián)不同的事件處理器践剂。

  • 當(dāng)被監(jiān)聽的套接字準(zhǔn)備好執(zhí)行連接應(yīng)答(accept)、讀饶缺臁(read)逊脯、寫入(write)、關(guān) 閉(close)等操作時竣贪,與操作相對應(yīng)的文件事件就會產(chǎn)生军洼,這時文件事件處理器就會調(diào)用套接字之前關(guān)聯(lián)好的事件處理器來處理這些事件。

  • 雖然文件事件處理器以單線程方式運(yùn)行演怎,但通過使用 I/O 多路復(fù)用程序來監(jiān)聽多個套接字匕争,文件事件處理器既實現(xiàn)了高性能的網(wǎng)絡(luò)通信模型,又可以很好地與 Redis 服務(wù)器中其他同樣以單線程方式運(yùn)行的模塊進(jìn)行對接爷耀,這保持了 Redis 內(nèi)部單線程設(shè)計的簡單性甘桑。

文件事件處理器,主要包括四個部分:

  • 多個 socket(客戶端連接)
  • IO 多路復(fù)用程序(支持多個客戶端連接的關(guān)鍵)
  • 文件事件分派器(將 socket 關(guān)聯(lián)到相應(yīng)的事件處理器)
  • 事件處理器(連接應(yīng)答處理器歹叮、命令請求處理器跑杭、命令回復(fù)處理器)

雖然說 Redis 是單線程模型,但是咆耿,實際上德谅,Redis 在 4.0 之后的版本中就已經(jīng)加入了對多線程的支持。

不過票灰,Redis 4.0 增加的多線程主要是針對一些大鍵值對的刪除操作的命令女阀,使用這些命令就會使用主處理之外的其他線程來“異步處理”。

Redis在處理客戶端的請求時屑迂,包括獲取 (socket 讀)浸策、解析、執(zhí)行惹盼、內(nèi)容返回 (socket 寫) 等都由一個順序串行的主線程處理庸汗,這就是所謂的“單線程”。但如果嚴(yán)格來講從Redis4.0之后并不是單線程手报,除了主線程外蚯舱,它也有后臺線程在處理一些較為緩慢的操作,例如清理臟數(shù)據(jù)掩蛤、無用連接的釋放枉昏、大 key 的刪除等等。

大體上來說揍鸟,Redis 6.0 之前主要還是單線程處理兄裂。那,Redis6.0 之前 為什么不使用多線程?

我覺得主要原因有下面 3 個:

  • 單線程編程容易并且更容易維護(hù)晰奖;
  • Redis 的性能瓶頸不在 CPU 谈撒,主要在內(nèi)存和網(wǎng)絡(luò);
  • 多線程就造成執(zhí)行順序不確定匾南,會存在死鎖啃匿、線程上下文切換,加解鎖等問題蛆楞,甚至?xí)绊懶阅堋?/li>

Redis6.0 引入多線程主要是為了充分利用cpu資源溯乒,提高網(wǎng)絡(luò) IO 讀寫性能,因為這個算是 Redis 中的一個性能瓶頸(Redis 的瓶頸主要受限于內(nèi)存和網(wǎng)絡(luò))臊岸。

雖然橙数,Redis6.0 引入了多線程,但是 Redis 的多線程只是在網(wǎng)絡(luò)數(shù)據(jù)的讀寫這類耗時操作上使用了帅戒, 執(zhí)行命令仍然是單線程順序執(zhí)行。因此崖技,你也不需要擔(dān)心線程安全問題逻住。

# Redis6.0 的多線程默認(rèn)是禁用的,只使用主線程迎献。如需開啟需要修改 redis 配置文件 redis.conf :
io-threads-do-reads yes

# 開啟多線程后瞎访,還需要設(shè)置線程數(shù),否則是不生效的吁恍。同樣需要修改 redis 配置文件 redis.conf :
io-threads 4 #官網(wǎng)建議4核的機(jī)器建議設(shè)置為2或3個線程扒秸,8核的建議設(shè)置為6個線程

ps:Redis6.0與Memcached多線程模型對比

  • 相同點(diǎn):都采用了 master線程-worker 線程的模型

  • 不同點(diǎn):Memcached 執(zhí)行主邏輯也是在 worker 線程里,模型更加簡單冀瓦,實現(xiàn)了真正的線程隔離伴奥,符合我們對線程隔離的常規(guī)理解。而 Redis 把處理邏輯交還給 master 線程翼闽,雖然一定程度上增加了模型復(fù)雜度拾徙,但也解決了線程并發(fā)安全等問題。

ps:Redis線程中經(jīng)常提到IO多路復(fù)用感局,如何理解尼啡?

  • 這是IO模型的一種,即經(jīng)典的Reactor設(shè)計模式询微,有時也稱為異步阻塞IO蚂夕。
  • 多路指的是多個socket連接蟋滴,復(fù)用指的是復(fù)用一個線程。多路復(fù)用主要有三種技術(shù):select,poll搏屑,epoll芬膝。epoll是最新的也是目前最好的多路復(fù)用技術(shù)。采用多路 I/O 復(fù)用技術(shù)可以讓單個線程高效的處理多個連接請求(盡量減少網(wǎng)絡(luò)IO的時間消耗),且Redis在內(nèi)存中操作數(shù)據(jù)的速度非痴畛裕快(內(nèi)存內(nèi)的操作不會成為這里的性能瓶頸),主要以上兩點(diǎn)造就了Redis具有很高的吞吐量西雀。

拓展:高性能IO模型

服務(wù)器端編程經(jīng)常需要構(gòu)造高性能的IO模型萨驶,常見的IO模型有四種:

  • 同步阻塞IO(Blocking IO):最簡單,用戶線程在內(nèi)核進(jìn)行IO操作時被阻塞艇肴。用戶線程通過系統(tǒng)調(diào)用read發(fā)起IO讀操作腔呜,由用戶空間轉(zhuǎn)到內(nèi)核空間。內(nèi)核等到數(shù)據(jù)包到達(dá)后再悼,然后將接收的數(shù)據(jù)拷貝到用戶空間核畴,完成read操作。
    缺點(diǎn):整個IO請求的過程中冲九,用戶線程是被阻塞的谤草,這導(dǎo)致用戶在發(fā)起IO請求時,不能做任何事情莺奸,對CPU的資源利用率不夠丑孩。
  • 同步非阻塞IO(Non-blocking IO):同步非阻塞IO是在同步阻塞IO的基礎(chǔ)上,將socket設(shè)置為NONBLOCK灭贷。由于socket是非阻塞的方式温学,因此用戶線程發(fā)起IO請求時立即返回。但并未讀取到任何數(shù)據(jù)甚疟,用戶線程需要不斷地發(fā)起IO請求仗岖,直到數(shù)據(jù)到達(dá)后,才真正讀取到數(shù)據(jù)览妖,繼續(xù)執(zhí)行轧拄。
    缺點(diǎn):雖然用戶線程每次發(fā)起IO請求后可以立即返回,但是為了等到數(shù)據(jù)黄痪,仍需要不斷地輪詢紧帕、重復(fù)請求,消耗了大量的CPU的資源桅打。一般很少直接使用這種模型是嗜,而是在其他IO模型中使用非阻塞IO這一特性。
  • IO多路復(fù)用(IO Multiplexing):即經(jīng)典的Reactor設(shè)計模式挺尾,有時也稱為異步阻塞IO鹅搪,Java中的Selector和Linux中的epoll都是這種模型。IO多路復(fù)用模型是建立在內(nèi)核提供的多路分離函數(shù)select基礎(chǔ)之上的遭铺,使用select函數(shù)可以避免同步非阻塞IO模型中輪詢等待的問題丽柿。
    基本流程:用戶首先將需要進(jìn)行IO操作的socket添加到select中恢准,然后阻塞等待select系統(tǒng)調(diào)用返回。當(dāng)數(shù)據(jù)到達(dá)時甫题,socket被激活馁筐,select函數(shù)返回。用戶線程正式發(fā)起read請求坠非,讀取數(shù)據(jù)并繼續(xù)執(zhí)行敏沉。
    從流程上來看,使用select函數(shù)進(jìn)行IO請求和同步阻塞模型沒有太大的區(qū)別炎码,甚至還多了添加監(jiān)視socket盟迟,以及調(diào)用select函數(shù)的額外操作,效率更差潦闲。但是攒菠,使用select以后最大的優(yōu)勢是用戶可以在一個線程內(nèi)同時處理多個socket的IO請求。用戶可以注冊多個socket歉闰,然后不斷地調(diào)用select讀取被激活的socket辖众,即可達(dá)到在同一個線程內(nèi)同時處理多個IO請求的目的。而在同步阻塞模型中和敬,必須通過多線程的方式才能達(dá)到這個目的赵辕。
    然而,使用select函數(shù)的優(yōu)點(diǎn)并不僅限于此概龄。雖然上述方式允許單線程內(nèi)處理多個IO請求,但是每個IO請求的過程還是阻塞的(在select函數(shù)上阻塞)饲握,平均時間甚至比同步阻塞IO模型還要長私杜。如果用戶線程只注冊自己感興趣的socket或者IO請求,然后去做自己的事情救欧,等到數(shù)據(jù)到來時再進(jìn)行處理衰粹,則可以提高CPU的利用率。
  • 異步IO(Asynchronous IO):即經(jīng)典的Proactor設(shè)計模式笆怠,也稱為異步非阻塞IO铝耻。“真正”的異步IO需要操作系統(tǒng)更強(qiáng)的支持蹬刷。在IO多路復(fù)用模型中瓢捉,事件循環(huán)將文件句柄的狀態(tài)事件通知給用戶線程,由用戶線程自行讀取數(shù)據(jù)办成、處理數(shù)據(jù)泡态。而在異步IO模型中,當(dāng)用戶線程收到通知時迂卢,數(shù)據(jù)已經(jīng)被內(nèi)核讀取完畢某弦,并放在了用戶線程指定的緩沖區(qū)內(nèi)桐汤,內(nèi)核在IO完成后通知用戶線程直接使用即可。

拓展:同步靶壮、異步和阻塞怔毛、非阻塞

  • 同步和異步的概念描述的是用戶線程與內(nèi)核的交互方式:同步是指用戶線程發(fā)起IO請求后需要等待或者輪詢,內(nèi)核IO操作完成后才能繼續(xù)執(zhí)行腾降;而異步是指用戶線程發(fā)起IO請求后仍繼續(xù)執(zhí)行拣度,當(dāng)內(nèi)核IO操作完成后會通知用戶線程,或者調(diào)用用戶線程注冊的回調(diào)函數(shù)蜂莉。

  • 阻塞和非阻塞的概念描述的是用戶線程調(diào)用內(nèi)核IO操作的方式:阻塞是指IO操作需要徹底完成后才返回到用戶空間蜡娶;而非阻塞是指IO操作被調(diào)用后立即返回給用戶一個狀態(tài)值,無需等到IO操作徹底完成映穗。

拓展:Reactor設(shè)計模式

  • EventHandler抽象類表示IO事件處理器窖张,它擁有IO文件句柄Handle(通過get_handle獲取)蚁滋,以及對Handle的操作handle_event(讀/寫等)宿接。
  • 繼承于EventHandler的子類可以對事件處理器的行為進(jìn)行定制。
  • Reactor類用于管理EventHandler(注冊辕录、刪除等)睦霎,并使用handle_events實現(xiàn)事件循環(huán),不斷調(diào)用同步事件多路分離器(一般是內(nèi)核)的多路分離函數(shù)select走诞,只要某個文件句柄被激活(可讀/寫等)副女,select就返回(阻塞),handle_events就會調(diào)用與文件句柄關(guān)聯(lián)的事件處理器的handle_event進(jìn)行相關(guān)操作蚣旱。

通過Reactor的方式碑幅,可以將用戶線程輪詢IO操作狀態(tài)的工作統(tǒng)一交給handle_events事件循環(huán)進(jìn)行處理。用戶線程注冊事件處理器之后可以繼續(xù)執(zhí)行做其他的工作(異步)塞绿,而Reactor線程負(fù)責(zé)調(diào)用內(nèi)核的select函數(shù)檢查socket狀態(tài)沟涨。當(dāng)有socket被激活時,則通知相應(yīng)的用戶線程(或執(zhí)行用戶線程的回調(diào)函數(shù))异吻,執(zhí)行handle_event進(jìn)行數(shù)據(jù)讀取裹赴、處理的工作。由于select函數(shù)是阻塞的诀浪,因此多路IO復(fù)用模型也被稱為異步阻塞IO模型棋返。注意,這里的所說的阻塞是指select函數(shù)執(zhí)行時線程被阻塞笋妥,而不是指socket懊昨。一般在使用IO多路復(fù)用模型時,socket都是設(shè)置為NONBLOCK的春宣,不過這并不會產(chǎn)生影響酵颁,因為用戶發(fā)起IO請求時嫉你,數(shù)據(jù)已經(jīng)到達(dá)了,用戶線程一定不會被阻塞躏惋。

IO多路復(fù)用是最常使用的IO模型幽污,但是其異步程度還不夠“徹底”,因為它使用了會阻塞線程的select系統(tǒng)調(diào)用簿姨。因此IO多路復(fù)用只能稱為異步阻塞IO距误,而非真正的異步IO。

巨人的肩膀

https://snailclimb.gitee.io/javaguide/#/docs/database/Redis/redis-all

https://mp.weixin.qq.com/s/CMu7oXVIKp2s-PXTdMlimA

https://www.cnblogs.com/yanguhung/p/10145755.html

http://www.reibang.com/p/2b523fbee36f

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末扁位,一起剝皮案震驚了整個濱河市准潭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌域仇,老刑警劉巖刑然,帶你破解...
    沈念sama閱讀 222,807評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異暇务,居然都是意外死亡泼掠,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評論 3 399
  • 文/潘曉璐 我一進(jìn)店門垦细,熙熙樓的掌柜王于貴愁眉苦臉地迎上來择镇,“玉大人,你說我怎么就攤上這事括改∧逋悖” “怎么了?”我有些...
    開封第一講書人閱讀 169,589評論 0 363
  • 文/不壞的土叔 我叫張陵嘱能,是天一觀的道長饲梭。 經(jīng)常有香客問我,道長焰檩,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,188評論 1 300
  • 正文 為了忘掉前任订框,我火速辦了婚禮析苫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘穿扳。我一直安慰自己衩侥,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,185評論 6 398
  • 文/花漫 我一把揭開白布矛物。 她就那樣靜靜地躺著茫死,像睡著了一般。 火紅的嫁衣襯著肌膚如雪履羞。 梳的紋絲不亂的頭發(fā)上峦萎,一...
    開封第一講書人閱讀 52,785評論 1 314
  • 那天屡久,我揣著相機(jī)與錄音,去河邊找鬼爱榔。 笑死被环,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的详幽。 我是一名探鬼主播筛欢,決...
    沈念sama閱讀 41,220評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼唇聘!你這毒婦竟也來了版姑?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,167評論 0 277
  • 序言:老撾萬榮一對情侶失蹤迟郎,失蹤者是張志新(化名)和其女友劉穎剥险,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谎亩,經(jīng)...
    沈念sama閱讀 46,698評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡炒嘲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,767評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了匈庭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片夫凸。...
    茶點(diǎn)故事閱讀 40,912評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖阱持,靈堂內(nèi)的尸體忽然破棺而出夭拌,到底是詐尸還是另有隱情,我是刑警寧澤衷咽,帶...
    沈念sama閱讀 36,572評論 5 351
  • 正文 年R本政府宣布鸽扁,位于F島的核電站,受9級特大地震影響镶骗,放射性物質(zhì)發(fā)生泄漏桶现。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,254評論 3 336
  • 文/蒙蒙 一鼎姊、第九天 我趴在偏房一處隱蔽的房頂上張望骡和。 院中可真熱鬧,春花似錦相寇、人聲如沸慰于。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽婆赠。三九已至,卻和暖如春佳励,著一層夾襖步出監(jiān)牢的瞬間休里,已是汗流浹背蛆挫。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留份帐,地道東北人璃吧。 一個月前我還...
    沈念sama閱讀 49,359評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像废境,于是被迫代替她去往敵國和親畜挨。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,922評論 2 361

推薦閱讀更多精彩內(nèi)容