下面由Redis教學欄位介紹Redis6.0到底為何介紹多執行緒? ,希望對需要的朋友有幫助!
作者簡介:曾任職於阿里巴巴,每日優鮮等網路公司,擔任技術總監。 15年電商互聯網經驗。
一百天前Redis作者antirez在部落格上(antirez.com)發布了一條重磅消息,Redis6.0正式發布了。其中最引人注目的改動就是,Redis6.0引入了多執行緒。
本文主要分成兩部分。首先我們先聊聊Redis6.0之前為什麼要採用單執行緒模型。然後再詳細解釋Redis6.0的多執行緒。
Redis6.0之前為什麼採用單執行緒模型
嚴格地說,從Redis 4.0之後並不是單線程。除了主執行緒外,還有一些後台執行緒處理一些較為緩慢的操作,例如無用連線的釋放、大 key 的刪除等等。
單執行緒模型,為何效能那麼高?
Redis作者從設計之初,進行了多方面的考慮。最後選擇使用單線程模型來處理命令。之所以選擇單線程模型,主要有以下幾個重要原因:
Redis操作基於內存,絕大多數操作的性能瓶頸不在CPU
-
單執行緒模型,避免了執行緒間切換帶來的效能開銷
使用單執行緒模型也能並發的處理客戶端的請求(多路復用I/O)
使用單執行緒模型,可維護性更高,開發,除錯和維護的成本較低
上述第三個原因是Redis最終採用單線程模型的決定性因素,其他的兩個原因都是使用單線程模型額外帶來的好處,在這裡我們會按順序介紹上述的幾個原因。
效能瓶頸不在CPU
#下圖是Redis官網對單執行緒模型的說明。大概意思是:Redis的瓶頸並不在CPU,它的主要瓶頸在於記憶體和網路。在Linux環境中,Redis每秒甚至可以提交100萬次請求。
為什麼說Redis的瓶頸不在CPU?
首先,Redis絕大部分的操作是基於記憶體的,而且是純kv(key-value)操作,所以指令執行速度非常快。我們可以大概理解成,redis中的資料儲存在一張大HashMap中,HashMap的優勢就是尋找和寫入的時間複雜度都是O(1)。 Redis內部採用這種結構儲存數據,就奠定了Redis高效能的基礎。根據Redis官網描述,在理想情況下Redis每秒可以提交一百萬次請求,每次請求提交所需的時間在奈秒的時間量級。既然每次的Redis操作都這麼快,單執行緒就可以完全搞定了,那還何必要用多執行緒呢!
執行緒上下文切換問題
另外,多執行緒場景下會發生執行緒上下文切換。線程是由CPU調度的,CPU的一個核在一個時間片內只能同時執行一個線程,在CPU由線程A切換到線程B的過程中會發生一系列的操作,主要過程包括保存線程A的執行現場,然後載入線程B的執行現場,這個過程就是「線程上下文切換」。其中涉及線程相關指令的保存和恢復。
頻繁的執行緒上下文切換可能會導致效能急劇下降,這會導致我們不僅沒有提升處理請求的速度,反而降低了效能,這也是 Redis 對於多執行緒技術持謹慎態度的原因之一。
在Linux系統中可以使用vmstat指令來查看上下文切換的次數,以下是vmstat查看上下文切換次數的範例:
vmstat 1 表示每秒統計一次, 其中cs列就是指上下文切換的數目. 一般情況下, 空閒系統的上下文切換每秒在1500以下。
並行處理客戶端的請求(I/O多路復用)
#如上所述:Redis的瓶頸並不在CPU,它的主要瓶頸在於記憶體和網路。所謂內存瓶頸很好理解,Redis做為緩存使用時很多場景需要緩存大量數據,所以需要大量內存空間,這可以通過集群分片去解決,例如Redis自身的無中心集群分片方案以及Codis這種基於代理的集群分片方案。
#對於網路瓶頸,Redis在網路I/O模型上採用了多路復用技術,來減少網路瓶頸帶來的影響。很多場景中使用單線程模型並不意味著程式不能並發的處理任務。 Redis 雖然使用單線程模型處理用戶的請求,但是它卻使用 I/O 多路復用技術「並行」處理來自客戶端的多個連接,同時等待多個連接發送的請求。使用 I/O多路復用技術能大幅減少系統的開銷,系統不再需要為每個連接創建專門的監聽線程,避免了由於大量的線程創建帶來的巨大性能開銷。
下面我們詳細解釋一下多路復用I/O模型。為了能更充分理解,我們先了解幾個基本概念。
Socket(套接字):Socket可以理解成,在兩個應用程式進行網路通訊時,分別在兩個應用程式中的通訊端點。通訊時,一個應用程式將資料寫入Socket,然後透過網路卡把資料發送到另外一個應用程式的Socket。我們平常所說的HTTP和TCP協定的遠端通信,底層都是基於Socket實現的。 5種網路IO模型也都要基於Socket實現網路通訊。
阻塞與非阻塞:所謂阻塞,就是發出一個請求不能立刻回傳回應,要等所有的邏輯全處理完才能回傳回應。非阻塞反之,發出一個請求立刻返回應答,不用等處理完所有邏輯。
核心空間與使用者空間:在Linux中,應用程式穩定性遠遠比不上作業系統程序,為了確保作業系統的穩定性,Linux區分了核心空間和使用者空間。可以這樣理解,內核空間運行作業系統程式和驅動程序,用戶空間運行應用程式。 Linux以這種方式隔離了作業系統程式和應用程序,避免了應用程式影響到作業系統本身的穩定性。這也是Linux系統超穩定的主因。所有的系統資源操作都在核心空間進行,例如讀寫磁碟文件,記憶體分配和回收,網路介面呼叫等。所以在一次網路IO讀取過程中,資料並不是直接從網路卡讀取到用戶空間中的應用程式緩衝區,而是先從網卡拷貝到核心空間緩衝區,然後再從核心拷貝到用戶空間中的應用程式緩衝區。對於網路IO寫入過程,過程則相反,先將資料從用戶空間中的應用程式緩衝區拷貝到核心緩衝區,再從核心緩衝區把資料透過網卡發送出去。
多路復用I/O模型,建立在多路事件分離函數select,poll,epoll之上。以Redis採用的epoll為例,在發起read請求前,先更新epoll的socket監控列表,然後等待epoll函數返回(此過程是阻塞的,所以說多路復用IO本質上也是阻塞IO模型)。當某個socket有資料到達時,epoll函數會傳回。此時用戶執行緒才正式發起read請求,讀取並處理資料。這個模式用一個專門的監視線程去檢查多個socket,如果某個socket有資料到達就交給工作執行緒處理。由於等待Socket資料到達過程非常耗時,所以這種方式解決了阻塞IO模型一個Socket連接就需要一個線程的問題,也不存在非阻塞IO模型忙輪詢帶來的CPU效能損耗的問題。多工IO模型的實際應用場景很多,大家耳熟能詳的Redis,Java NIO,以及Dubbo採用的通訊框架Netty都採用了這種模型。
下圖是基於epoll函數Socket程式設計的詳細流程。
可維護性
#我們知道,多執行緒可以充分利用多核心CPU,在高並發場景下,能夠減少因I/O等待而帶來的CPU損耗,帶來良好的效能表現。不過多執行緒卻是一把雙面刃,帶來好處的同時,還會帶來程式碼維護困難,線上問題難於定位與調試,死鎖等問題。多執行緒模型中程式碼的執行過程不再是串列的,多個執行緒同時存取的共享變數如果處理不當也會帶來詭異的問題。
我們透過一個例子,看多執行緒場景下發生的詭異現象。看下面的程式碼:
class MemoryReordering { int num = 0; boolean flag = false; public void set() { num = 1; //语句1 flag = true; //语句2 } public int cal() { if( flag == true) { //语句3 return num + num; //语句4 } return -1; } }
flag為true時,cal() 方法回傳值是多少?很多人會說:這還用問嗎!肯定回傳2
结果可能会让你大吃一惊!上面的这段代码,由于语句1和语句2没有数据依赖性,可能会发生指令重排序,有可能编译器会把flag=true放到num=1的前面。此时set和cal方法分别在不同线程中执行,没有先后关系。cal方法,只要flag为true,就会进入if的代码块执行相加的操作。可能的顺序是:
语句1先于语句2执行,这时的执行顺序可能是:语句1->语句2->语句3->语句4。执行语句4前,num = 1,所以cal的返回值是2
语句2先于语句1执行,这时的执行顺序可能是:语句2->语句3->语句4->语句1。执行语句4前,num = 0,所以cal的返回值是0
我们可以看到,在多线程环境下如果发生了指令重排序,会对结果造成严重影响。
当然可以在第三行处,给flag加上关键字volatile来避免指令重排。即在flag处加上了内存栅栏,来阻隔flag(栅栏)前后的代码的重排序。当然多线程还会带来可见性问题,死锁问题以及共享资源安全等问题。
boolean volatile flag = false;
Redis6.0为何引入多线程?
Redis6.0引入的多线程部分,实际上只是用来处理网络数据的读写和协议解析,执行命令仍然是单一工作线程。
从上图我们可以看到Redis在处理网络数据时,调用epoll的过程是阻塞的,也就是说这个过程会阻塞线程,如果并发量很高,达到几万的QPS,此处可能会成为瓶颈。一般我们遇到此类网络IO瓶颈的问题,可以增加线程数来解决。开启多线程除了可以减少由于网络I/O等待造成的影响,还可以充分利用CPU的多核优势。Redis6.0也不例外,在此处增加了多线程来处理网络数据,以此来提高Redis的吞吐量。当然相关的命令处理还是单线程运行,不存在多线程下并发访问带来的种种问题。
性能对比
压测配置:
Redis Server: 阿里云 Ubuntu 18.04,8 CPU 2.5 GHZ, 8G 内存,主机型号 ecs.ic5.2xlarge Redis Benchmark Client: 阿里云 Ubuntu 18.04,8 2.5 GHZ CPU, 8G 内存,主机型号 ecs.ic5.2xlarge
多线程版本Redis 6.0,单线程版本是 Redis 5.0.5。多线程版本需要新增以下配置:
io-threads 4 # 开启 4 个 IO 线程 io-threads-do-reads yes # 请求解析也是用 IO 线程
压测命令: redis-benchmark -h 192.168.0.49 -a foobared -t set,get -n 1000000 -r 100000000 --threads 4 -d ${datasize} -c 256
图片来源于网络
图片来源于网络
从上面可以看到 GET/SET 命令在多线程版本中性能相比单线程几乎翻了一倍。另外,这些数据只是为了简单验证多线程 I/O 是否真正带来性能优化,并没有针对具体的场景进行压测,数据仅供参考。本次性能测试基于 unstble 分支,不排除后续发布的正式版本的性能会更好。
最后
可见单线程有单线程的好处,多线程有多线程的优势,只有充分理解其中的本质原理,才能灵活运用于生产实践当中。
以上是Redis6.0到底為何引入多執行緒?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

Redis的關鍵特性包括速度、靈活性和豐富的數據結構支持。 1)速度:Redis作為內存數據庫,讀寫操作幾乎瞬時,適用於緩存和會話管理。 2)靈活性:支持多種數據結構,如字符串、列表、集合等,適用於復雜數據處理。 3)數據結構支持:提供字符串、列表、集合、哈希表等,適合不同業務需求。

