Redis:22---客戶端API:client、monitor)

?

一、client list

  • client list命令能列出與Redis服務端相連的所有客戶端連接信息。例如下面代碼是在一個Redis實例上執行client list的結果,其中每一行代表一個客戶端信息:

  • ?

  • 下面將選擇幾個重要的屬性進行說明,其余通過表格的形式進行展示

①標識:id、addr、fd、name

  • 這四個屬性屬于客戶端的標識:

    • id:客戶端連接的唯一標識,這個id是隨著Redis的連接自增的,重啟 Redis后會重置為0

    • addr:客戶端連接的ip和端口

    • fd:socket的文件描述符,與lsof命令結果中的fd是同一個,如果fd=-1代表當前客戶端不是外部客戶端,而是Redis內部的偽裝客戶端

    • name:客戶端的名字,后面的client setName和client getName兩個命令會對其進行說明

②輸入緩沖區:qbuf、qbuf-free

  • Redis為每個客戶端分配了輸入緩沖區,它的作用是:將客戶端發送的命令臨時保存,同時Redis從會輸入緩沖區拉取命令并執行,輸入緩沖區為客 戶端發送命令到Redis執行命令提供了緩沖功能,如下圖所示

  • client list中qbuf和qbuf-free:

    • 這兩個屬性分別代表這個緩沖區的總容量和剩余容量

    • Redis沒有提供相應的配置來規定每個緩沖區的大小,輸入緩沖區會根據輸入內容大小的不同動態調整,只是要求每個客戶端緩沖區的大小不能超過1G,超過后客戶端將被關閉

    • 下面是Redis源碼中對于輸入緩沖區的硬編碼:

/* Protocol and I/O related defines */
#define REDIS_MAX_QUERYBUF_LEN (1024*1024*1024) /* 1GB max query buffer. */
  • 輸入緩沖使用不當會產生兩個問題:

    • 一旦某個客戶端的輸入緩沖區超過1G,客戶端將會被關閉

    • 輸入緩沖區不受maxmemory控制,假設一個Redis實例設置了maxmemory為4G,已經存儲了2G數據,但是如果此時輸入緩沖區使用了3G,已經超過maxmemory限制,可能會產生數據丟失、鍵值淘汰、OOM等情況(如下圖所示)

  • 上圖的執行效果如下:

  • 上面已經看到,輸入緩沖區使用不當造成的危害非常大,那么造成輸入緩沖區過大的原因有哪些?

    • 輸入緩沖區過大主要是因為Redis的處理速度跟不上輸入緩沖區的輸入速度,并且每次進入輸入緩沖區的命令包含了大量 bigkey,從而造成了輸入緩沖區過大的情況

    • 還有一種情況就是Redis發生了阻塞,短期內不能處理命令,造成客戶端輸入的命令積壓在了輸入緩沖區, 造成了輸入緩沖區過大

  • 那么如何快速發現和監控呢?監控輸入緩沖區異常的方法有兩種:

    • 通過定期執行client list命令,收集qbuf和qbuf-free找到異常的連接記錄 并分析,最終找到可能出問題的客戶端。

    • 通過info命令的info clients模塊,找到最大的輸入緩沖區,例如下面命令中的其中client_recent_max_input_buffer代表最大的輸入緩沖區,例如可以設置超過10M就進行報警

  • 上面兩種方法各有自己的優劣勢,下圖對兩種方法進行了對比:

  • 運維提示:輸入緩沖區問題出現概率比較低,但是也要做好防范,在開發中要減少bigkey、減少Redis阻塞、合理的監控報警

③輸出緩沖區:obl、oll、omem

  • Redis為每個客戶端分配了輸出緩沖區,它的作用是:保存命令執行的結 果返回給客戶端,為Redis和客戶端交互返回結果提供緩沖

  • 與輸入緩沖區不同的是:

    • 輸出緩沖區的容量可以通過參數client-outputbuffer-limit來進行設置

    • 并且輸出緩沖區做得更加細致,按照客戶端的不同分為三種:普通客戶端、發布訂閱客戶端、slave客戶端。如下圖所示

  • client-output-buffer-limit格式如下。參數意義為:

    • <class>:客戶端類型,分為三種。a)normal:普通客戶端;b) slave:slave客戶端,用于復制;c)pubsub:發布訂閱客戶端

    • <hard limit>:如果客戶端使用的輸出緩沖區大于該值,客戶端會被立即關閉

    • <soft limit>和<soft seconds>:如果客戶端使用的輸出緩沖區超過了并且持續了秒,客戶端會被立即關閉

  • Redis的默認配置是:

