redis 為什么會阻塞

目錄

前言

客戶端交換時的阻塞

redis 磁盤交換的阻塞

主從節點交互的阻塞

切片集群交互時的阻塞

異步執行的演變

redis 異步執行如何實現的


前言

大家對redis 比較熟悉吧,只要做項目都會用到redis,提高系統的吞吐。小米商城搶購高峰18k的qps,redis 在其中扮演著非常重要的角色。有時我們操作不當,redis 阻塞了,影響了整個業務。我記得2018年的時候,順豐就出現了一個事故,有同學在線上對redis的一個操作,直接阻塞了,影響公司的整個業務,后面這位同學被辭退了。如果他對redis比較了解的話,也不會出現這樣的事故。

redis 為什么會阻塞,與它的本身設計有一定關系。大家都知道redis 是單線程的,這個并完全正確,只是說我們對數據的讀寫是在主線程中完成的。但RDB,aof 重寫,刪除都是在子線程完成的。我們說的阻塞就是阻塞的主線程,redis 為什么會這么設計,抽時間我會詳細講解。這篇文章我主要從客戶端,磁盤,主從節點,切片集群實例 多方面取闡述這個問題。

客戶端交換時的阻塞

客戶端的功能,與redis 服務器建立連接就有對網絡IO的影響,對數據庫的增刪改查操作。redis 采用的是多路復用機制,避免了主線程一直處在等待網絡連接或請求到來的狀態,所以,網絡 IO 不是導致 Redis 阻塞的因素。剩下的也就是增刪改查對redis 的影響,這部分功能也是redis 主要的任務,復雜度高的肯定會阻塞redis。

那么怎么判斷操作復雜度高不高?就是看復雜度的O(N),這個復雜度也可以看作是空間復雜度。N越大復雜度越高,越容易阻塞。我們可以對照redis 官網的命令,進行查看。一般集合的操作都是O(N) 的復雜度。比如HGETALL,SMEMBERS,以及集合的聚合統計操作,交,并,差集。這些操作特殊是集合的全量查詢和聚合操作。

我們看了查詢,刪除會不會造成redis 阻塞了。當然會了,刪除操作的本質是釋放鍵值對占用的內存空間。它不僅釋放內存空間,在Redis 釋放內存時,操作系統會把釋放掉的內存塊插入到一個空閑內存塊鏈表,以便后續進行管理和分配。這個過程需要時間并且會阻塞當前釋放內存的應用程序。元素數量越大,這個操作消耗的時間越多,越容易阻塞,這就是我們所說的bigkey 刪除。

還有就是清空數據庫的操作例如( FLUSHDB 和 FLUSHALL 操作)必然也是一個潛在的阻塞風險,因為它涉及到刪除和釋放所有的鍵值對。

redis 磁盤交換的阻塞

磁盤的操作一直是系統的瓶頸,一次讀盤需要經過尋道,旋轉,傳輸 這么復雜的操作。很多數據庫都用了各種技術來避免經常操作磁盤,比如mysql 用了WAL技術,關于這方面技術描述,可以閱讀我的普通索引和唯一索引詳解。redis 最大的賣點還是還性能,它保證的是操作的高效性,就沒有用這么復雜的技術。redis 與磁盤操作的功能都是放在子線程操作,這樣就避免了主線程的阻塞。redis 生成的 RDB 快照,aof 日志重寫。但是有一個文件比較特殊,aof 日志,會根據不同寫回策略做落盤保存。一個同步寫磁盤的操作的耗時大約是 1~2ms,如果有大量的寫操作需要記錄在 AOF 日志中,并同步寫回的話,就會阻塞主線程了。大家可以看到redis 磁盤交換的阻塞主要發生在aof 日志同步寫。

主從節點交互的阻塞

一般的架構都是一主多從,主節點寫,從節點讀。主從同步的大概過程是,主節點身材RDB 快照,這個操作上面已經講過,是通過子線程生成的,不會阻塞。對于從節點來說就是兩個步驟,一個是FLUSHDB 清空當前數據庫,這個肯定會阻塞,從節點的redis 會回放RDB 的數據,這個操作會阻塞從節點。加載RDB 文件也會阻塞,最終影響的從節點的讀。

切片集群交互時的阻塞

使用 Redis Cluster 作為集群方案,當我們增加實例或刪除實例時,數據會在不同實例進行遷移。一般會是漸進式的遷移,如果遇到bigkey 會阻塞節點

異步執行的演變

