首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Valkey 支持 LTTng trace event,可观测性迎来史诗级升级

Valkey 支持 LTTng trace event,可观测性迎来史诗级升级

作者头像
皮振伟
发布2025-08-19 13:01:59
发布2025-08-19 13:01:59
6600
代码可运行
举报
文章被收录于专栏:皮振伟的专栏皮振伟的专栏
运行总次数:0
代码可运行

Redis/Valkey 性能问题分析的难点

Valkey 作为业界最快的 KV 数据库之一,在业界受到广泛使用和好评。在 1KB KV 场景下,单核性能超过 150K QPS,这也树立了良好的口碑。用户对它的预期同样很高 ——“快”,进一步来说是 “稳定的快”,不希望在任何场景下出现延迟抖动。然而,实际使用中往往不如预期:

  • 随着硬件水平的提高,单机上可以部署更多的实例,多个实例之间的影响。可能出现的 CPU 竞争、网卡队列竞争、磁盘 IO 竞争等等。
  • 随着操作系统的复杂度提升,fork 带来的性能损耗、透明大页的潜在性能影响、page cache 回收影响内存分配等等。
  • Valkey 经过多年的发展,自身的代码量也在增加,有些命令天然就是慢了很多(例如 INFO),有些命令带来的隐式影响可能持续(例如 BGSAVE)。
  • Redis/Valkey 天然的单执行线程模型,控制面与数据面未隔离,导致高负载场景下缺少有效手段获取信息。

实际的问题场景还要更加复杂,可能存在上述的场景的组合,甚至更多的问题场景还在这些枚举之外。自 Redis 时代开始,就逐步增强它的可观测性,然而它的覆盖、分析的场景终究比较有限,对于实际的问题解决只能提供有限的帮助。Vakley 的可观测性难在:

  • Valkey 的预期 QPS 很高,trace 机制哪怕 overhead 比较低,都会带来一定的性能影响,哪怕是 printf 打 log 都会是巨大的 overhead。同时,也会产生大量的 trace 日志。
  • Valkey 的预期延迟很低,如果对于每个 command 都用最粗暴的写文件日志的方式,遇到内核的内存回收将会带来不可预期的延迟。

基于这几点,Valkey 需要一个高性能的(overhead 很低的)、不会对 valkey 自身带来性能大幅抖动、可以动态开关的 trace 机制,增强它的可观测性。于是,我(皮振伟 pizhenwei@github)和同事(李智强 artikell@github)一起开发了 Valkey 的 LTTng trace event 机制,并把它贡献到 Valkey 上游。

关于 LTTng

LTTng 是一个开源的系统级跟踪框架,专为 Linux 内核和用户空间应用程序设计,支持低开销、高精度的全系统事件追踪。它通过采用共享内存+RCU(Read-Copy-Update)同步机制,实现低锁竞争的数据写入,同时记录程序执行过程中的上下文信息(如函数调用、系统事件、线程调度等),进程异步回收已满的缓冲区数据,避免阻塞主程序,帮助开发者分析性能瓶颈、调试复杂问题。

Valkey 使用 LTTng 提升可观测性

例如,我们对所有的命令声明如下的格式(LTTng UST 八股文):

代码语言:javascript
代码运行次数:0
运行
复制
LTTNG_UST_TRACEPOINT_EVENT(
        /* Tracepoint provider name */
        valkey_commands,
        /* Tracepoint name */
        command_call,
        /* Input arguments */
        LTTNG_UST_TP_ARGS(
                int, prot,
                const char *, saddr,
                const char *, daddr,
                const char *, name,
                uint64_t, duration
        ),  
        /* Output event fields */
        LTTNG_UST_TP_FIELDS(
                lttng_ust_field_enum(valkey_commands, valkey_conn_type_enum, int, enum_field, prot)
                lttng_ust_field_string(saddr, saddr)
                lttng_ust_field_string(daddr, daddr)
                lttng_ust_field_string(name, name)
                lttng_ust_field_integer(uint64_t, duration, duration)
        )   
)

同时,在 valkey/src/server.c 中嵌入如何的代码:

代码语言:javascript
代码运行次数:0
运行
复制
valkey_commands_trace(valkey_commands, command_call, connGetTypeId(c->conn), getClientPeerId(c), getClientPeerId(c), real_cmd->declared_name, duration);

其中,valkey_commands 是 LTTng trace event 的 provider,command_call 是 tracepoint。通过 lttng 命令行工具执行

代码语言:javascript
代码运行次数:0
运行
复制
 lttng enable-event -u 'valkey_commands:command_call'

即可使能这个事件的追踪。经过实际测试,如果编译打开 LTTng 的支持,运行时每次都会都会执行这个函数,但是 overhead 很低,几乎可以忽略不计,是因为在函数入口的地方需要判断是否真的需要输出,在没有动态打开这个 tracepoint 的情况下,这里仅仅是 test 一个变量就返回。如果通过 lttng 命令动态打开这个 tracepoint,那么将会带来低于~7%的性能损失,然而在实际运行中,valkey 几乎不会跑满整个 CPU,那么这个损耗的比例将会更低,那么这个 overhead 是可以接受的。

