广告位 · 页头(后台-模块管理-新建模块后替换此区域)

Redis调用慢如何用SkyWalking做APM链路追踪?

在微服务系统中,Redis 承担缓存、分布式锁、计数器等多种角色,一次 HTTP 请求往往伴随多次 Redis 读写。当接口响应变慢时,仅凭应用日志很难判断是 Redis 命令本身变慢、连接池等待还是网络抖动,更难以把某次 Redis 操作和上游请求对应起来。SkyWalking 提供了一套分布式追踪能力,通过探针自动捕获 Redis 客户端调用,将 Redis 操作作为链路中的一个 Span 展示出来,帮助开发人员按请求维度观察缓存访问耗时、目标节点和具体命令。下面从探针原理、接入方式以及排障方法三个方面展开。

Redis调用慢如何用SkyWalking做APM链路追踪?

SkyWalking 识别 Redis 调用的底层机制

SkyWalking 的 Java Agent 基于字节码增强技术,在应用启动时通过 javaagent 参数挂载探针。探针并不修改业务源码,而是对 Redis 客户端类库中的关键方法进行拦截。以 Jedis 为例,客户端执行 set、get、hget 等命令时,最终会走到连接发送和响应解析的方法。SkyWalking 的 jedis-plugin 在这些方法进入时创建一个 Exit Span,在方法返回或抛出异常时结束 Span,并记录耗时、命令类型、目标地址和异常信息。

对于 Lettuce 客户端,由于其异步和非阻塞特性,探针的拦截点会绑定在 RedisCommand 的发送阶段以及 RedisFuture 完成阶段。Lettuce 插件通常会在命令发出时创建 Span,在 Netty 响应完成或回调触发时结束 Span。这样做的好处是即使客户端使用异步 API,也能准确统计从发出命令到收到响应的完整时间。Redisson 作为基于 Lettuce 或 Netty 的高级客户端,SkyWalking 也提供了对应识别逻辑,会将 Redisson 的调用映射到底层 Redis 命令,并在 Span 中保留高层 API 的方法名,方便业务人员理解。

Span 的标签是后续排障的基础。SkyWalking 默认会记录 db.type 为 Redis,db.statement 为命令原文,db.instance 为数据库索引,peer 为 Redis 节点地址。这些信息会进入 OAP 服务端存储,并在 Trace 详情中展示。需要注意的是,命令原文中可能包含敏感数据,例如缓存用户信息时的 key 前缀。如果担心泄露,可以通过插件的配置项对 db.statement 进行截断或脱敏。

Java 应用中接入 Redis 链路追踪的步骤

接入过程对现有代码侵入很小。首先下载 SkyWalking Java Agent,并在应用启动脚本中增加 javaagent 参数。下面的启动脚本指定了应用名和 OAP 后端地址,其中 agent 服务名会作为拓扑图中的节点名称。

java -javaagent:/opt/skywalking-agent/skywalking-agent.jar \
     -Dskywalking.agent.service_name=order-service \
     -Dskywalking.collector.backend_service=127.0.0.1:11800 \
     -jar order-service.jar

如果项目中同时使用 Jedis 和 Lettuce,SkyWalking 会自动根据类路径中的客户端类型加载对应插件,不需要业务代码手动集成。比如在 Spring Boot 中使用 Lettuce 连接 Redis,只需要保持原来的 RedisTemplate 使用方式,Trace 中就会自动出现 Redis Span。接入后可以先启动应用并发送一次请求,在 SkyWalking UI 的 Trace 列表中查看是否出现 redis 相关 Span。

有些场景下需要把 Trace ID 或自定义业务字段写入日志,方便在 ELK 或日志平台中关联。SkyWalking 提供了 apm-toolkit-trace 依赖,可以通过 TraceContext 获取当前 Trace ID 或添加关联字段。下面的示例展示在读取缓存后记录当前 key 到 Trace 上下文,便于在 SkyWalking 页面中按 key 检索。