綜上所述:我們講到了幾個阻塞點:集合全量查詢和聚合操作、bigkey 刪除、FLUSHDB 和 FLUSHALL 、AOF 日志同步寫,從庫加載RDB 文件。這些都是Redis 的性能的瓶頸,這些也一直困擾著redis 的開發人員。隨著版本的遞進,發生了一些變化。有些已經放在子線程去執行了,就意味著,它并不是 Redis 主線程的關鍵路徑上的操作。

那么什么是主線程的關鍵路徑上的操作:這就是說,客戶端把請求發送給 Redis 后,等著 Redis 返回數據結果的操作,比如獲取數據,進行接下來的業務。

按照這個定義來說的話:集合全量查詢和聚合操作依舊是關鍵路徑上的操作,依舊會阻塞,這個是無法避免的。

bigkey 刪除、FLUSHDB 和 FLUSHALL 并不需要給客戶端返回具體數據結果,不算關鍵路徑上的操作。它們算不算阻塞點呢,這個其實挺復雜的。這個叫做惰性刪除(lazy free),這個功能 Redis 4.0 以后才有的功能,并不是所有的key 都能異步刪除。

關于異步刪除我需要補充幾點,希望大家做個理性的判斷:

  1. lazy-free是4.0新增的功能,但是默認是關閉的,需要手動開啟。

  1. 手動開啟lazy-free時,有4個選項可以控制,分別對應不同場景下,要不要開啟異步釋放內存機制:

    a) lazyfree-lazy-expire:key在過期刪除時嘗試異步釋放內存

    b) lazyfree-lazy-eviction:內存達到maxmemory并設置了淘汰策略時嘗試異步釋放內存

    c) lazyfree-lazy-server-del:執行RENAME/MOVE等命令或需要覆蓋一個key時,刪除舊key嘗試異步釋放內存

    d) replica-lazy-flush:主從全量同步,從庫清空數據庫時異步釋放內存

  2. 即使開啟了lazy-free,如果直接使用DEL命令還是會同步刪除key,只有使用UNLINK命令才會可能異步刪除key 而 FLUSHDB ASYNC、FLUSHALL AYSNC 才會異步清空庫

  3. 這也是最關鍵的一點,上面提到開啟lazy-free的場景,除了replica-lazy-flush之外,其他情況都只是可能去異步釋放key的內存,并不是每次必定異步釋放內存的。 開啟lazy-free后,Redis在釋放一個key的內存時,首先會評估代價,如果釋放內存的代價很小,那么就直接在主線程中操作了,沒必要放到異步線程中執行(不同線程傳遞數據也會有性能消耗)。 什么情況才會真正異步釋放內存?這和key的類型、編碼方式、元素數量都有關系(詳細可參考源碼中的lazyfreeGetFreeEffort函數):

    a) 當Hash/Set底層采用哈希表存儲(非ziplist/int編碼存儲)時,并且元素數量超過64個

    b) 當ZSet底層采用跳表存儲(非ziplist編碼存儲)時,并且元素數量超過64個

    c) 當List鏈表節點數量超過64個(注意,不是元素數量,而是鏈表節點的數量,List的實現是在每個節點包含了若干個元素的數據,這些元素采用ziplist存儲) 只有以上這些情況,在刪除key釋放內存時,才會真正放到異步線程中執行,其他情況一律還是在主線程操作。 也就是說String(不管內存占用多大)、List(少量元素)、Set(int編碼存儲)、Hash/ZSet(ziplist編碼存儲)這些情況下的key在釋放內存時,依舊在主線程中操作。 可見,即使開啟了lazy-free,String類型的bigkey,在刪除時依舊有阻塞主線程的風險。所以,即便Redis提供了lazy-free,我建議還是盡量不要在Redis中存儲bigkey。

個人理解Redis在設計評估釋放內存的代價時,不是看key的內存占用有多少,而是關注釋放內存時的工作量有多大。從上面分析基本能看出,如果需要釋放的內存是連續的,Redis作者認為釋放內存的代價比較低,就放在主線程做。如果釋放的內存不連續(大量指針類型的數據),這個代價就比較高,所以才會放在異步線程中去執行。

所以我雖然在以后的版本刪除有可能是異步刪除,還是不要存儲bigkey,對bigkey 進行刪除。

如果真的要對bigkey 刪除呢,我給你個小建議:先使用集合類型提供的 SCAN 命令讀取數據,然后再進行刪除。因為用 SCAN 命令可以每次只讀取一部分數據并進行刪除,這樣可以避免一次性刪除大量 key 給主線程帶來的阻塞。

例如,對于 Hash 類型的 bigkey 刪除,你可以使用 HSCAN 命令,每次從 Hash 集合中獲取一部分鍵值對(例如 200 個),再使用 HDEL 刪除這些鍵值對,這樣就可以把刪除壓力分攤到多次操作中,那么,每次刪除操作的耗時就不會太長,也就不會阻塞主線程了。