同理,还支持了更多的 tracepoint,详情见

代码语言:javascript
代码运行次数:0
运行
复制
valkey/src/trace/README.md
代码语言:javascript
代码运行次数:0
运行
复制
| event                      | provider          |
| -------------------------- | ----------------- |
| command_call               | valkey_commands   |
| rdb_unlink_temp_file       | valkey_bgsave     |
| fork                       | valkey_bgsave     |
| while_blocked_cron         | valkey_server     |
| module_acquire_gil         | valkey_server     |
| command_unblocking         | valkey_server     |
| expire_del                 | valkey_db         |
| active_defrag_cycle        | valkey_db         |
| eviction_del               | valkey_db         |
| eviction_lazyfree          | valkey_db         |
| eviction_cycle             | valkey_db         |
| expire_cycle               | valkey_db         |
| cluster_config_open        | valkey_cluster    |
| cluster_config_write       | valkey_cluster    |
| cluster_config_fsync       | valkey_cluster    |
| cluster_config_rename      | valkey_cluster    |
| cluster_config_dir_fsync   | valkey_cluster    |
| cluster_config_close       | valkey_cluster    |
| cluster_config_unlink      | valkey_cluster    |
| aof_write_pending_fsync    | valkey_aof        |
| aof_write_active_child     | valkey_aof        |
| aof_write_alone            | valkey_aof        |
| aof_write                  | valkey_aof        |
| aof_fsync_always           | valkey_aof        |
| aof_fstat                  | valkey_aof        |
| aof_rename                 | valkey_aof        |

在开发的过程中,我们构造场景尝试效果:valkey-benchmark 多线程测试 valkey-server 的性能,同时另外的一个终端执行 keys 命令,valkey-benchmark 输出:

代码语言:javascript
代码运行次数:0
运行
复制
GET: rps=227428.0 (overall: 222756.2) avg_msec=0.114 (overall: 0.117)
    GET: rps=225248.0 (overall: 223005.2) avg_msec=0.115 (overall: 0.117)
    GET: rps=167474.1 (overall: 217942.2) avg_msec=0.193 (overall: 0.122) --> performance drop
    GET: rps=220192.0 (overall: 218129.5) avg_msec=0.118 (overall: 0.122)
    GET: rps=222868.0 (overall: 218493.7) avg_msec=0.117 (overall: 0.121)

其中的一秒(第三行)的输出中 RPS 大幅降低。同时我们分析对应的 trace event 可以看到:

代码语言:javascript
代码运行次数:0
运行
复制
[21:16:30.420997167] (+0.000004064) zhenwei valkey:command_call: { cpu_id = 6 }, { prot = "tcp", conn = "127.0.0.1:6379-127.0.0.
1:54668", name = "get", duration = 1 }
    [21:16:30.421001262] (+0.000004095) zhenwei valkey:command_call: { cpu_id = 6 }, { prot = "tcp", conn = "127.0.0.1:6379-127.0.0.
1:54782", name = "get", duration = 1 }
    [21:16:30.485562459] (+0.064561197) zhenwei valkey:command_call: { cpu_id = 6 }, { prot = "tcp", conn = "127.0.0.1:6379-127.0.0.1:54386", name = "keys", duration = 64551 } --> root cause
    [21:16:30.485583101] (+0.000020642) zhenwei valkey:command_call: { cpu_id = 6 }, { prot = "tcp", conn = "127.0.0.1:6379-127.0.0.1:54522", name = "get", duration = 1 }
    [21:16:30.485763891] (+0.000180790) zhenwei valkey:command_call: { cpu_id = 6 }, { prot = "tcp", conn = "127.0.0.1:6379-127.0.0.1:54542", name = "get", duration = 1 }
    [21:16:30.485766451] (+0.000002560) zhenwei valkey:command_call: { cpu_id = 6 }, { prot = "tcp", conn = "127.0.0.1:6379-127.0.0.1:54438", name = "get", duration = 1 }

可以看到 keys 命令执行了 64551 us,考虑到 valkey-server 使用单线程运行,那么在执行 keys 命令期间,valkey-server 的多线程多连接上都会 阻塞,因此性能受到了大幅影响。详情见 valkey 的 commit

代码语言:javascript
代码运行次数:0
运行
复制
1941d28acd53c08335f85700ae8f8d1cf5cdc40c

Valkey 在 LTTng 的帮助下,预期可观测性将迎来史诗级升级,那种低频的、短暂的延迟抖动有可能会更加容易分析出来。未来更多的 tracepoint 加入,valkey 的可观测性能力将大幅帮助开发者和用户分析、解决问题!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-06-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 AlwaysGeek 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Redis/Valkey 性能问题分析的难点
  • 关于 LTTng
  • Valkey 使用 LTTng 提升可观测性
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档