Redis-⑤解决命中率低的问题

314 阅读3分钟

背景

某银行客户监控告警,Redis命中率低于基准值。

命中率低会影响系统性能。

统计命中率

在 Redis 中,统计命中率(cache hit rate)可以通过分析 Redis 提供的统计信息来完成。具体步骤如下:

  1. 查看 Redis 统计信息:使用 INFO 命令可以获取 Redis 的各种统计信息,包括缓存命中率相关的数据。

  2. 解析相关字段:从 INFO 命令的输出中,我们主要关注两个字段:

    • keyspace_hits:表示键命中的次数。
    • keyspace_misses:表示键未命中的次数。
  3. 计算命中率:命中率可以通过以下公式计算:

image.png

实际操作步骤

  1. 连接 Redis:可以使用 redis-cli 工具或者通过编程语言连接 Redis 实例。
  2. 执行 INFO 命令:获取统计信息。
  3. 计算命中率:解析返回的数据并计算命中率。

使用 redis-cli 示例

> ./redis-cli INFO stats

这条命令会返回类似以下的输出:

> info stats
# Stats
...
keyspace_hits:1000
keyspace_misses:500
...

我们需要提取 keyspace_hitskeyspace_misses 两个值。

使用 Python 代码示例

import redis

# 连接到 Redis
r = redis.StrictRedis(host='localhost', port=6379, db=0)

# 获取统计信息
info = r.info('stats')

# 提取 keyspace_hits 和 keyspace_misses
keyspace_hits = info['keyspace_hits']
keyspace_misses = info['keyspace_misses']

# 计算命中率
if (keyspace_hits + keyspace_misses) > 0:
    hit_rate = (keyspace_hits / (keyspace_hits + keyspace_misses)) * 100
else:
    hit_rate = 0

print(f"Cache Hit Rate: {hit_rate:.2f}%")

通过上述步骤,你可以轻松统计 Redis 的命中率。关键在于使用 INFO 命令获取统计信息,并根据公式计算命中率。这样可以帮助你了解 Redis 缓存的性能,并进行相应的优化。

实例

项目初次启动,会进行缓存预热,观测redis 的命中率,项目启动前:

> info stats
# Stats
...
keyspace_hits:125
keyspace_misses:86
...

项目启动后:

> info stats
# Stats
...
keyspace_hits:187
keyspace_misses:105
...

可见,预热阶段增加了19次未命中。正常情况下,预热应该全部都是往缓存里set值,为什么会出现未命中呢?

记录未命中的命令

检查源代码, 在代码中进行埋点。对Redis的操作,统一都经过RedisCacheUtil, 所以修改它来记录未命中键:

public class RedisCacheUtil {

    public static <T> T hget(String keyString field) throws BizBussinessRuntimeException {
        try {
            Object v = hashOperations.get(key, field);
            if (v == null) {
                log.warn("Redis未命中键:" + key);
            }
            return (T)v;
        } catch (Exception e) {
            log.error("hget(" + key + ") 操作失败!,msg:" + e.getMessage());
            throw new BizBussinessRuntimeException(IErrMsg.ERR_REDIS, "Redis操作失败");
        }
    }

    public static <T> T getObject(String key) throws BizBussinessRuntimeException {
        try {
            Object v = valueOperations.get(key);
            if (v == null) {
                log.warn("Redis未命中键:" + key);
            }
            return (T) v;
        } catch (Exception e) {
            log.error("getObject(" + key + ") 操作失败!,msg:" + e.getMessage());
            throw new BizBussinessRuntimeException(IErrMsg.ERR_REDIS, "Redis操作失败");
        }
    }

    // 其它代码...
}

启动后,查看warn级别的日志, 果然发现了未命中的KEY。

解决

接下来, 打条件断点, 追踪调用链路, 看看是哪里在查缓存

原来是有接口在翻译字典,结果查询字典描述没查到,原因是查询的时候, 没有加租户的条件去查

加上租户参数

再进行测试,发现原来的19次未命中, 变成了1次。

用同样的方法,继续排查,那1次未命中是执行了什么命令,之后也解决掉了。

完成后, 重新启动项目, 持续观察了一段时间,发现缓存未命中次数稳定住了, 不再增加。