亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Redis6.0為什么要引入多線程

發布時間:2020-10-20 14:48:02 來源:億速云 閱讀:746 作者:小新 欄目:關系型數據庫

這篇文章給大家分享的是有關Redis6.0為什么要引入多線程的內容。小編覺得挺實用的,因此分享給大家做個參考。一起跟隨小編過來看看吧。

Redis6.0之前為什么采用單線程模型


嚴格地說,從Redis 4.0之后并不是單線程。除了主線程外,還有一些后臺線程處理一些較為緩慢的操作,例如無用連接的釋放、大 key 的刪除等等。

單線程模型,為何性能那么高?


Redis作者從設計之初,進行了多方面的考慮。最終選擇使用單線程模型來處理命令。之所以選擇單線程模型,主要有如下幾個重要原因:

  1. Redis操作基于內存,絕大多數操作的性能瓶頸不在CPU

  2. 單線程模型,避免了線程間切換帶來的性能開銷

  3. 使用單線程模型也能并發的處理客戶端的請求(多路復用I/O)

  4. 使用單線程模型,可維護性更高,開發,調試和維護的成本更低

上述第三個原因是Redis最終采用單線程模型的決定性因素,其他的兩個原因都是使用單線程模型額外帶來的好處,在這里我們會按順序介紹上述的幾個原因。

性能瓶頸不在CPU

下圖是Redis官網對單線程模型的說明。大概意思是:Redis的瓶頸并不在CPU,它的主要瓶頸在于內存和網絡。在Linux環境中,Redis每秒甚至可以提交100萬次請求。

Redis6.0為什么要引入多線程

為什么說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查看上下文切換次數的示例:

Redis6.0為什么要引入多線程vmstat 1 表示每秒統計一次, 其中cs列就是指上下文切換的數目. 一般情況下, 空閑系統的上下文切換每秒在1500以下。

并行處理客戶端的請求(I/O多路復用)

如上所述:Redis的瓶頸并不在CPU,它的主要瓶頸在于內存和網絡。所謂內存瓶頸很好理解,Redis做為緩存使用時很多場景需要緩存大量數據,所以需要大量內存空間,這可以通過集群分片去解決,例如Redis自身的無中心集群分片方案以及Codis這種基于代理的集群分片方案。

對于網絡瓶頸,Redis在網絡I/O模型上采用了多路復用技術,來減少網絡瓶頸帶來的影響。很多場景中使用單線程模型并不意味著程序不能并發的處理任務。Redis 雖然使用單線程模型處理用戶的請求,但是它卻使用 I/O 多路復用技術“并行”處理來自客戶端的多個連接,同時等待多個連接發送的請求。使用 I/O多路復用技術能極大地減少系統的開銷,系統不再需要為每個連接創建專門的監聽線程,避免了由于大量的線程創建帶來的巨大性能開銷。

Redis6.0為什么要引入多線程

下面我們詳細解釋一下多路復用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都采用了這種模型。

Redis6.0為什么要引入多線程

下圖是基于epoll函數Socket編程的詳細流程。

Redis6.0為什么要引入多線程

可維護性

我們知道,多線程可以充分利用多核CPU,在高并發場景下,能夠減少因I/O等待帶來的CPU損耗,帶來很好的性能表現。不過多線程卻是一把雙刃劍,帶來好處的同時,還會帶來代碼維護困難,線上問題難于定位和調試,死鎖等問題。多線程模型中代碼的執行過程不再是串行的,多個線程同時訪問的共享變量如果處理不當也會帶來詭異的問題。

Redis6.0為什么要引入多線程

我們通過一個例子,看一下多線程場景下發生的詭異現象。看下面的代碼:

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引入的多線程部分,實際上只是用來處理網絡數據的讀寫和協議解析,執行命令仍然是單一工作線程。

Redis6.0為什么要引入多線程

從上圖我們可以看到Redis在處理網絡數據時,調用epoll的過程是阻塞的,也就是說這個過程會阻塞線程,如果并發量很高,達到幾萬的QPS,此處可能會成為瓶頸。一般我們遇到此類網絡IO瓶頸的問題,可以增加線程數來解決。開啟多線程除了可以減少由于網絡I/O等待造成的影響,還可以充分利用CPU的多核優勢。Redis6.0也不例外,在此處增加了多線程來處理網絡數據,以此來提高Redis的吞吐量。當然相關的命令處理還是單線程運行,不存在多線程下并發訪問帶來的種種問題。

Redis6.0為什么要引入多線程

性能對比


壓測配置:

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

Redis6.0為什么要引入多線程

圖片來源于網絡 Redis6.0為什么要引入多線程

圖片來源于網絡

從上面可以看到 GET/SET 命令在多線程版本中性能相比單線程幾乎翻了一倍。另外,這些數據只是為了簡單驗證多線程 I/O 是否真正帶來性能優化,并沒有針對具體的場景進行壓測,數據僅供參考。本次性能測試基于 unstble 分支,不排除后續發布的正式版本的性能會更好。

可見單線程有單線程的好處,多線程有多線程的優勢,只有充分理解其中的本質原理,才能靈活運用于生產實踐當中。

感謝各位的閱讀!關于Redis6.0為什么要引入多線程就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

弥勒县| 平江县| 云霄县| 望江县| 子洲县| 锦屏县| 类乌齐县| 永靖县| 清新县| 大冶市| 招远市| 台湾省| 无锡市| 施秉县| 高邑县| 柘荣县| 兴宁市| 麻阳| 洪洞县| 五莲县| 遂宁市| 西贡区| 双鸭山市| 呼和浩特市| 彭水| 兖州市| 类乌齐县| 阿坝县| 玉屏| 兰考县| 太湖县| 漳州市| 漳平市| 台东市| 华容县| 玉环县| 维西| 项城市| 张掖市| 临朐县| 西乡县|