Redis的核心功能是高性能的內存數據存儲和處理系統。 1)高速數據訪問:Redis將數據存儲在內存中,提供微秒級別的讀寫速度。 2)豐富的數據結構:支持字符串、列表、集合等,適應多種應用場景。 3)持久化:通過RDB和AOF方式將數據持久化到磁盤。 4)發布訂閱:可用於消息隊列或實時通信系統。

Redis支持多種數據結構,具體包括:1.字符串(String),適合存儲單一值數據;2.列表(List),適用於隊列和棧;3.集合(Set),用於存儲不重複數據;4.有序集合(SortedSet),適用於排行榜和優先級隊列;5.哈希表(Hash),適合存儲對像或結構化數據。

Redis計數器是一種使用Redis鍵值對存儲來實現計數操作的機制,包含以下步驟:創建計數器鍵、增加計數、減少計數、重置計數和獲取計數。 Redis計數器的優勢包括速度快、高並發、持久性和簡單易用。它可用於用戶訪問計數、實時指標跟踪、遊戲分數和排名以及訂單處理計數等場景。

使用 Redis 命令行工具 (redis-cli) 可通過以下步驟管理和操作 Redis:連接到服務器,指定地址和端口。使用命令名稱和參數向服務器發送命令。使用 HELP 命令查看特定命令的幫助信息。使用 QUIT 命令退出命令行工具。