AOF 日志呢,如果 AOF 日志配置成 everysec 選項后,也不會去阻塞,異步執行。

從庫加載RDB 文件 呢,這個在從庫上需要在主線程執行,這個是不能異步的。為了避免阻塞,在這個地方給大家一個建議:從庫加載 RDB 文件:把主庫的數據量大小控制在 2~4GB 左右,以保證 RDB 文件能以較快的速度加載。

redis 異步執行如何實現的

有些操作需要異步執行,redis 主線程通過一個鏈表形式的任務隊列和子線程進行交互,等到后臺子線程從任務隊列中讀取任務進行操作。

好了就講到這些了,其他的希望大家補充

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/715913.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/715913.shtml
英文地址,請注明出處:http://en.pswp.cn/news/715913.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

KubeSphere平臺安裝系列之三【Linux多節點部署KubeSphere】(3/3)

**《KubeSphere平臺安裝系列》** 【Kubernetes上安裝KubeSphere(親測–實操完整版)】(1/3) 【Linux單節點部署KubeSphere】(2/3) 【Linux多節點部署KubeSphere】(3/3) **《KubeS…

一句話講清楚數據庫中事務的隔離級別(通俗易懂版)

為什么我只說通俗易懂版不說嚴謹版? 因為嚴謹版遍地都是, 但是他們卻有一個缺點就是讓人看得云里霧里, 所以這就是我寫通俗易懂版的初衷! 但是既然是通俗易懂版就必然有缺陷, 只為了各位在開發過程中頭腦更加清晰, 如有錯誤還望兄弟們不吝賜教! 在MySQL數據庫中,事務一共有4…

C語言之strcmp函數,strlen函數

strcmp函數是比較兩個字符串ASCII大小的函數。 比較方式是自左向右比較&#xff0c;直到出現不同字符或者\0為止 語法格式 strcmp(字符串1,字符串2&#xff09; 如果兩個字符串相同&#xff0c;會返回數值0 如果字符串1>字符串2,會返回一個正數 如果字符串1<字符串2…

新一代電話機器人開源PHP源代碼

使用easyswoole 框架開發的 新一代電話機器人開源PHP源碼 項目地址&#xff1a;https://gitee.com/ddrjcode/robotphp 代理商頁面演示地址 http://119.23.229.15:8080 用戶名&#xff1a;c0508 密碼&#xff1a;123456 包含 AI外呼管理&#xff0c;話術管理&#xff0c;CR…

每日一題 — 復寫零

1089. 復寫零 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 首先找到最后一個復寫的數&#xff1a; 雙指針算法&#xff1a; 1、先判斷 cur 位置上的值 2、然后決定 dest 移動一步還是兩步 3、然后判斷 dest 是否到終點了 4、最后 cur 處理越界的情況 arr[n-1] …

使用sourceCompatibility = 11不匹配解決方法

運行springbootgradle項目報錯。 原因&#xff1a;在生產該項目時&#xff0c;選擇的JDK是11版本的&#xff0c;但是本地電腦只安裝了1.8版本。不兼容所以報錯。 解決辦法&#xff1a; 找到build.gradle配置文件—>找到sourceCompatibility ‘11’—>把11改成自己本地…

思維題(藍橋杯 填空題 C++)

目錄 題目一&#xff1a; ?編輯 代碼&#xff1a; 題目二&#xff1a; 代碼&#xff1a; 題目三&#xff1a; 代碼&#xff1a; 題目四&#xff1a; 代碼&#xff1a; 題目五&#xff1a; 代碼&#xff1a; 題目六&#xff1a; 代碼七&#xff1a; 題目八&#x…

用python和pygame庫實現刮刮樂游戲

用python和pygame庫實現刮刮樂游戲 首先&#xff0c;確保你已經安裝了pygame庫。如果沒有安裝&#xff0c;可以通過以下命令安裝&#xff1a; pip install pygame 示例有兩個。 一、簡單刮刮樂游戲 準備兩張圖片&#xff0c;一張作為背景bottom_image.png&#xff0c;一張作…

Leetcoder Day35| 動態規劃part02

62.不同路徑 一個機器人位于一個 m x n 網格的左上角 &#xff08;起始點在下圖中標記為 “Start” &#xff09;。 機器人每次只能向下或者向右移動一步。機器人試圖達到網格的右下角&#xff08;在下圖中標記為 “Finish” &#xff09;。 問總共有多少條不同的路徑&#xff…

