redis是目前最流行的 NoSQL 內(nèi)存數(shù)據(jù)庫擦耀,然而如果在使用過程中出現(xiàn)濫用骚秦、亂用的情況碍舍,很容易發(fā)生性能問題,此時(shí)我們就要去關(guān)注慢查詢?nèi)罩厩海疚膰L試給大家介紹一種通過 elastic stack
來快速分析 redis 慢查詢?nèi)罩镜姆椒ㄠ戮粒M芙o大家提供幫助。
redis slowlog簡介
redis是目前最流行的緩存系統(tǒng)跃脊,因其豐富的數(shù)據(jù)結(jié)構(gòu)和良好的性能表現(xiàn)宇挫,被各大公司廣泛使用。盡管redis性能極佳酪术,但若不注意使用方法器瘪,極容易出現(xiàn)慢查詢,慢查詢多了或者一個(gè)20s的慢查詢會導(dǎo)致操作隊(duì)列(redis是單進(jìn)程)堵塞绘雁,最終引起雪崩甚至整個(gè)服務(wù)不可用橡疼。對于慢查詢語句,redis提供了相關(guān)的配置和命令庐舟。
配置有兩個(gè):slowlog-log-slower-than
和 slowlog-max-len
欣除。slowlog-log-slower-than
是指當(dāng)命令執(zhí)行時(shí)間(不包括排隊(duì)時(shí)間)超過該時(shí)間時(shí)會被記錄下來,單位為微秒
挪略,比如通過下面的命令历帚,就可以記錄執(zhí)行時(shí)長超過20ms
的命令了。
config set slowlog-log-slower-than 20000
slowlog-max-len
是指redis可以記錄的慢查詢命令的總數(shù)杠娱,比如通過下面的命令挽牢,就可以記錄最近100條慢查詢命令了。
config set slowlog-max-len 100
操作慢查詢的命令有兩個(gè):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"
上面返回了兩個(gè)慢查詢命令,其中每行的含義如下:
- 第一行是一個(gè)慢查詢id。該id是自增的奏赘,只有在 redis server 重啟時(shí)該id才會重置寥闪。
- 第二行是慢查詢命令執(zhí)行的時(shí)間戳
- 第三行是慢查詢命令執(zhí)行耗時(shí),單位為微秒
- 第四行是慢查詢命令的具體內(nèi)容磨淌。
slowlog reset
命令是清空慢日志隊(duì)列疲憋。
elastic stack
elastic stack是elastic公司的一系列軟件產(chǎn)品,包括elasticsearch梁只、kibana缚柳、logstash、beats等搪锣,感興趣的可以去官網(wǎng)看各個(gè)產(chǎn)品的詳細(xì)介紹秋忙,此次不再做詳細(xì)的講解。本次分析過程中构舟,我們會用到elasticsearch
灰追、kibana
和beats
三款產(chǎn)品。elasticsearch
用來存儲解析后的redis slowlog狗超,kibana
用于圖形化分析弹澎,beats
用于收集redis slowlog。
這里著重講一下beats
努咐,它是一系列輕量級的數(shù)據(jù)收集產(chǎn)品統(tǒng)稱苦蒿,目前官方提供了filebeat
、packetbeat
渗稍、heartbeat
佩迟、metricbeat
等,可以用來收集日志文件竿屹、網(wǎng)絡(luò)包报强、心跳包、各類指標(biāo)數(shù)據(jù)等拱燃。像我們這次要收集的redis slowlog
秉溉,官方還沒有提供相關(guān)工具,需要我們自己實(shí)現(xiàn)扼雏,但借助beats
的一系列腳手架工具坚嗜,我們可以方便快速的創(chuàng)建自己的rsbeat
---redis slowlog beat
夯膀。
rsbeat原理簡介
接下來我們先講解一下rsbeat
的實(shí)現(xiàn)原理诗充,一圖勝千言,我們先來看下它的工作流诱建。
我們由下往上分析:
- 最下面是我們要分析的redis server列表
- 再往上便是
rsbeat
蝴蜓,它會與這些redis server建立連接并定期去拉取slowlog
。 - 在啟動時(shí),
rsbeat
會發(fā)送下面的命令到每一臺redis server
茎匠,來完成slowlog的配置格仲,這里設(shè)置記錄最近執(zhí)行時(shí)長超過20ms
的500條命令。
config set slowlog-log-slower-than 20000
config set slowlog-max-len 500
slowlog reset
- 然后
rsbeat
會定時(shí)去拉取每臺redis server
的慢查詢命令
slowlog get 500
slowlog reset
注意之類slowlog reset
是因?yàn)榇舜我呀?jīng)將所有的慢日志都取出了诵冒,下次獲取時(shí)取最新生成的凯肋,防止重復(fù)計(jì)算。
-
rsbeat
將解析的慢日志發(fā)布到elasticsearch
中進(jìn)行存儲 - 通過
kibana
進(jìn)行slowlog的圖形化分析
rsbeat
的整個(gè)工作流到這里已經(jīng)介紹完畢了汽馋,是不是很簡單呢侮东?下面我們來簡單看一下rsbeat
的核心代碼實(shí)現(xiàn)。
rsbeat核心代碼講解
rsbeat
已經(jīng)在github上開源了豹芯,感興趣的同學(xué)可以自己去下下來使用悄雅。下面我們分析的代碼位于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
初始化時(shí)進(jìn)行的操作宽闲,這里也就是發(fā)送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
方法實(shí)現(xiàn)了定時(shí)從redis server
拉取最新的slowlog
列表,并將它們轉(zhuǎn)化為elasticsearch
中可以存儲的數(shù)據(jù)后我碟,發(fā)布到elasticsearch
中放案。這里重點(diǎn)說下每一個(gè)字段的含義:
- @timestamp是指當(dāng)前時(shí)間戳。
- @log_timestamp是指慢日志命令執(zhí)行的時(shí)間戳矫俺。
- slow_id是該慢日志的id吱殉。
- cmd是指執(zhí)行的 redis 命令,比如
sadd
厘托、scard
等等友雳。 - key是指redis key的名稱
- args是指 redis 命令的其他參數(shù),通過
cmd
铅匹、key
押赊、args
我們可以完整還原執(zhí)行的redis命令。 - duration是指redis命令執(zhí)行的具體時(shí)長包斑,單位是微秒流礁。
- ip_port是指發(fā)生命令的 redis server 地址。
有了這些字段罗丰,我們就可以用kibana
來愉快的進(jìn)行可視化數(shù)據(jù)分析了神帅。
Kibana圖形化分析slowlog
Kibana
提供了非常方便的數(shù)據(jù)分析操作,這里不展開解釋了萌抵,感興趣的可以自行去學(xué)習(xí)找御,這里直接上圖元镀,看下最終的分析結(jié)果。
上圖可以看到最近有一個(gè)很明顯的數(shù)量減少霎桅,原因是我們解決了相關(guān)的慢查詢栖疑。
看完上面的截圖,有沒有心動滔驶,想親自操刀試一下遇革?Kibana
操作起來非常簡單,尤其對于程序員來講揭糕,使用起來得心應(yīng)手澳淑。趕緊下載rsbeat下來自己試一下吧!
總結(jié)
隨著 elastic stack 的發(fā)展插佛,其使用門檻越來越低杠巡,我認(rèn)為目前所有的有志于做數(shù)據(jù)分析的工程師都應(yīng)該了解和掌握它的用法。有了它的幫助雇寇,你可以以極快的速度搭建起自己的一套免費(fèi)強(qiáng)大的數(shù)據(jù)分析軟件氢拥,它的優(yōu)點(diǎn)包括但不限于下面提到的:
- 數(shù)據(jù)源任意且自定制。只要你能將數(shù)據(jù)讀取出來并存儲到
elasticsearch
中即可分析锨侯。 - 支持海量數(shù)據(jù)分析嫩海。得益于
elastic
多年的迅猛發(fā)展,其產(chǎn)品已經(jīng)非常成熟囚痴,上TB的數(shù)據(jù)都可以輕松應(yīng)對存儲與分析叁怪。有了它,你就可以舍棄數(shù)據(jù)一多就卡頓的excel
了深滚。 - 強(qiáng)大的開源社區(qū)支持奕谭。
elastic
產(chǎn)品的迅猛發(fā)展離不開開源社區(qū)的支持,你只要在社區(qū)中提出自己的問題或者需求痴荐,總會有人即時(shí)給你答復(fù)和建議血柳。如果你有一定的開發(fā)能力,那么完全可以按照自己的想法來折騰生兆。
別再看了难捌,趕緊去自己動手實(shí)踐下吧!