redis是目前最流行的 NoSQL 內存數據庫,然而如果在使用過程中出現濫用、亂用的情況,很容易發生性能問題,此時我們就要去關注慢查詢日志,本文嘗試給大家介紹一種通過 elastic stack
來快速分析 redis 慢查詢日志的方法,希望能給大家提供幫助。
redis slowlog簡介
redis是目前最流行的緩存系統,因其豐富的數據結構和良好的性能表現,被各大公司廣泛使用。盡管redis性能極佳,但若不注意使用方法,極容易出現慢查詢,慢查詢多了或者一個20s的慢查詢會導致操作隊列(redis是單進程)堵塞,最終引起雪崩甚至整個服務不可用。對于慢查詢語句,redis提供了相關的配置和命令。
配置有兩個:slowlog-log-slower-than
和 slowlog-max-len
。slowlog-log-slower-than
是指當命令執行時間(不包括排隊時間)超過該時間時會被記錄下來,單位為微秒
,比如通過下面的命令,就可以記錄執行時長超過20ms
的命令了。
config set slowlog-log-slower-than 20000
slowlog-max-len
是指redis可以記錄的慢查詢命令的總數,比如通過下面的命令,就可以記錄最近100條慢查詢命令了。
config set slowlog-max-len 100
操作慢查詢的命令有兩個:slowlog get [len]
和 slowlog reset
。slowlog get [len]
命令獲取指定長度的慢查詢列表。
redis 127.0.0.1:6379> slowlog get 2
1) 1) (integer) 14
2) (integer) 1309448221
3) (integer) 15
4) 1) "ping"
2) 1) (integer) 13
2) (integer) 1309448128
3) (integer) 30
4) 1) "slowlog"
2) "get"
3) "100"
上面返回了兩個慢查詢命令,其中每行的含義如下:
- 第一行是一個慢查詢id。該id是自增的,只有在 redis server 重啟時該id才會重置。
- 第二行是慢查詢命令執行的時間戳
- 第三行是慢查詢命令執行耗時,單位為微秒
- 第四行是慢查詢命令的具體內容。
slowlog reset
命令是清空慢日志隊列。
elastic stack
elastic stack是elastic公司的一系列軟件產品,包括elasticsearch、kibana、logstash、beats等,感興趣的可以去官網看各個產品的詳細介紹,此次不再做詳細的講解。本次分析過程中,我們會用到elasticsearch
、kibana
和beats
三款產品。elasticsearch
用來存儲解析后的redis slowlog,kibana
用于圖形化分析,beats
用于收集redis slowlog。
這里著重講一下beats
,它是一系列輕量級的數據收集產品統稱,目前官方提供了filebeat
、packetbeat
、heartbeat
、metricbeat
等,可以用來收集日志文件、網絡包、心跳包、各類指標數據等。像我們這次要收集的redis slowlog
,官方還沒有提供相關工具,需要我們自己實現,但借助beats
的一系列腳手架工具,我們可以方便快速的創建自己的rsbeat
---redis slowlog beat
。
rsbeat原理簡介
接下來我們先講解一下rsbeat
的實現原理,一圖勝千言,我們先來看下它的工作流。
我們由下往上分析:
- 最下面是我們要分析的redis server列表
- 再往上便是
rsbeat
,它會與這些redis server建立連接并定期去拉取slowlog
。 - 在啟動時,
rsbeat
會發送下面的命令到每一臺redis server
,來完成slowlog的配置,這里設置記錄最近執行時長超過20ms
的500條命令。
config set slowlog-log-slower-than 20000
config set slowlog-max-len 500
slowlog reset
- 然后
rsbeat
會定時去拉取每臺redis server
的慢查詢命令
slowlog get 500
slowlog reset
注意之類slowlog reset
是因為此次已經將所有的慢日志都取出了,下次獲取時取最新生成的,防止重復計算。
-
rsbeat
將解析的慢日志發布到elasticsearch
中進行存儲 - 通過
kibana
進行slowlog的圖形化分析
rsbeat
的整個工作流到這里已經介紹完畢了,是不是很簡單呢?下面我們來簡單看一下rsbeat
的核心代碼實現。
rsbeat核心代碼講解
rsbeat
已經在github上開源了,感興趣的同學可以自己去下下來使用。下面我們分析的代碼位于beater/rsbeat.go,這也是rsbeat
的核心文件。
func poolInit(server string, slowerThan int) *redis.Pool {
return &redis.Pool{
MaxIdle: 3,
MaxActive: 3,
IdleTimeout: 240 * time.Second,
Dial: func() (redis.Conn, error) {
c, err := redis.Dial("tcp", server, redis.DialConnectTimeout(3*time.Second), redis.DialReadTimeout(3*time.Second))
if err != nil {
logp.Err("redis: error occurs when connect %v", err.Error())
return nil, err
}
c.Send("MULTI")
c.Send("CONFIG", "SET", "slowlog-log-slower-than", slowerThan)
c.Send("CONFIG", "SET", "slowlog-max-len", 500)
c.Send("SLOWLOG", "RESET")
r, err := c.Do("EXEC")
if err != nil {
logp.Err("redis: error occurs when send config set %v", err.Error())
return nil, err
}
logp.Info("redis: config set %v", r)
return c, err
},
TestOnBorrow: func(c redis.Conn, t time.Time) error {
_, err := c.Do("PING")
logp.Info("redis: PING")
return err
},
}
}
poolInit
方法是rsbeat
初始化時進行的操作,這里也就是發送slowlog
配置的地方,代碼很簡單,就不展開解釋了。
func (bt *Rsbeat) redisc(beatname string, init bool, c redis.Conn, ipPort string) {
defer c.Close()
logp.Info("conn:%v", c)
c.Send("SLOWLOG", "GET")
c.Send("SLOWLOG", "RESET")
logp.Info("redis: slowlog get. slowlog reset")
c.Flush()
reply, err := redis.Values(c.Receive()) // reply from GET
c.Receive() // reply from RESET
logp.Info("reply len: %d", len(reply))
for _, i := range reply {
rp, _ := redis.Values(i, err)
var itemLog itemLog
var args []string
redis.Scan(rp, &itemLog.slowId, &itemLog.timestamp, &itemLog.duration, &args)
argsLen := len(args)
if argsLen >= 1 {
itemLog.cmd = args[0]
}
if argsLen >= 2 {
itemLog.key = args[1]
}
if argsLen >= 3 {
itemLog.args = args[2:]
}
logp.Info("timestamp is: %d", itemLog.timestamp)
t := time.Unix(itemLog.timestamp, 0).UTC()
event := common.MapStr{
"type": beatname,
"@timestamp": common.Time(time.Now()),
"@log_timestamp": common.Time(t),
"slow_id": itemLog.slowId,
"cmd": itemLog.cmd,
"key": itemLog.key,
"args": itemLog.args,
"duration": itemLog.duration,
"ip_port": ipPort,
}
bt.client.PublishEvent(event)
}
}
redisc
方法實現了定時從redis server
拉取最新的slowlog
列表,并將它們轉化為elasticsearch
中可以存儲的數據后,發布到elasticsearch
中。這里重點說下每一個字段的含義:
- @timestamp是指當前時間戳。
- @log_timestamp是指慢日志命令執行的時間戳。
- slow_id是該慢日志的id。
- cmd是指執行的 redis 命令,比如
sadd
、scard
等等。 - key是指redis key的名稱
- args是指 redis 命令的其他參數,通過
cmd
、key
、args
我們可以完整還原執行的redis命令。 - duration是指redis命令執行的具體時長,單位是微秒。
- ip_port是指發生命令的 redis server 地址。
有了這些字段,我們就可以用kibana
來愉快的進行可視化數據分析了。
Kibana圖形化分析slowlog
Kibana
提供了非常方便的數據分析操作,這里不展開解釋了,感興趣的可以自行去學習,這里直接上圖,看下最終的分析結果。
上圖可以看到最近有一個很明顯的數量減少,原因是我們解決了相關的慢查詢。
看完上面的截圖,有沒有心動,想親自操刀試一下?Kibana
操作起來非常簡單,尤其對于程序員來講,使用起來得心應手。趕緊下載rsbeat下來自己試一下吧!
總結
隨著 elastic stack 的發展,其使用門檻越來越低,我認為目前所有的有志于做數據分析的工程師都應該了解和掌握它的用法。有了它的幫助,你可以以極快的速度搭建起自己的一套免費強大的數據分析軟件,它的優點包括但不限于下面提到的:
- 數據源任意且自定制。只要你能將數據讀取出來并存儲到
elasticsearch
中即可分析。 - 支持海量數據分析。得益于
elastic
多年的迅猛發展,其產品已經非常成熟,上TB的數據都可以輕松應對存儲與分析。有了它,你就可以舍棄數據一多就卡頓的excel
了。 - 強大的開源社區支持。
elastic
產品的迅猛發展離不開開源社區的支持,你只要在社區中提出自己的問題或者需求,總會有人即時給你答復和建議。如果你有一定的開發能力,那么完全可以按照自己的想法來折騰。
別再看了,趕緊去自己動手實踐下吧!