client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
  • 和輸入緩沖區相同的是,輸出緩沖區也不會受到maxmemory的限制,如果使用不當同樣會造成maxmemory用滿產生的數據丟失、鍵值淘汰、OOM等情況

  • 實際上輸出緩沖區由兩部分組成:

    • 固定緩沖區(16KB):返回比較小的執行結果

    • 動態緩沖區:返回比較大的結果。例如大的字符串、hgetall、smembers命令的結果等,通過Redis源碼中redis.h的redisClient結構體(Redis3.2版本變為Client)可以看到兩個緩沖區的實現細節:

typedef struct redisClient {
// 動態緩沖區列表
list *reply;
// 動態緩沖區列表的長度(對象個數)
unsigned long reply_bytes;
// 固定緩沖區已經使用的字節數
int bufpos;
// 字節數組作為固定緩沖區
char buf[REDIS_REPLY_CHUNK_BYTES];
} redisClient;
  • 固定緩沖區使用的是字節數組,動態緩沖區使用的是列表。當固定緩沖區存滿后會將Redis新的返回結果存放在動態緩沖區的隊列中,隊列中的每個對象就是每個返回結果,如下圖所示:

  • obl、oll、omem:

    • client list中的obl代表固定緩沖區的長度,oll代表動態緩沖區列表的長度,omem代表使用的字節數

    • 例如下面代表當前客戶端的固定緩沖區的長度為0,動態緩沖區有4869個對象,兩個部分共使用了133081288字節=126M 內存:

id=7 addr=127.0.0.1:56358 fd=6 name= age=91 idle=0 flags=O db=0 sub=0 psub=0 multi=-1
qbuf=0 qbuf-free=0 obl=0 oll=4869 omem=133081288 events=rw cmd=monitor
  • 監控輸出緩沖區的方法依然有兩種:

    • ①通過定期執行client list命令,收集obl、oll、omem找到異常的連接記錄 并分析,最終找到可能出問題的客戶端

    • ②通過info命令的info clients模塊,找到輸出緩沖區列表最大對象數,例如(其中,client_longest_output_list代表輸出緩沖區列表最大對象數):

    • 這兩種統計方法的優劣勢和輸入緩沖區是一樣的,這里就不再贅述了

  • 相比于輸入緩沖區,輸出緩沖區出現異常的概率相對會比較大,那么如何預防呢?方法如下:

    • 進行上述監控,設置閥值,超過閥值及時處理

    • 適當增大slave的輸出緩沖區的,如果master節點寫入較大,slave客戶 端的輸出緩沖區可能會比較大,一旦slave客戶端連接因為輸出緩沖區溢出 被kill,會造成復制重連

    • 限制容易讓輸出緩沖區增大的命令,例如,高并發下的monitor命令就 是一個危險的命令

    • 及時監控內存,一旦發現內存抖動頻繁,可能就是輸出緩沖區過大

    • 限制普通客戶端輸出緩沖區的,把錯誤扼殺在搖籃中,例如可以進行如下設置:

client-output-buffer-limit normal 20mb 10mb 120

④客戶端的存活狀態(age、idle)

  • client list中的age和idle分別代表:當前客戶端已經連接的時間、最近一次的空閑時間:

  • 例如下面這條記錄代表當期客戶端連接Redis的時間為304秒,其中空閑了0秒:

  • 例如下面這條記錄代表當期客戶端連接Redis的時間為8888581秒,其中空閑了8888581秒。實際上這種就屬于不太正常的情況,當age等于idle時, 說明連接一直處于空閑狀態

演示案例

  • 為了更加直觀地描述age和idle,下面用一個例子進行說明:

