本文字数:3849字
预计阅读时间:15 分钟
在开发测试过程中,或排查生产问题时,难免会碰到想查看redis服务中某些key的值是怎么变动的,以便确认数据变更与业务流转是否保持一致。此时,就涉及到命令回看——回看redis中执行的变更命令(数据变更)。
那如何实现命令回看呢?作为一个redis使用人员,最先想到的是monitor命令,通过执行monitor来查看redis服务执行的命令情况。
还可根据monitor结果筛选包含指定键/键类型的命令信息,命令示例如下:
事件通知英文名称 | 事件通知名称 | 事件通知说明 | 示例 |
notification | |||
notification | ——展示键事件类型 对应变更的key | PUBLISH __keyevent@0__:del mykey |
(除不包含在A类中的独有特性键未命中事件和键新增事件) |
在对应的redis应用执行config set notify-keyspace-events AKE。如需关注键新增事件或键未命中事件,请在配置值中额外补加对应类字符。
订阅事件通知消息并处理纪录
存在获取变更后数据不准确的场景 |
这时,不免发现一个问题,我的键变更命令是什么时候执行的?你这个记录里边没有呀?是的,很遗憾,redis 7.0之前的aof文件中并未存储命令执行时间,所以能拿到和提供的也就这么多了。
那么7.0及之后版本的redis aof文件中是有执行时间了吗?这个可以有。
命令 | 命令参数 | 时间 |
java 解析aof文件示例如下:
public class RedisKeyChangeTest{
@Test
public void scanAof(){
String localFilePath = "/data/redis.aof"; //aof file path
long currentLine = 1;
try (FileReader fileReader = new FileReader(new File(localFilePath));
BufferedReader bufferedReader = new BufferedReader(fileReader);){
List<RedisKeyUpdateRecord> redisRecordList = new ArrayList<>();
List<RedisKeyUpdateRecord> tempRedisRecords = new ArrayList<>();
String str;
while((str = bufferedReader.readLine()) != null){
currentLine++;
if(str.startsWith("*")){
RedisKeyUpdateRecord redisRecord = new RedisKeyUpdateRecord();
StringBuffer values = new StringBuffer();
String s = str.replaceFirst("\\*", "");
Integer paramNum = Integer.valueOf(s);
for (int i = 0; i < 2* paramNum; i++){
String s1 = bufferedReader.readLine();
currentLine++;
if(i % 2 == 0){
continue;
}
if(i == 1){
redisRecord.setCommand(s1);
}else if(i == 3){
redisRecord.setKey(s1);
}else{
if(i == 5){
values.append(s1);
}else{
values.append(" ");
values.append(s1);
}
}
}
redisRecord.setValue(values.toString());
redisRecordList.add(redisRecord);
tempRedisRecords.add(redisRecord);
}
if(str.startsWith("#TS:")){
String timestamp = str.replace("#TS:", "");
Long updateTime = Long.valueOf(timestamp);
Timestamp time = new Timestamp(updateTime * 1000);
tempRedisRecords.forEach(redisRecord -> redisRecord.setUpdateTime(time));
tempRedisRecords.clear();
log.info("save redis key record, currentLine:{}", currentLine);
}
}
}catch (Exception e){
log.error("save redis key record error, execute currentLine:{}", currentLine, e.getMessage());
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
class RedisKeyUpdateRecord {
/**
* 命令
*/
private String command;
/**
* key
*/
private String key;
/**
* value
*/
private String value;
/**
* 更新时间
*/
private Timestamp updateTime;
}
}
序号 | 方案 | 说明 | 影响 |
1 | monitor | 1. 键都在同一个redis实例时,操作简单; | 1. 对redis应用性能(响应)有明显影响; |
2. 键跨redis实例时,操作较复杂,且需分别整理结果; | |||
3. 能查看命令类型最多(包含查询); | 2. 线上环境操作需谨慎,且不能长时间执行; | ||
4. 命令执行时间最精确; | |||
2 | 键事件通知 | 1. 操作较复杂,订阅键通知消息; | 对redis应用cpu资源占用 |
2. 需自己实现消息解析和处理,尤其在键值变更时,需要自己去获取变更后的值; | |||
3. 单个键频繁变动时,键值存在获取不准的问题,且以消息接收时间作为命令执行时间存在一定时间滞后; | |||
4. 仅能获取写命令; | |||
3 | aof文件(redis7.0-) | 1. 操作较简单,解析aof文件即可,但无命令执行时间; | 对服务影响较小 |
2. 如果键跨redis实例,需要在相关redis实例分别获取aof解析; | |||
3. 仅能获取写命令; | |||
4 | aof文件(redis7.0+) | 1. 操作较复杂,解析aof文件即可,并以aof文件中时记录的间戳,作为命令执行时间; | 对服务影响较小 |
2. 如果键跨redis实例,需要在相关redis实例分别获取aof解析; | |||
3. 仅能获取写命令; |