如何在Linux配置C、C++、Go語言的編譯環境?

在 Linux 系統上配置 C、C、Go 語言的編譯環境可以通過安裝相應的編譯器和相關工具來實現。以下是在 Linux 系統上配置這些語言的編譯環境的一般步驟&#xff1a; 1. C 和 C 編譯環境配置&#xff1a; 安裝 GCC 編譯器&#xff08;一般 Linux 發行版都會包含&#xff09;&…

Android 顯示系統框架

一.FrameBuffer FrameBuffer 介紹&#xff1a; FrameBuffer中文譯名為幀緩沖驅動&#xff0c;它是出現在2.2.xx內核中的一種驅動程序接口。主設備號為29&#xff0c;次設備號遞增。 Linux抽象出FrameBuffer這個設備來供用戶態進程實現直接寫屏。FrameBuffer機制模仿顯卡的功能…

Day11:信息打點-Web應用企業產權指紋識別域名資產網絡空間威脅情報

目錄 Web信息收集工具 業務資產-應用類型分類 Web單域名獲取-接口查詢 Web子域名獲取-解析枚舉 Web架構資產-平臺指紋識別 思維導圖 章節知識點&#xff1a; Web&#xff1a;語言/CMS/中間件/數據庫/系統/WAF等 系統&#xff1a;操作系統/端口服務/網絡環境/防火墻等 應用…

dart中的事件隊列與微任務

dart在每個事件循環中&#xff0c;會先執行同步任務代碼&#xff0c;然后分別檢查兩個任務隊列&#xff1a;微任務隊列和事件隊列。dart總是先執行微任務隊列中的代碼&#xff0c;然后才是事件隊列中的代碼。當兩個隊列中的任務都執行完成后&#xff0c;線程進入休眠狀態&#…

Stable Diffusion WebUI API http://127.0.0.1:7860/docs空白

在嘗試調用Stable Diffusion WebUI API的時候&#xff0c;打開http://127.0.0.1:7860/docs遇到了以下頁面 網絡診斷是這樣的原因&#xff1a; 修bug&#xff0c;改來改去遇到了以下兩種頁面&#xff1a; 此時http://127.0.0.1:7860可以如下正常顯示&#xff1a; 查資料的時候找…

vue+springboot項目部署服務器

項目倉庫&#xff1a;vuespringboot-demo: vuespringboot增刪改查的demo (gitee.com) ①vue中修改配置 在public文件夾下新建config.json文件&#xff1a; {"serverUrl": "http://localhost:9090"//這里localhost在打包后記得修改為服務器公網ip } 然后…

[NSSCTF 2nd] web復現

1.php簽到 <?phpfunction waf($filename){$black_list array("ph", "htaccess", "ini");$ext pathinfo($filename, PATHINFO_EXTENSION);foreach ($black_list as $value) {if (stristr($ext, $value)){return false;}}return true; }if(i…

nginx 配置瀏覽器不緩存文件 每次都會從服務器 請求新的文件

目錄 解決問題方法說明 測試html環境js環境第一步然后修改內容 打開帶有js緩存的頁面強制刷新 配置nginx 每次打開頁面都會重新請求index.js 文件重啟nginx再次修改index.js 總結設置為全局 解決問題 適用于實時更新數據的&#xff0c;網頁 可以讓用戶每次都是重新請求&#x…

C語言中的套娃——函數遞歸

目錄 一、什么是遞歸 1.1.遞歸的思想 1.2.遞歸的限制條件 二、舉例體會 2.1.求n的階乘 2.2.順序打印整數的每一位 2.3.斐波那契數列 三、遞歸與迭代 一、什么是遞歸 在學習C語言的過程中&#xff0c;我們經常會跟遞歸打交道&#xff0c;什么是遞歸呢&#xff1f;它其實…

LNMP 架構

環境準備&#xff1a;lnmp 需要安裝 nginx mysql php 論壇/博客 軟件 使用LNMP架構搭建 論壇 1. 關閉防火墻和和核心防護 systemctl disable --now firewalld setenforce 0 2. 編譯安裝 nginx 安裝依賴包 yum -y install pcre-devel zlib-devel gcc gcc-c make 創建…

在Redhat 7 Linux上安裝llama.cpp [ 錯誤stdatomic.h: No such file or directory]

前期準備 在github上下載llama.cpp或克隆。 GitHub - ggerganov/llama.cpp: LLM inference in C/C ? git clone https://github.com/ggerganov/llama.cpp.gitcd llama.cpp 執行make命令編譯llama.cpp make 在huggingface里下載量化了的 gguf格式的llama2模型。 https:/…