String key = "hello";
// 1) 生成jedis,并執行get操作
Jedis jedis = new Jedis("127.0.0.1", 6379);
System.out.println(jedis.get(key));
// 2) 休息10秒
TimeUnit.SECONDS.sleep(10);
// 3) 執行新的操作ping
System.out.println(jedis.ping());
// 4) 休息5秒
TimeUnit.SECONDS.sleep(5);
// 5) 關閉jedis連接
jedis.close();
  • 下面對代碼中的每一步進行分析,用client list命令來觀察age和idle參數的相應變化(備注:為了與redis-cli的客戶端區分,本次測試客戶端IP地址:10.7.40.98)

  • 1)在執行代碼之前,client list只有一個客戶端,也就是當前的rediscli,下面為了節省篇幅忽略掉這個客戶端。

  • 2)使用Jedis生成了一個新的連接,并執行get操作,可以看到IP地址為 10.7.40.98的客戶端,最后執行的命令是get,age和idle分別是1秒和0秒

  • 3)休息10秒,此時Jedis客戶端并沒有關閉,所以age和idle一直在遞 增:

  • 4)執行新的操作ping,發現執行后age依然在增加,而idle從0計算,也 就是不再閑置

  • 5)休息5秒,觀察age和idle增加:

  • 6)關閉Jedis,Jedis連接已經消失:

⑤客戶端類型(flag)

  • client list中的flag是用于標識當前客戶端的類型

  • 例如flag=S代表當前客 戶端是slave客戶端、flag=N代表當前是普通客戶端,flag=O代表當前客戶端 正在執行monitor命令。下圖列出了11種客戶端類型:

  • 序號客戶端類型說明
    lN普通客戶端
    2M當前客戶端是master節點
    3s當前客戶端是slave節點
    4o當前客戶端正在執行monitor命令
    5x當前客戶端正在執行事務
    6b當前客戶端正在等得阻塞事件
    7i當前客戶端正在等待VM IO,但是此狀態目前已經廢棄不用
    8d一個受監視的鍵已被修改,EXEC命令將失敷
    9u客戶端未被阻察
    10c回復完整輸出后,關閉連接
    11A盡可能快地關閉連接?

二、client setName和client getName

client setName xx

client setName

  • client setName用于給客戶端設置名字,這樣比較容易標識出客戶端的來源。例如將當前客戶端命名為test_client,可以執行如下操作:

  • 此時再執行client list命令,就可以看到當前客戶端的name屬性為test_client:

client getName

client getName
  • 如果想直接查看當前客戶端的name,可以使用client getName命令

  • 第一次進入客戶端時,客戶端是沒有名字的,因此名字為空

  • 更改名字之后,就可以看到更改后的名字了。例如:

  • client getName和setName命令可以做為標識客戶端來源的一種方式,但是通常來講,在Redis只有一個應用方使用的情況下,IP和端口作為標識會更加清晰。當多個應用方共同使用一個Redis,那么此時client setName可以作為標識客戶端的一個依據

三、client kill

client kill ip:port
  • 此命令用于殺掉指定IP地址和端口的客戶端

  • 由于一些原因(例如設置timeout=0時產生的長時間idle的客戶端),需要手動殺掉客戶端連接時,可以使用client kill命令

演示案例

  • 例如左側為一個客戶端(127.0.0.1:34658),右側為一個客戶端(127.0.0.1:34660)

  • 如果想殺掉127.0.0.1:34656的客戶端,可以執行:

  • 執行命令后,client list結果只剩下了127.0.0.1:34658自己這個客戶端:

四、client pause

client pause timeout(毫秒)
  • client pause命令用于阻塞客戶端timeout毫秒數,在此期間客戶端連接將被阻塞。如下圖所示:

演示案例

  • 例如在一個客戶端執行下面的命令,在之后的10000毫秒內的其他客戶端連接都會被阻塞

  • 過一會后在另一個客戶端執行ping命令,發現整個ping命令執行了2.40秒(手動執行redis-cli,只為了演示,不代表真實執行時間):

  • 該命令可以在如下場景起到作用:

    • client pause只對普通和發布訂閱客戶端有效,對于主從復制(從節點內部偽裝了一個客戶端)是無效的,也就是此期間主從復制是正常進行的, 所以此命令可以用來讓主從復制保持一致

    • client pause可以用一種可控的方式將客戶端連接從一個Redis節點切換到另一個Redis節點

  • 需要注意的是在生產環境中,暫停客戶端成本非常高

