Redis流量镜像的实现
来源 | OSCHINA 社区
作者 | KL博主
原文链接:https://my.oschina.net/klblog/blog/10115744
背景
对 Redis 场景降本增效,涉及到将部分 Redis 实例迁移到类似社区 pika 这种支持 Redis 协议的基于 SSD 磁盘存储的项目(阿里云 Tair),降低存储成本。迁移过程需要进行性能验证,除了基本的选型压测之外,还必须对每个业务场景做全指令的性能覆盖,才能确保业务迁移的性能以及指令兼容稳定性。常规的做法是需要业务开发配合在工程里进行流量双发,或者小范围流量灰度。
以上这个问题,不管哪种方式都需要投入更多的人力和时间,对降本增效本身这件事情来说,大大降低了 roi 。如果能够做到直接将原 Redis 的所有读流量重放到目标 Redis SSD 的实例,则迁移整件事件 SRE 可以完成 99% ,而且将大大缩短迁移实例的时间,所以 Redis 流量镜像这个需求就应运而生了。
tips:我们的数据迁移方案采用阿里云的 DTS ,DTS 是基于 Redis 主从复制的原理实现的,所以写流量性能在数据同步过程就可以直接验证了
调研
Google 上、Github 上逛了一圈,没有十分契合的东西,所以最后决定自研。查到的一些相关信息如下:
阿里云的 SLS Redis 审计日志
阿里云的 Redis 实例支持将 Redis 的执行日志丢到 SLS(一个日志记录查询的产品) 记录,但是只有写流量的记录,达不到 Redis 读流量回放的需求。
pika redis-copy 工具:https://github.com/OpenAtomFoundation/pika/issues/2044
这个工具已经被 pika 项目丢弃了。目前只有文档,仓库里已经没有相关的代码了。不过本文实现也是基于和 pika 的实现原理一样的
istio 实现 Redis 流量镜像:https://github.com/cloudnativeto/cloudnativeto.github.io/issues/76
基于 istio 做 Redis 的镜像流量,必须接入 istio 才行,局限性太大了,而且引入一个新的 istio 组件需要做很多的稳定性测试,所以这个路线就直接否了
实现
直接接入正题,采用 Redis 的 monitor 指令来实现这个需求。
/**
* @author kl (http://kailing.pub)
* @since 2023/9/27
*/
public class RedisMonitorTest {
static final JedisPool targetRedisPool = new JedisPool("127.0.0.1", 6398);
static final JedisPool sourceRedisPool = new JedisPool("127.0.0.1", 6379);
static final Set<String> redisReadCommands = new HashSet<>(Arrays.asList(
"get", "strlen", "exists", "getbit", "getrange", "substr", "mget", "llen", "lindex",
"lrange", "sismember", "scard", "srandmember", "sinter", "sunion", "sdiff", "smembers",
"sscan", "zrange", "zrangebyscore", "zrevrangebyscore", "zrangebylex", "zrevrangebylex",
"zcount", "zlexcount", "zrevrange", "zcard", "zscore", "zrank", "zrevrank", "zscan", "hget",
"hmget", "hlen", "hstrlen", "hkeys", "hvals", "hgetall", "hexists", "hscan", "randomkey",
"keys", "scan", "dbsize", "type", "sync", "psync", "ttl", "touch", "pttl", "dump", "object",
"memory", "bitcount", "bitpos", "georadius_ro", "georadiusbymember_ro", "geohash",
"geopos", "geodist", "pfcount", "xrange", "xrevrange", "xlen", "xread", "xpending",
"xinfo", "lolwut"
));
public static void main(String[] args) {
try (Jedis jedis = sourceRedisPool.getResource()) {
jedis.monitor(new JedisMonitor() {
@Override
public void onCommand(String command) {
sendCommand(command);
}
});
}
}
public static void sendCommand(String commandStr) {
String[] parts = commandStr.split("\"");
if (parts.length < 2) {
return;
}
String cmd = parts[1];
List<String> args = new ArrayList<>();
for (int i = 3; i < parts.length; i += 2) {
args.add(parts[i]);
}
if (redisReadCommands.contains(cmd.toLowerCase())) {
ProtocolCommand command = () -> cmd.getBytes(StandardCharsets.UTF_8);
try (Jedis jedis = targetRedisPool.getResource()) {
try {
long startTime = System.currentTimeMillis();
jedis.sendCommand(command, args.toArray(new String[0]));
System.out.println(cmd + ":" + (System.currentTimeMillis() - startTime));
} catch (Exception e) {
System.err.println(cmd + e.getMessage());
}
}
}
}
}
实现解析
1695869359.747056 [0 127.0.0.1:64257] "ZRANGEBYSCORE" "delayed_tasks" "0" "1695869359805247076" "LIMIT" "0" "1"
1695869359.748040 [0 127.0.0.1:64257] "EXISTS" "asynq:{sys}:paused"
1695869359.748259 [0 127.0.0.1:64257] "EXISTS" "asynq:{sync}:paused"
1695869359.748578 [0 127.0.0.1:64257] "EXISTS" "asynq:{default}:paused"
1695869359.748916 [0 127.0.0.1:64257] "ZRANGEBYSCORE" "delayed_tasks" "0" "1695869359869190783" "LIMIT" "0" "1"
1695869359.749154 [0 127.0.0.1:64257] "ZRANGEBYSCORE" "delayed_tasks" "0" "1695869359877625076" "LIMIT" "0" "1"
1695869359.749348 [0 127.0.0.1:64257] "ZRANGEBYSCORE" "delayed_tasks" "0" "1695869359878760313" "LIMIT" "0" "1"
1695869359.749530 [0 127.0.0.1:64257] "ZRANGEBYSCORE" "delayed_tasks" "0" "1695869359882064571" "LIMIT" "0" "1"
1695869359.779048 [0 127.0.0.1:64257] "ZRANGEBYSCORE" "delayed_tasks" "0" "1695869360024586886" "LIMIT" "0" "1"
1695869359.785898 [0 127.0.0.1:64257] "ZRANGEBYSCORE" "delayed_tasks" "0" "1695869360031603858" "LIMIT" "0" "1"
1695869359.786092 [0 127.0.0.1:64257] "ZRANGEBYSCORE" "delayed_tasks" "0" "1695869360031656719" "LIMIT" "0" "1"
1695869359.786923 [0 127.0.0.1:64257] "ZRANGEBYSCORE" "delayed_tasks" "0" "1695869360031666910" "LIMIT" "0" "1"
注意事项
monitor 文档:https://redis.io/commands/monitor/
结语
END
点这里 ↓↓↓ 记得 关注✔ 标星⭐ 哦
微信扫码关注该文公众号作者