redis 内存分析工具 `rma4go`

redis 内存分析工具 rma4go

redis是一个很有名的内存型数据库,这里不做详细介绍。而rma4go (redis memory analyzer for golang) 是一个redis的内存分析工具,这个工具的主要作用是针对运行时期的redis进行内存的分析,统计redis中key的分布情况, 各种数据类型的使用情况,key的size,大key的数量及分布, key的过期状况分布等一些有助于定位redis使用问题的工具,希望这能够给应用开发者提供便利排查生产中所遇到的实际问题。

rma4go的应用场景

redis是目前很流行的一个内存型数据库,很多企业都在使用。 但由于业界并没有很多对于redis使用上的规范,或者是有一些规范并没有被很好的遵循, 存在很多redis使用上的问题,我这边就列举一些例子:

  1. redis 存用满了, 不知道key的分布情况,不知道来源于那个应用
  2. redis 被block了,不知道什么原因导致的block,是哪个应用里的什么key的操作导致的
  3. 想迁移redis数据,或者调整一些设置,但不知道要不要对redis里的数据进行保留,以及不知道什么业务在使用等
  4. redis的key的过期情况不明朗, 不知道哪些东西可以删除或者调整
    其实上面的一些问题是我随便列举出来的一些,并不是所有的存在的问题,相信也有很多其他场景同样会用到这样的一个redis内存分析工具rma4go

rma4go的具体功能

数据维度

对于key的分析我们这个工具会提供如下几个维度的数据:

  • key的数量分布维度
  • key的过期分布维度
  • key的类型分布维度
  • key对应的的数据的大小分布维度
  • key的前缀分布维度
  • 慢key与大key的维度

当然以后如果发现有更好的纬度也会添加进去,目前先以这几个纬度为主

数据类型设计

type RedisStat struct {
   
	All     KeyStat `json:"all"`
	String  KeyStat `json:"string"`
	Hash    KeyStat `json:"hash"`
	Set     KeyStat `json:"set"`
	List    KeyStat `json:"list"`
	ZSet    KeyStat `json:"zset"`
	Other   KeyStat `json:"other"`
	BigKeys KeyStat `json:"bigKeys"`
}

// distributions of keys of all prefixes
type Distribution struct {
   
	KeyPattern string `json:"pattern"`
	Metrics
}

// basic metrics of a group of key
type Metrics struct {
   
	KeyCount       int64 `json:"keyCount"`
	KeySize        int64 `json:"keySize"`
	DataSize       int64 `json:"dataSize"`
	KeyNeverExpire int64 `json:"neverExpire"`
	ExpireInHour   int64 `json:"expireInHour"`  // >= 0h < 1h
	ExpireInDay    int64 `json:"expireInDay"`   // >= 1h < 24h
	ExpireInWeek   int64 `json:"expireInWeek"`  // >= 1d < 7d
	ExpireOutWeek  int64 `json:"expireOutWeek"` // >= 7d
}

实现细节

key元信息
type KeyMeta struct {
	Key      string
	KeySize  int64
	DataSize int64
	Ttl      int64
	Type     string
}

众所周知, redis里的所有的数据基本都是由key的, 也是根据key进行操作的,那么对redis里的key进行分析我们必须要记录下来这个key的信息才可以做到, 我们能记录的信息正如以上结构中的一样, key本身, key的大小, 数据的大小, 过期时间以及key的类型。这些信息是我们对key进行分析的一个基础信息,都可以通过一些简单的redis命令就可以取到。

遍历redis所有key

要对一个redis进行完整的key分析, 我们就需要有办法能够访问到所有key的源信息, 所幸redis提供了 scan这么一种方式可以比较轻量的遍历所有的key,访问到相应的key的元信息。
这样对于redis而言, 进行在线key分析的时候造成的压力也不会非常大,当然key分析不能再QPS高峰期进行, 需要在redis资源余量允许的情况下进行分析。

另外由于redis本身的一个内存清理机制,有25%的过期占用可以在分析key的时候被清理掉, 因此这个分析工具同时兼具了清理一部分内存的作用, 如果redis里面存在过期的而且存在于内存里面的key的话。

对记录的信息进行分析与汇总

有了遍历所有key的方法, 又有了元数据, 剩下的事情就是把这些数据进行聚合汇总, 这个主要是一个算法上的工作,
最难的部分要数这个key聚合的部分了, 这里面有很多取舍, 由于作者我本人不是专攻算法的, 而且没有找到合适的库, 因此只能动手自己想了一种方式。 基本的思路是:

压缩的算法
  1. 对于每个新的key的元信息, 添加到老的key分析对象里去
  2. 对这个key从后往前缩短, 去除尾部,看是否已经包含这个key的统计信息,如果包含, 则把key的信息累加上去, 如果不包含则创建一个新的纪录。
  3. 当记录的个数添加到一定数量的时候, 对对象的个数进行一次压缩
    • 压缩的算法也是从字符串的末尾往字符串首部进行压缩
    • 当压缩不能增加这个pattern 的key的个数的时候使用原来的key(压缩前的key)
    • 当压缩可以增加这个pattern的key的个数的时候,进行key的合并,把pattern设置成压缩后的pattern
    • 当记录的条数超过指定的条数就循环往复,直到压缩到小于指定的条数为止
    • 如果对于key的最小长度(就算再压缩也要保留一两位)有要求, 有一些压缩到字符串的最小长度的参数可以进行调整与设置, 进行一定的取舍。
  4. 直到scan完毕
代码如下
const (
	defaultSize = 128
	compactNum  = 30
	maxLeftNum =  150
	minKeyLenLower = 2
	minKeyLen   = 5
)


func (stat *KeyStat) compact() {
   
	distMap := stat.Distribution
	tmpMap := make(map[string][]string, defaultSize)
	shrinkTo := compactNum
	for k := range distMap {
   
		compactedKey := k
		
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值