import org.apache.skywalking.apm.toolkit.trace.TraceContext;
public class CacheService {
    private final RedisTemplate<String, String> redisTemplate;
    public CacheService(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    public String getValue(String key) {
        String value = redisTemplate.opsForValue().get(key);
        TraceContext.putCorrelation("cache.key", key);
        return value;
    }
}

除了自动采集,还可以通过插件配置调整采集粒度。SkyWalking 支持在 agent.config 中配置忽略某些 Redis 命令,或者设置 Span 的最大属性长度。例如高频的 ping 或 exists 命令如果不希望产生大量 Span,可以在插件配置中排除。采样率也需要根据流量评估,过高会消耗较多存储和 CPU,过低则可能漏掉偶发慢请求。对于 Redis 排障场景,建议对写操作和读操作采用相同采样策略,避免只保留成功请求而丢失异常链路。

结合 Trace 数据定位 Redis 慢查询和链路瓶颈

当一个接口响应时间突然升高,可以先在 SkyWalking 的拓扑图中观察调用链上各个节点耗时占比。如果 Redis 节点显示为慢节点,点击进入对应的 Trace 记录。Trace 详情中会列出 Redis Span 的命令、耗时、目标实例和发生时间。通过对比多次请求的 Redis Span 耗时,可以判断是某个 key 的读取突然变慢,还是连接池排队导致整体延迟上升。

例如某次排查中发现 get 命令 Span 耗时只有 8 毫秒,但 hgetall 命令 Span 耗时达到 300 毫秒以上。进一步查看 db.statement 发现某个 hash key 包含几十万字段,属于典型的大 Key 问题。此时可以结合 Redis 服务器上的 slowlog 进一步确认。Redis 慢日志配置如下,通过阈值记录执行时间超过 10 毫秒的命令。

slowlog-log-slower-than 10000
slowlog-max-len 128

将 SkyWalking Span 中记录的时间戳、命令和耗时与 Redis 慢日志进行比对,可以验证慢命令是否只影响特定请求,还是所有请求都会命中。如果 Span 显示只有特定用户或特定 key 的请求变慢,通常是数据倾斜或缓存设计问题;如果所有请求都变慢,则需要检查 Redis 服务器负载、内存碎片以及网络链路。

连接池等待是另一种典型问题。Lettuce 默认使用共享连接,当并发量超过连接池上限时,获取连接的等待时间会被记录到 Span 的前置耗时中。SkyWalking 展示的 Redis Span 耗时包含获取连接、发送命令和接收响应三部分。如果整体耗时远高于命令本身执行时间,可以重点查看客户端连接池配置和 Redis 服务端的 maxclients 参数。通过调整连接池大小或增加 Redis 实例,可以缓解排队问题。

Redis 链路追踪的常见误区和调优建议

很多人认为接入 SkyWalking 后 Redis 调用会自动出现完整入参和返回值,这是不准确的。SkyWalking 记录的是命令语句和基础元数据,并不会默认保存 Redis 返回的全部内容。这样做一方面是为了控制 Span 体积,另一方面是避免敏感数据进入链路存储。如果确实需要保留返回值片段,可以通过 apm-toolkit 的 ActiveSpan 接口自定义标签,但要控制标签大小,否则会拖慢应用并增大 OAP 存储压力。

另一个常见误区是关闭异常 Span 采集。有些团队为了减少告警,只保留成功请求的 Trace,这会导致 Redis 超时、连接池耗尽等异常场景无法追踪。正确的做法是保持异常链路全量采集,并对正常请求采用较低采样率。SkyWalking 支持动态修改采样率,OAP 会通过配置下发到 Agent,无需重启应用。这样既能保留问题现场,又能控制整体数据量。

在日常调优中,可以定期查看 Redis Span 的平均耗时和 P99 耗时变化。如果发现 P99 明显高于平均值,说明存在少量慢命令或偶发网络抖动。此时可以结合 Trace ID 在业务日志中检索对应请求,查看当时是否有大量并发写入、缓存续期操作或 Lua 脚本执行。对于 Lua 脚本调用,SkyWalking 记录的是 EVAL 命令,耗时可能包含整个脚本执行过程,需要进一步拆分脚本内部的 Redis 子命令才能精确分析。

最后要注意 Agent 版本、Redis 客户端版本和 SkyWalking OAP 版本的兼容性。不同版本对 Lettuce 异步回调的拦截实现有所不同,升级客户端后如果发现 Redis Span 丢失或耗时统计异常,可以先检查插件列表和日志。保持 Agent 与 OAP 同版本是稳定的基础,同时可以根据实际流量调整 Span 属性长度、命令排除规则和存储周期,让 Redis 链路追踪真正成为定位缓存问题的有效工具。

广告位 · 文章底部(后台-模块管理替换此区域)