跳到主要内容

QPS

2025年01月06日
柏拉文
越努力,越幸运

一、认识


QPS(Queries Per Second) 表示系统每秒处理的请求数,是衡量系统性能的重要指标。使用 Redis 作为数据存储和统计工具,可以高效实现精准的 QPS 统计,尤其在高并发场景中表现出色。实现方案如下: 基于滑动窗口的 QPS 统计: 使用 Redis 的有序集合(ZSET),将每个请求的时间戳记录为分数。动态统计过去 N 秒内的请求数,精准反映实时流量。

滑动窗口 通过更精细化的时间统计解决固定窗口的临界问题。它利用 Redis 有序集合(ZSET)存储请求的时间戳,并动态调整统计范围,使得时间窗口可以 滑动具体步骤为: 为每个用户或操作通过 ZADD 创建一个有序集合键,值为请求的时间戳。当用户发起请求时, 添加当前时间戳到有序集合,通过 ZREMRANGEBYSCORE 删除集合中超出时间窗口范围的旧时间戳, 通过 ZCARD 获取集合中当前时间窗口内的元素数量, 如果数量超过限制,拒绝请求, 并设置键的过期时间, 避免无用数据长期占用存储。滑动窗口 可以实现精细化统计,能够更精确地限制请求频率,避免流量突增,解决了固定窗口的临界问题,限流更加平滑,时间粒度更加精细。并具有高灵活性,可适用于更高要求的限流场景,例如对实时性要求较高的 API。而且可以动态调整,可以灵活调整时间窗口和请求限制。

二、语法


const redis = require('redis');
const redisClient = redis.createClient();
redisClient.connect();

// 滑动窗口 QPS 统计
async function recordQPSSlidingWindow(windowInSeconds = 1) {
const currentTime = Date.now(); // 当前毫秒时间戳
const windowStart = currentTime - windowInSeconds * 1000; // 窗口开始时间

const key = `qps:zset`;

// 添加当前请求时间到有序集合
await redisClient.zAdd(key, { score: currentTime, value: currentTime.toString() });

// 删除窗口外的请求
await redisClient.zRemRangeByScore(key, 0, windowStart);

// 获取窗口内的请求数
const count = await redisClient.zCard(key);

// 设置过期时间,避免无用数据
await redisClient.expire(key, windowInSeconds + 1);

return count;
}

// 示例:记录请求并输出 QPS
(async () => {
const qps = await recordQPSSlidingWindow(1);
console.log(`QPS in the last 1 second: ${qps}`);
})();