时间:2021-11-17
话接上个月,处理过数据空间不足,迁移数据道不同机器后,近期redis出现严重问题。
一个周末过后回来,保存在redis中的数据不见了,不见了,不见了。
redis是有过期删除机制的,问过开发的兄弟,这家伙死活说他没有设置过期的时间,我知道在兄弟开发确实不可能手动去写过期时间的,问题一定出现在配置上,看了下springboot的yml配置文件中也没有设置,对应的微服务上也没有,最后在共通包common项目找到RedisConfig.java
这里面有设置ttl的时间,不了解的人肯定不知道干什么用的,我也是找了好多网站看到ttl是设置过期时间的,这家伙一定是网上找的配置用户登录过期验证,不了解就全部照搬源代码了,经过这么搞,我们一起又要考虑恢复了。
数据过期自动删除在 aof文件会生成del命令,幸好我们有做aof开启记录。不然redis干了什么都不知道,数据丢失就真的恢复不过来了,通过分析redis自动删除数据后我们就没在操作库了,所以del的都在aof文件的最后,通过tail查看 :
tail -n 30 appendonly.aof

确认最后都是del的命令
然后删除,网上找到脚本
#!/bin/bash
# 文件
log=appendonly.aof
# 要删除的行数
line=30
A=$(sed -n '$=' $log)
sed -i $(($A-$line+1)),${A}d $log
将文件与aof放到一起
现在就差确定要删掉多少行就可以执行了。
使用java代码编写一个循环读文件判断文件中连续出现del的行号,记录下最后的行号,计算下这之间的行数填入脚本中
springboot启动类
package org.danyuan.application;
import org.danyuan.application.service.FileDelHelp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class AppendonlyHelpApplication implements ApplicationRunner {
public static void main(String[] args) {
SpringApplication.run(AppendonlyHelpApplication.class, args);
}
@Autowired
FileDelHelp fileDelHelp;
@Override
public void run(ApplicationArguments args) throws Exception {
fileDelHelp.run();
}
}
配置类
package org.danyuan.application.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import lombok.Data;
@Component
@ConfigurationProperties(prefix = "user")
@Data
public class UserConfig {
// 报告文档生成路径
private String path;
private String file;
private String delStartWith;
private String delContextStartWith;
private String expStartWith;
private String expContextStartWith;
private String encoding;
}
处理逻辑类
package org.danyuan.application.service;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Resource;
import org.danyuan.application.config.UserConfig;
import org.springframework.stereotype.Service;
@Service
public class FileDelHelp {
String line = System.lineSeparator();
Long ln = 0L;
int times = 0;
Long[] tem = { 0L, 0L, 0L };
@Resource
UserConfig userConfig;
public void run() throws Exception {
String path = userConfig.getPath();
String fileName = userConfig.getFile();
File file = new File(path.replace("\\", "/") + "/" + fileName);
if (!file.exists()) {
throw new Exception("文件未找到,请确定文件路径和文件名称正确!!!");
}
InputStreamReader reader = new InputStreamReader(new FileInputStream(file), userConfig.getEncoding());
OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(new File(path.replace("\\", "/") + "/" + fileName + ".new")), userConfig.getEncoding());
try {
System.out.println("以行为单位读取文件内容,一次读一整行:");
BufferedReader bufferedReader = new BufferedReader(reader);
String tempString = null;
List<String> list = new ArrayList<>();
while ((tempString = bufferedReader.readLine()) != null) {
ln++;
// 显示行号
if (tempString.equals(userConfig.getDelStartWith()) || tempString.equals(userConfig.getExpStartWith())) {
System.out.println("第一次出现匹配的行号" + ln);
list.add(tempString);
content(bufferedReader, tempString, list, writer);
} else {
writer.append(tempString + "\r");
}
}
writer.close();
reader.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e1) {
}
}
}
}
private void content(BufferedReader reader, String tempString, List<String> list, OutputStreamWriter writer) throws IOException {
while ((tempString = reader.readLine()) != null) {
if (tempString.startsWith("*")) {
list = extracted(list, writer);
}
ln++;
list.add(tempString);
}
System.out.println("最后的行号" + ln);
if (list.size() > 0) {
extracted(list, writer);
}
}
/**
* @param tempString
* @param list
* @param writer
* @param ln
* @param tem
* @param times
* @return
* @throws IOException
*/
private List<String> extracted(List<String> list, OutputStreamWriter writer) throws IOException {
boolean f = true;
boolean start = false;
for (int a = 0; a < list.size(); a++) {
String tempString = list.get(a);
if (tempString.equals(userConfig.getDelStartWith()) || tempString.equals(userConfig.getExpStartWith())) {
start = true;
}
if (start && (tempString.startsWith(userConfig.getDelContextStartWith()) || tempString.startsWith(userConfig.getExpContextStartWith()))) {
f = false;
}
}
if (f && list.size() > 0) {
if (times == 3) {
System.out.println("连续出现匹配行号后还有内容行号:" + ln);
}
times = 0;
tem = new Long[] { 0L, 0L, 0L };
for (String string : list) {
// System.out.println(string);
if (string == null) {
string = "";
}
writer.append(string + line);
}
} else if (times < 3) {
tem[times] = ln - list.size();
times++;
if (times == 3) {
for (Long long1 : tem) {
System.out.println("连续出现匹配行号" + long1);
}
}
}
list = new ArrayList<>();
return list;
}
}
配置信息
user:
path: "/home/redis-data/node1"
file: "appendonly.aof"
del-start-with: "*2"
del-context-start-with: "FINANCE.listhash"
exp-start-with: "*3"
exp-context-start-with: "FINANCE.listhash"
encoding: "ISO-8859-1"
以星号分割文件,2是删除语句开头,中间包含DEL行和删除的key,3是带有过期配置语句的开头,
del-context-start-with 配置的是删除的key的开头
exp-start-with是配置了过期时间的开头
多个连续的删除 FINANCE.listhash 出现时才会打印出来行号
执行后

这是会生成一个appendonly.aof.new 文件,此文件目前没有用,直接删除即可
rm -f appendonly.aof.new
然后
cd 到 delfile.sh
chmod +x delfile.sh
sh delfile.sh
执行完脚本appendonly.aof 才被修改完,
然后删除 rdb文件
rm -f dump.rdb
修改系统时间 ,讲时间向前面点推,因为aof里面可能还有设置过期时间的语句在什么位置不太清楚,我们只是删掉了del语句,万一恢复后redis 又到了要删数据的时候,又执行了删除,不就白干了么,
date -s "2021-11-11 10:10:10"
然后重启redis
docker restart redis-node1
docker restart redis-node2
docker restart redis-node3
数据得到恢复
查看ttl果然redis 还是有过期时间的设置,这也与我们猜想redis执行过期删除数据的机制想法一样。

接下来批量修改数据为永久保存,取消过期
redis-cli -h 192.168.0.40 -p 6379 keys "FINANCE.listhash:*" | xargs -i redis-cli -h 192.168.0.40 -p 6379 PERSIST {}
redis-cli -h 192.168.0.33 -p 6380 keys "FINANCE.listhash:*" | xargs -i redis-cli -h 192.168.0.33 -p 6380 PERSIST {}
redis-cli -h 192.168.0.34 -p 6381 keys "FINANCE.listhash:*" | xargs -i redis-cli -h 192.168.0.34 -p 6381 PERSIST {}
在次查看 ttl 时间已经没有了

到此redis数据修改算是完成
最后注意时间修改回来
点波关注 系统搭建(docker)
网友评论