五、monitor

  • monitor命令用于監控Redis正在執行的命令?

演示案例

  • 如下圖所示:

    • 我們打開了兩個redis-cli,右側先執行monitor命令,左側再執行其他命令

    • 可以看到monitor命令能夠監聽其他客戶端正在執行的命令,并記錄了詳細的時間戳

  • 注意事項:monitor的作用很明顯,如果開發和運維人員想監聽Redis正在執行的命令,就可以用monitor命令,但事實并非如此美好,每個客戶端都有自己的輸出緩沖區,既然monitor能監聽到所有的命令,一旦Redis的并發量過大, monitor客戶端的輸出緩沖會暴漲,可能瞬間會占用大量內存。

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

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

相關文章

leetcode175. 組合兩個表(SQL)

表1: Person ---------------------- | 列名 | 類型 | ---------------------- | PersonId | int | | FirstName | varchar | | LastName | varchar | ---------------------- PersonId 是上表主鍵 表2: Address ---------------------- | 列名 …

Redis:20---常用功能之(發布與訂閱)

一、發布與訂閱概述Redis提供了基于“發布/訂閱”模式的消息機制&#xff0c;此種模式下&#xff0c;消息發布者和訂閱者不進行直接通信&#xff0c;發布者客戶端向指定的頻道&#xff08;channel&#xff09;發布消息&#xff0c;訂閱該頻道的每個客戶端都可以收到該消息。 下…

Redis:19---常用功能之(HyperLogLog)

一、HyperLogLog概述HyperLogLog并不是一種新的數據結構&#xff08;實際類型為字符串類型&#xff09;&#xff0c;而是一種基數算法&#xff0c;通過HyperLogLog可以利用極小的內存空間完成獨立總數的統計&#xff0c;數據集可以是IP、Email、ID等HyperLogLog提供了3個命令&a…

計算時間差的Oracle函數

Oracle函數可以實現諸多的功能&#xff0c;下面就介紹使用Oracle函數計算時間差的實現方法&#xff0c;如果您對Oracle函數方面感興趣的話&#xff0c;不妨一看。 兩個Date類型字段&#xff1a;START_DATE&#xff0c;END_DATE&#xff0c;計算這兩個日期的時間差&#xff08;分…

leetcode181. 超過經理收入的員工(SQL)

Employee 表包含所有員工&#xff0c;他們的經理也屬于員工。每個員工都有一個 Id&#xff0c;此外還有一列對應員工的經理的 Id。 ------------------------------ | Id | Name | Salary | ManagerId | ------------------------------ | 1 | Joe | 70000 | 3 |…

UNIX(多線程):01---線程簡介及線程限制

一、線程的概念 典型的UNIX進程可以看成只有一個控制線程:一個進程在同一時刻只做一件事。有了多個控制線程后,在程序設計時可以把進程設計成在同一時刻能夠做不止一件事,每個線程處理各自獨立的任務二、線程的優點 通過為每種事件類型分配單獨的處理線程,可以簡化處理異步…

UNIX(多線程):13---condition_variable、wait、notify_one、notify_all

條件變量std::condition_variable、wait()、notify_one() 線程A: 等待一個條件滿足線程B: 專門往消息隊列中放入消息(數據),達到一定條件,通知處于等待中的線程A。std::condition_variable實際上是一個類,是一個和條件相關的一個類,說白了就是等待一個條件達成。這個類是…

leetcode176. 第二高的薪水(SQL)

編寫一個 SQL 查詢&#xff0c;獲取 Employee 表中第二高的薪水&#xff08;Salary&#xff09; 。 ------------ | Id | Salary | ------------ | 1 | 100 | | 2 | 200 | | 3 | 300 | ------------ 例如上述 Employee 表&#xff0c;SQL查詢應該返回 200 作為第二…

UNIX(多線程):17---異步任務提供者(Provider) 介紹

std::promise 類概述 Promise 對象可以保存某一類型 T 的值,該值可被 future 對象讀取(可能在另外一個線程中),因此 promise 也提供了一種線程同步的手段。在 promise 對象構造時可以和一個共享狀態(通常是std::future)相關聯,并可以在相關聯的共享狀態(std::future)上保…

leetcode180. 連續出現的數字(SQL)

