03:小功能大用处
03:小功能大用處
文章目錄
- 03:小功能大用處
- 1、本章知識
- 1.1 本章內容
- 1.2 重點
- 2、慢查詢分析
- 2.1 慢查詢的兩個配置參數
- 2.2 慢查詢最佳實踐
- 3、Redis Shell
- 3.1 redis-cli常用命令
- 3.2 redis-cli命令總結
- 3.2 redis-benchmark命令
- 3.3 Pipeline
- 3.3.1 執行過程
- 3.3.2 Pipeline性能測試
- 3.3.3 原生批量命令與Pipeline對比
- 3.3.4 最佳實踐
- 3.4 Redis與Lua
- 3.5 Bitmaps
- 3.5.1 數據結構模型
- 3.5.2 命令
- 3.5.3 Bitmaps分析
- 3.6 HyperLogLog
- 3.6.1 HyperLogLog命令
- 3.7 發布訂閱
- 3.7.1 命令
- 3.8 GEO(地理信息定位)
1、本章知識
1.1 本章內容
- 慢查詢分析:通過慢查詢分析,找到有問題的命令進行優化。
- Redis Shell:功能強大的Redis Shell會有意想不到的實用功能。
- Pipeline:通過Pipeline(管道或者流水線)機制有效提高客戶端性能。
- 事務與Lua:制作自己的專屬原子命令。
- Bitmaps:通過在字符串數據結構上使用位操作,有效節省內存,為開 發提供新的思路。
- HyperLogLog:一種基于概率的新算法,難以想象地節省內存空間。
- 發布訂閱:基于發布訂閱模式的消息通信機制。
- GEO:Redis3.2提供了基于地理位置信息的功能。
1.2 重點
- 1)慢查詢中的兩個重要參數slowlog-log-slower-than和slowlog-max-len。
- 2)慢查詢不包含命令網絡傳輸和排隊時間。
- 3)有必要將慢查詢定期存放。
- 4)redis-cli一些重要的選項,例如–latency、–-bigkeys、-i和-r組合。
- 5)redis-benchmark的使用方法和重要參數。
- 6)Pipeline可以有效減少RTT次數,但每次Pipeline的命令數量不能無節制。
- 7)Redis可以使用Lua腳本創造出原子、高效、自定義命令組合。
- 8)Redis執行Lua腳本有兩種方法:eval和evalsha。
- 9)Bitmaps可以用來做獨立用戶統計,有效節省內存。
- 10)Bitmaps中setbit一個大的偏移量,由于申請大量內存會導致阻塞。
- 11)HyperLogLog雖然在統計獨立總量時存在一定的誤差,但是節省的內存量十分驚人。
2、慢查詢分析
所謂慢查詢日志就是系統在命令執行前后計算每條命令的執行時間,當超過預設閥值,就將這條命令的相關信息(例如:發生時 間,耗時,命令的詳細信息)記錄下來,Redis也提供了類似的功能。
Redis客戶端執行一條命令分為如下4個部分:
- 一條客戶端命令的生命周期:
- 發送命令
- 命令排隊
- 命令執行
- 返回結果
需要注意,慢查詢只統計(命令執行)的時間,所以沒有慢查詢并不代表客戶端沒有超時問題。
2.1 慢查詢的兩個配置參數
- 對于慢查詢功能,需要明確兩件事:
- 預設閥值怎么設置?
- 慢查詢記錄存放在哪?
Redis提供了slowlog-log-slower-than和slowlog-max-len配置來解決這兩個問題。
- slowlog-log-slower-than
從字面意思就可以看出,slowlog-log-slower-than就是那個預設閥值, 它的單位是微秒(1秒=1000毫秒=1000000微秒),默認值是10000即10毫秒,假如執 行了一條很慢的命令(例如keys*),如果它的執行時間超過了10000微秒,那么它將被記錄在慢查詢日志中。
注意:如slowlog-log-slower-than = 0會記錄所有的命令,slowlog-log-slower-than < 0對于任何命令都不會進行記錄。
- slowlog-max-len
從字面意思看,只是說明了慢查詢日志最多存儲多少條,并沒有說明存放在哪里?
實際上Redis使用了一個列表來存儲慢查詢日志,slowlog-max-len就是列表的最大長度。
一個新的命令滿足慢查詢條件時 被插入到這個列表中,當慢查詢日志列表已處于其最大長度時,最早插入的 一個命令將從列表中移出,例如slowlog-max-len設置為5,當有第6條慢查詢插入的話,那么隊頭的第一條數據就出列,第6條慢查詢就會入列。
- 在Redis中有兩種修改配置的方法:
- 一種是修改配置文件
- 另一種是使用config set命令動態修改
下面使用config set命令將slowlog-log-slower- than設置為20000微秒,slowlog-max-len設置為256
命令行查看修改配置文件項config get | config set | config rewrite
192.168.49.171:6392> config get slow* 1) "slowlog-log-slower-than" 2) "10000" 3) "slowlog-max-len" 4) "128" 192.168.49.171:6392> config set slowlog-log-slower-than 20000 OK 192.168.49.171:6392> config set slowlog-max-len 256 OK 192.168.49.171:6392> config rewrite OK 192.168.49.171:6392> config get slow* 1) "slowlog-log-slower-than" 2) "20000" 3) "slowlog-max-len" 4) "256" 192.168.49.171:6392>要Redis將配置持久化到本地配置文件,需要執行config rewrite命令,config rewrite命令重寫配置文件
192.168.49.171:6392> slowlog get 1) 1) (integer) 02) (integer) 15740618273) (integer) 148324) 1) "DEL"2) "baobao" 192.168.49.171:6392> slowlog len (integer) 1 192.168.49.171:6392>- 慢查詢日志數據結構
- 那么記錄的中的1)2)3)4)分別表示什么呢?
- 1)表示日志唯一標識符uid
- 2)命令執行時系統的時間戳
- 3)命令執行的時長,以微妙來計算
- 4)命令和命令的參數
2.2 慢查詢最佳實踐
- 慢查詢功能可以有效地幫助我們找到Redis可能存在的瓶頸,但在實際 使用過程中要注意以下幾點:
- slowlog-max-len配置建議:線上建議調大慢查詢列表,記錄慢查詢時 Redis會對長命令做截斷操作,并不會占用大量內存。增大慢查詢列表可以減緩慢查詢被剔除的可能,例如線上可設置為1000以上。
- slowlog-log-slower-than配置建議:默認值超過10毫秒判定為慢查詢,需要根據Redis并發量調整該值。由于Redis采用單線程響應命令,對于高流量的場景,如果命令執行時間在1毫秒以上,那么Redis最多可支撐OPS不到 1000。因此對于高OPS場景的Redis建議設置為1毫秒。
- 慢查詢只記錄命令執行時間,并不包括命令排隊和網絡傳輸時間。因此客戶端執行命令的時間會大于命令實際執行時間。因為命令執行排隊機制,慢查詢會導致其他命令級聯阻塞,因此當客戶端出現請求超時,需要檢 查該時間點是否有對應的慢查詢,從而分析出是否為慢查詢導致的命令級聯阻塞。
- 由于慢查詢日志是一個先進先出的隊列,也就是說如果慢查詢比較多 的情況下,可能會丟失部分慢查詢命令,為了防止這種情況發生,可以定期執行slow get命令將慢查詢日志持久化到其他存儲中(例如MySQL),然后 可以制作可視化界面進行查詢。
3、Redis Shell
Redis提供了redis-cli、redis-server、redis-benchmark等Shell工具。
| redis-server | 啟動redis |
| redis-cli | redis命令行工具 |
| redis-benchmark | 基準測試工具 |
| redis-check-aof | AOF持久化文件檢測工具和修復工具 |
| redis-check-dump | RDB持久化文件檢測工具和修復工具 |
| redis-sentinel | 啟動redis-sentinel |
3.1 redis-cli常用命令
1. -r(repeat)選項代表將命令執行多次,例如下面操作將會執行三次ping命令:[root@manager01 ~]# redis-cli -r 3 -h 192.168.49.171 -p 6393 ping PONG PONG PONG2.-i(interval)選項代表每隔幾秒執行一次命令,但是-i選項必須和-r選項一起使用,下面的操作會每隔1秒執行一次ping命令,一共執行5次:[root@manager01 ~]# redis-cli -h 192.168.49.171 -p 6393 -r 5 -i 1 ping PONG PONG PONG PONG PONG注意-i的單位是秒,不支持毫秒為單位,但是如果想以每隔10毫秒執行一次,可以用-i 0.01,例如:[root@manager01 ~]# redis-cli -h 192.168.49.171 -p 6393 -r 5 -i 0.01 ping PONG PONG PONG PONG PONG例如下面的操作利用-r和-i選項,每隔1秒輸出內存的使用量,一共輸出5次:[root@manager01 ~]# redis-cli -h 192.168.49.171 -p 6393 -r 5 -i 1 info |grep used_memory_human used_memory_human:40.34M used_memory_human:40.34M used_memory_human:40.34M used_memory_human:40.31M used_memory_human:40.34M3.-x選項代表從標準輸入(stdin)讀取數據作為redis-cli的***一個參數,例如下面的操作會將字符串world作為set hello的值:[root@manager01 ~]# echo "world" | redis-cli -h 192.168.49.171 -p 6393 -x set hello OK[root@manager01 ~]# redis-cli -h 192.168.49.171 -p 6393 get hello "world\n"[root@manager01 ~]# echo -n "world_1" | redis-cli -h 192.168.49.171 -p 6393 -x set hello_1 --因為echo命令是默認帶有回車\n的,不帶回車需要echo –n命令:從標準輸入讀入一個參數到redis,就不會有回車符. OK [root@manager01 ~]# redis-cli -h 192.168.49.171 -p 6393 get hello_1 "world_1" [root@manager01 ~]# 4.-c(cluster)選項是連接Redis Cluster節點時需要使用的,-c選項可以防止moved和ask異常。[root@manager01 ~]# redis-cli -h 192.168.49.173 -p 6379 -c 192.168.49.173:6379> info cluster # Cluster cluster_enabled:1 192.168.49.173:6379> 5.-a 如果Redis配置了密碼,可以用-a(auth)選項,有了這個選項就不需要手動輸入auth命令。6.--scan選項和--pattern選項用于掃描指定模式的鍵,相當于使用scan命令。7.--slave選項是把當前客戶端模擬成當前Redis節點的從節點,可以用來獲取當前Redis節點的更新操作,有關于Redis復制將在第6章進行詳細介紹。合理的利用這個選項可以記錄當前連接Redis節點的一些更新操作,這些更新操作很可能是實際開發業務時需要的數據。下面開啟***個客戶端,使用--slave選項,看到同步已完成: $ redis-cli --slave SYNC with master, discarding 72 bytes of bulk transfer... SYNC done. Logging commands from master. 再開啟另一個客戶端做一些更新操作: redis-cli 127.0.0.1:6379> set hello world OK 127.0.0.1:6379> set a b OK 127.0.0.1:6379> incr count 1 127.0.0.1:6379> get hello "world" ***個客戶端會收到Redis節點的更新操作: redis-cli --slave SYNC with master, discarding 72 bytes of bulk transfer... SYNC done. Logging commands from master. "PING" "PING" "PING" "PING" "PING" "SELECT","0" "set","hello","world" "set","a","b" "PING" "incr","count" PING命令是由于主從復制產生的,第6章會對主從復制進行介紹。8.--rdb選項會請求Redis實例生成并發送RDB持久化文件,保存在本地。可使用它做持久化文件的定期備份。9.--pipe選項用于將命令封裝成Redis通信協議定義的數據格式,批量發送給Redis執行: [root@manager01 ~]# echo -en "PING\r\n SET w3ckey redis\r\nGET w3ckey\r\nINCR visitor\r\nINCR visitor\r\nINCR visitor\r\n" | redis-cli -h 192.168.49.171 -p 6392 PONG OK "redis" (integer) 1 (integer) 2 (integer) 310、--bigkeys統計bigkey的分布,使用scan命令對redis的鍵進行采樣,從中找到內存占用比較大的鍵,這些鍵可能是系統的瓶頸。11、--eval用于執行lua腳本12、--latency [root@manager01 ~]# redis-cli -h 192.168.49.171 -p 6393 --latency min: 0, max: 4, avg: 0.30 (3757 samples)有三個選項,--latency、--latency-history、--latency-dist。它們可檢測網絡延遲,展現的形式不同。13、--stat 可實時獲取redis的重要統計信息。info命令雖然比較全,但這里可看到一些增加的數據,如requests(每秒請求數)14、--raw 和 --no-raw --no-raw 要求返回原始格式。--raw 顯示格式化的效果。 [root@manager01 ~]# redis-cli -h 192.168.49.171 -p 6393 --stat ------- data ------ --------------------- load -------------------- - child - keys mem clients blocked requests connections 8 40.31M 8 0 2798512 ( 0) 19198 8 40.35M 8 0 2798517 ( 5) 19198 8 40.35M 8 0 2798525 ( 8) 191983.2 redis-cli命令總結
- 字符串命令
- 修改字符串操作命令
- 修改數字值操作命令
- 位圖命令
- 列表命令
- 其他列表命令
- 哈希命令
*其他哈希命令
- 集合命令
集合并交差運算操作命令
*有序集合命令
- 其他有序集合命令
- 發布訂閱命令
- 連接操作相關的命令
- 服務端相關命令
與客戶端連接、獲取命令信息相關的命令
與配置文件、磁盤操作相關的命令
其他命令
- 腳本命令
- 對KEY操作的命令
- Hyperloglog命令
- 地理空間命令
- 事務命令
- 集群命令
3.2 redis-benchmark命令
redis-benchmark可以為Redis做基準性能測試,它提供了很多選項幫助開 發和運維人員測試Redis的相關性能
| 1 | -h | 指定服務器主機名 | 127.0.0.1 |
| 2 | -p | 指定服務器端口 | 6379 |
| 3 | -s | 指定服務器 socket | |
| 4 | -c | 指定并發連接數 | 50 |
| 5 | -n | 指定請求數 | 10000 |
| 6 | -d | 以字節的形式指定 SET/GET 值的數據大小 | 2 |
| 7 | -k | 1=keep alive 0=reconnect | 1 |
| 8 | -r | SET/GET/INCR 使用隨機 key, SADD 使用隨機值 | |
| 9 | -P | 通過管道傳輸 請求 | 1 |
| 10 | -q | 強制退出 redis。僅顯示 query/sec 值 | |
| 11 | –csv | 以 CSV 格式輸出 | |
| 12 | -l | 生成循環,永久執行測試 | |
| 13 | -t | 僅運行以逗號分隔的測試命令列表。 | |
| 14 | -I | Idle 模式。僅打開 N 個 idle 連接并等待。 |
3.3 Pipeline
3.3.1 執行過程
Redis客戶端執行一條命令分為如下四個過程:
1)發送命令 2)命令排隊 3)命令執行 4)返回結果 ;其中1) 4)稱為Round Trip Time(RTT,往返時間)。
Redis提供了批量操作命令(例如mget、mset等),有效地節約RTT。但 大部分命令是不支持批量操作的,例如要執行n次hgetall命令,并沒有 mhgetall命令存在,需要消耗n次RTT。Redis的客戶端和服務端可能部署在不 同的機器上。例如客戶端在北京,Redis服務端在上海,兩地直線距離約為1300公里,那么1次RTT時間=1300×2/(300000×2/3)=13毫秒(光在真空中 傳輸速度為每秒30萬公里,這里假設光纖為光速的2/3),那么客戶端在1秒 內大約只能執行80次左右的命令,這個和Redis的高并發高吞吐特性背道而馳。
Pipeline(流水線)機制能改善上面這類問題,它能將一組Redis命令進行組裝,通過一次RTT傳輸給Redis,再將這組Redis命令的執行結果按順序返回給客戶端,為沒有使用Pipeline執行了n條命令,整個過程需要n次 RTT
redis-cli的–pipe選項實際上就是使用Pipeline機制,例如下面操作將set hello world和incr counter兩條命令組裝
[root@manager01 ~]# echo -en '*3\r\n$3\r\nSET\r\n$5\r\nhello\r\n$5\r\nworld\r\n*2\r\n$4\r\nincr\r\n$7\r\ncounter\r\n' | redis-cli -h 192.168.49.171 -p 6393 --pipe All data transferred. Waiting for the last reply... Last reply received from server. errors: 0, replies: 2 [root@manager01 ~]#3.3.2 Pipeline性能測試
- 給出了在不同網絡環境下非Pipeline和Pipeline執行10000次set操作 的效果,可以得到如下兩個結論:
- Pipeline執行速度一般比逐條執行要快。
- 客戶端和服務端的網絡延時越大,Pipeline的效果越明顯。
在不同網絡下,10000條set非Pipeline和Pipeline的執行時間對比
3.3.3 原生批量命令與Pipeline對比
- Pipeline模擬出批量操作的效果與原生批量命令的區別:
- 原生批量命令是原子的,Pipeline是非原子的。
- 原生批量命令是一個命令對應多個key,Pipeline支持多個命令。
- 原生批量命令是Redis服務端支持實現的,而Pipeline需要服務端和客戶端的共同實現。
3.3.4 最佳實踐
Pipeline雖然好用,但是每次Pipeline組裝的命令個數不能沒有節制,否 則一次組裝Pipeline數據量過大,一方面會增加客戶端的等待時間,另一方 面會造成一定的網絡阻塞,可以將一次包含大量命令的Pipeline拆分成多次 較小的Pipeline來完成。
Pipeline只能操作一個Redis實例,但是即使在分布式Redis場景中,也可 以作為批量操作的重要優化手段
3.4 Redis與Lua
3.5 Bitmaps
3.5.1 數據結構模型
在我們平時的開發過程中,會有-些 bool 型數據需要存取,比如用戶1年的簽到記錄,簽了是1,沒簽是0,要記錄 365 天。如果使用普通的 key/value ,每個用戶
要記錄 365 個,當用戶數上億的時候,需要的存儲空間是驚人的。
為了解決這個問題, Redis 提供了位圖數據結構,這樣每天的簽到記錄只占據一個位, 365 天就是 365 個位, 46 個字節(一個稍長一點的字符串)就可以完全容納下,
這就大大節約了存儲空間。位圖的最小單位是比特(bit ),每個 bit 的取值只能是0或1
3.5.2 命令
- 設置值setbit key offset value
設置鍵的第offset個位的值(從0算起),假設現在有20個用戶, userid=0,5,11,15,19的用戶對網站進行了訪問,那么當前Bitmaps初始化結果如圖所示。
如果此時有一個userid=50的用戶訪問了網站,那么Bitmaps的結構變成了下圖,第20位~49位都是0(補零)。
- Bitmaps間的運算
bitop是一個復合操作,它可以做多個Bitmaps的and(交集)、or(并 集)、not(非)、xor(異或)操作并將結果保存在destkey中
利用bitop and命令計算兩天都訪問網站的用戶
3.5.3 Bitmaps分析
假設網站有1億用戶,每天獨立訪問的用戶有5千萬,如果每天用集合類型和Bitmaps分別存儲活躍用戶可以得到下表
3.6 HyperLogLog
先思考一個常見的業務問題:如果你負責開發維護一個大型的網站,有一天老板技產品經理要網站上每個網頁每天的 UV 數據,然后讓你來開發這個統計模塊,你會如何實現?
如果統計 PV ,那非常好辦,給每個網頁配一個獨立的 Redis 計數器就可以了,把這個計數器的 key 后綴加上當天的日期。這樣來一個請求,執行 incrby 指令一次,最終就可以統計出所有的 PV 數據。
但是 UV 不一樣,它要去重,同一個用戶一天之內的多次訪問請求只能計數一次。這就要求每一個網頁請求都需要帶上用戶的 ID ,無論是登錄用戶還是未登錄用戶都需要一個唯一 來標識。
你也許已經想到了一個簡單的方案,那就是為每一個頁面設置一個獨立的 set集合來存儲所有當天訪問過此頁面的用戶囚。當一個請求過來時,我們使用 sadd 將用戶ID 塞進去就可以了。通過 scard 可以取出這個集合的大小,這個數字就是這個頁
面的 UV 數據。沒錯,這是一個非常簡單的可行方案。
但是,如果你的頁面訪問量非常大,比如一個爆款頁面可能有幾千萬個 UV ,你就需要一個很大的 set 集合來統計,這就非常浪費空間。如果這樣的頁面很多,那所需要的存儲空間是驚人的。為這樣一個去重功能就耗費這樣多的存儲空間,值得嗎?
其實老板所需要的數據并不需要太精確, 105 萬和 106 萬這兩個數字對于老板來說并沒有多大區別。那么,有沒有更好的解決方案呢?
3.6.1 HyperLogLog命令
HyperLogLog并不是一種新的數據結構(實際類型為字符串類型),而 是一種基數算法,通過HyperLogLog可以利用極小的內存空間完成獨立總數的統計,數據集可以是IP、Email、ID等。
- HyperLogLog提供了3個命令:pfmerge
- pfadd 增加計數
- pfcount 計算獨立用戶數
- pfmerge 可以求出多個HyperLogLog的并集并賦值給destkey
集合類型和HyperLogLog占用空間對比
HyperLogLog內存占用量非常小,但是存在錯誤率
- 結構選型時只需要確認如下兩條即可:
- 只為了計算獨立總數,不需要獲取單條數據。
- 可以容忍一定誤差率,畢竟HyperLogLog在內存的占用量上有很大的優勢。
3.7 發布訂閱
Redis提供了基于“發布/訂閱”模式的消息機制,此種模式下,消息發布 者和訂閱者不進行直接通信,發布者客戶端向指定的頻道(channel)發布消 息,訂閱該頻道的每個客戶端都可以收到該消息
3.7.1 命令
1.發布消息 publish channel message
2.訂閱消息 subscribe channel [channel …]
3.取消訂閱 unsubscribe [channel [channel …]]
4.按照模式訂閱和取消訂閱 psubscribe pattern [pattern…] punsubscribe [pattern [pattern …]]
5.查詢訂閱
(1)查看活躍的頻道 pubsub channels [pattern]
(2)查看頻道訂閱數 pubsub numsub [channel …]
(3)查看模式訂閱數 pubsub numpat
- 如果有多個客戶端同時訂閱了;有關訂閱命令有兩點需要注意:
- 客戶端在執行訂閱命令之后進入了訂閱狀態,只能接收subscribe、 psubscribe、unsubscribe、punsubscribe的四個命令。
- 新開啟的訂閱客戶端,無法收到該頻道之前的消息,因為Redis不會對 發布的消息進行持久化。
3.8 GEO(地理信息定位)
Redis3.2版本提供了GEO地理信息定位功能, 支持存儲地理位置信息用來實現諸如附近位置、 搖一搖這類依賴于地理位置信息的功能, 對于需要實現這些功能的開發者來說是一大福音。
geoadd key longitude latitude member [longitude latitude member …] 增加地理位置信息
geopos key member 獲取地理位置信息
longitude、 latitude、 member分別是該地理位置的經度、 緯度、 成員, 表3-7展示5個城市的經緯度
geodist key member1 member2 [unit] 獲取兩個地理位置的距離
- 其中unit代表返回結果的單位,包含以下四種:
- m(meters) 代表米。
- km(kilometers) 代表公里。
- mi(miles) 代表英里。
- ft( feet) 代表尺
- 獲取指定位置范圍內的地理信息位置集合
georadius key longitude latitude radiusm|km|ft|mi [withcoord] [withdist] [withhash] [COUNT count] [asc|desc] [store key] [storedist key]
georadiusbymember key member radiusm|km|ft|mi [withcoord] [withdist] [withhash] [COUNT count] [asc|desc] [store key] [storedist key]
georadius和georadiusbymember兩個命令的作用是一樣的, 都是以一個地理位置為中心算出指定半徑內的其他地理信息位置,不同的是georadius命令的中心位置給出了具體的經緯度,georadiusbymember只需給出成員即可。其中radiusm|km|ft|mi是必需參數,指定了半徑(帶單位),
- 這兩個命令有很多可選參數, 如下所示:
- withcoord: 返回結果中包含經緯度。
- withdist: 返回結果中包含離中心節點位置的距離。
- withhash: 返回結果中包含geohash, 有關geohash后面介紹。
- COUNT count: 指定返回結果的數量。
- asc|desc: 返回結果按照離中心節點的距離做升序或者降序。
- store key: 將返回結果的地理位置信息保存到指定鍵。
- storedist key: 將返回結果離中心節點的距離保存到指定鍵
總結
- 上一篇: 沈颖刚:生物柴油或是高原柴油货车污染治理
- 下一篇: 银行业务总揽之-银行对公业务