美文网首页
【4.2.4】服务器安装 Docker Swarm中搭建 Re

【4.2.4】服务器安装 Docker Swarm中搭建 Re

作者: 王滕辉 | 来源:发表于2021-11-17 12:05 被阅读0次

时间: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 
image.png

确认最后都是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 出现时才会打印出来行号

执行后

image.png

这是会生成一个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执行过期删除数据的机制想法一样。


image.png

接下来批量修改数据为永久保存,取消过期

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 时间已经没有了


image.png

到此redis数据修改算是完成
最后注意时间修改回来

点波关注 系统搭建(docker)

相关文章

网友评论

      本文标题:【4.2.4】服务器安装 Docker Swarm中搭建 Re

      本文链接:https://www.haomeiwen.com/subject/ckdltrtx.html