編寫一個 SQL 查詢&#xff0c;查找所有至少連續出現三次的數字。 --------- | Id | Num | --------- | 1 | 1 | | 2 | 1 | | 3 | 1 | | 4 | 2 | | 5 | 1 | | 6 | 2 | | 7 | 2 | --------- 例如&#xff0c;給定上面的 Logs 表&#xff0c; 1 是唯一連續…

UNIX(多線程):18---異步任務提供者(Provider) 介紹(續)

本文主要介紹 std::packaged_task。 std::packaged_task 包裝一個可調用的對象,并且允許異步獲取該可調用對象產生的結果,從包裝可調用對象意義上來講,std::packaged_task 與 std::function 類似,只不過 std::packaged_task 將其包裝的可調用對象的執行結果傳遞給一個 std:…

leetcode182. 查找重復的電子郵箱(SQL)

編寫一個 SQL 查詢&#xff0c;查找 Person 表中所有重復的電子郵箱。 示例&#xff1a; ------------- | Id | Email | ------------- | 1 | ab.com | | 2 | cd.com | | 3 | ab.com | ------------- 根據以上輸入&#xff0c;你的查詢應返回以下結果&#xff1a; ----…

UNIX(多線程):19---Future 類型詳解

Future 類型詳解 本文主要介紹 std::future,std::shared_future 以及 std::future_error,另外還會介紹 <future> 頭文件中的 std::async,std::future_category 函數以及相關枚舉類型。 std::future 詳解 std::future 概述 前面已經多次提到過 std::future,那么 std::…

UNIX(多線程):20---生產者消費者實例

本文將綜合運用 C++11 中的新的基礎設施(主要是多線程、鎖、條件變量)來闡述一個經典問題——生產者消費者模型,并給出完整的解決方案。 生產者消費者問題是多線程并發中一個非常經典的問題,相信學過操作系統課程的同學都清楚這個問題的根源。本文將就四種情況分析并介紹生產…

leetcode183. 從不訂購的客戶(SQL)

某網站包含兩個表&#xff0c;Customers 表和 Orders 表。編寫一個 SQL 查詢&#xff0c;找出所有從不訂購任何東西的客戶。 Customers 表&#xff1a; ----------- | Id | Name | ----------- | 1 | Joe | | 2 | Henry | | 3 | Sam | | 4 | Max | ----------- Or…

UNIX(多線程):21---線程池實現原理

線程池簡介: 線程過多會帶來調度開銷,進而影響緩存局部性和整體性能。 而線程池維護著多個線程,等待著監督管理者分配可并發執行的任務。這避免了在處理短時間任務時創建與銷毀線程的代價。線程池不僅能夠保證內核的充分利用,還能防止過分調度。可用線程數量應該取決于可用…

leetcode197. 上升的溫度(SQL)

給定一個 Weather 表&#xff0c;編寫一個 SQL 查詢&#xff0c;來查找與之前&#xff08;昨天的&#xff09;日期相比溫度更高的所有日期的 Id。 --------------------------------------------- | Id(INT) | RecordDate(DATE) | Temperature(INT) | -----------------------…

UNIX(多線程):22---幾種常見的線程池

常見線程池 1、newSingleThreadExecutor 創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。 從構造方法來看,它可以單獨執行,也可以與周期線程池結合用。其任務隊列是LinkedBlockingQueue,這是個無界的阻塞…

js正則表達式限制文本框只能輸入數字,小數點,英文字母

1.文本框只能輸入數字代碼(小數點也不能輸入)<input οnkeyup"this.valuethis.value.replace(/\D/g,)" onafterpaste"this.valuethis.value.replace(/\D/g,)"> 2.只能輸入數字,能輸小數點. <input οnkeyup"if(isNaN(value))execCommand(und…

UNIX(多線程):23---線程池注意事項和常見問題

使用線程池的注意事項 死鎖任何多線程程序都有死鎖的風險,最簡單的情形是兩個線程AB,A持有鎖1,請求鎖2,B持有鎖2,請求鎖1。(這種情況在mysql的排他鎖也會出現,不會數據庫會直接報錯提示)。線程池中還有另一種死鎖:假設線程池中的所有工作線程都在執行各自任務時被阻塞…