Redis集群模式通過分片將Redis實例部署到多個服務器,提高可擴展性和可用性。搭建步驟如下:創建奇數個Redis實例,端口不同;創建3個sentinel實例,監控Redis實例並進行故障轉移;配置sentinel配置文件,添加監控Redis實例信息和故障轉移設置;配置Redis實例配置文件,啟用集群模式並指定集群信息文件路徑;創建nodes.conf文件,包含各Redis實例的信息;啟動集群,執行create命令創建集群並指定副本數量;登錄集群執行CLUSTER INFO命令驗證集群狀態;使

要從 Redis 讀取隊列,需要獲取隊列名稱、使用 LPOP 命令讀取元素,並處理空隊列。具體步驟如下:獲取隊列名稱:以 "queue:" 前綴命名,如 "queue:my-queue"。使用 LPOP 命令:從隊列頭部彈出元素並返回其值,如 LPOP queue:my-queue。處理空隊列:如果隊列為空,LPOP 返回 nil,可先檢查隊列是否存在再讀取元素。

Redis 集群中使用 zset:zset 是一種有序集合,將元素與評分關聯。分片策略: a. 哈希分片:根據 zset 鍵的哈希值分佈。 b. 範圍分片:根據元素評分劃分為範圍,並將每個範圍分配給不同的節點。讀寫操作: a. 讀操作:如果 zset 鍵屬於當前節點的分片,則在本地處理;否則,路由到相應的分片。 b. 寫入操作:始終路由到持有 zset 鍵的分片。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

SublimeText3漢化版
中文版,非常好用

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

Dreamweaver Mac版
視覺化網頁開發工具

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。