Redis 4.0 升级至 5.0 实施手册

Redis 4.0 升级至 5.0 实施手册

一、背景

为提升 Redis 服务的性能、稳定性及功能扩展性,计划将现有 Redis 4.0 版本升级至 5.0 版本。Redis 5.0 虽与 4.0 整体兼容性较好,但升级过程需兼顾客户端适配、数据兼容、集群高可用及业务无感知等核心要点,同时需解决配置分散、自动重连验证等实际问题,确保升级平稳落地。

二、解决方案

通过"定位业务服务→验证重连机制"的核心逻辑,保障升级后业务与 Redis 服务的适配性,具体步骤如下:

  1. 基于 Redis 实例查询已建立连接的客户端,定位客户端所属主机;
  2. 在该主机上排查运行中的 JVM 进程,根据进程 ID 核查是否占用 Redis 端口;
  3. 确定目标 JVM 服务的工作目录,以此锁定实际使用该 Redis 实例的业务服务;
  4. 检索对应项目代码中 Redis 实例的构造参数,验证其是否支持 Redis 服务升级过程中的中断重连机制。

三、前置处理

将 Redis 从 4.0 升级到 5.0 时,虽整体兼容性较好,但需重点关注以下关键事项,确保升级过程平稳且业务不受影响:

1. 版本兼容性检查

(1)客户端兼容性
  • 客户端版本:确保业务使用的 Redis 客户端(如 Jedis、Lettuce 等)适配 Redis 5.0,具体版本要求:
    • Jedis 需升级至 2.10.0+(推荐 3.x 及以上版本,更好适配 Redis 5.0 新特性);
    • Lettuce 需升级至 5.0+ 版本,以支持 Redis 5.0 新增命令及协议优化。
  • 命令兼容性 :Redis 5.0 新增 ZPOPMIN/ZPOPMAXXADD(Stream 相关)等命令,旧客户端可能不支持,但 SETGETINCR 等基础命令完全兼容,不影响现有核心业务。
(2)数据格式兼容性
  • Redis 5.0 对 String、Hash、List 等现有数据结构的存储格式无变更,升级后可直接读取 4.0 版本数据,无需数据迁移或转换;
  • 持久化文件(RDB/AOF)格式兼容:Redis 5.0 可加载 4.0 生成的 RDB 和 AOF 文件,低版本(4.0)无法加载高版本(5.0)生成的持久化文件,需做好版本回滚的数据备份。

2. 新特性与行为变更

需评估 Redis 5.0 新特性的应用场景,及现有业务是否受行为变更影响:

(1)核心新特性(可选使用)
  • Stream 数据结构:类 Kafka 的持久化消息队列,支持消费组、消息确认等机制,可替代 List 实现的简单队列,适用于需可靠消息队列的业务场景;
  • ZPOPMIN/ZPOPMAX 命令:原子性弹出有序集合最小/最大元素,替代 ZRANGE + ZREM 组合操作,适配优先级队列场景;
  • 改进的主从复制:优化复制过程中网络中断恢复逻辑,降低全量同步概率,提升集群稳定性;
  • Lua 脚本优化 :扩展 EVAL 命令参数列表,执行效率略有提升,现有 Lua 脚本无需修改即可兼容。
(2)行为变更(需重点注意)
  • EXISTS 命令扩展:支持同时检查多个 key(返回存在的数量),但单 key 检查行为不变(返回 1/0),对现有代码无影响;
  • TYPE 命令返回值细化:如对 Stream 类型返回 stream、对过期键返回对应类型(4.0 可能返回 none),常规业务极少依赖该返回值,无需调整;
  • 过期键删除策略:底层优化清理效率,对外行为(键过期后不可访问)不变,不影响业务逻辑。

3. 集群与高可用调整

(1)Redis Cluster
  • 5.0 优化集群故障转移速度和槽位迁移效率,升级后稳定性提升;
  • 升级策略:先升级从节点,再升级主节点(滚动升级),避免集群不可用。
(2)哨兵(Sentinel)
  • 5.0 微调哨兵故障检测逻辑,提升主从切换准确性,无需修改哨兵配置即可兼容;
  • 升级顺序:先升级哨兵集群,再升级 Redis 节点,确保哨兵能正确识别新版本节点。

4. 性能与资源考量

  • 内存占用:对 Hash、Set 等数据结构的内存编码优化,相同数据量下内存占用略有降低,整体差异较小;
  • CPU 与网络 :优化网络 IO 处理和命令执行效率,高并发场景吞吐量可提升 5%-10%;需避免使用 KEYSFLUSHALL 等阻塞命令,否则仍可能出现性能波动;
  • 持久化优化:AOF 重写和 RDB 生成效率提升,降低持久化对业务的影响。

5. 升级步骤与回滚预案

(1)环境准备
  • 在测试环境部署 Redis 5.0,加载生产环境的 RDB/AOF 文件完成兼容性测试,验证所有依赖 Redis 的核心业务流程;
  • 检查并升级客户端至兼容 5.0 的版本,在测试环境完成功能验证。
(2)滚动升级(推荐)
  • 单机部署:停止 4.0 实例 → 替换为 5.0 二进制文件 → 加载原有 RDB/AOF 启动 → 验证功能后对外提供服务;
  • 主从集群:先升级从节点(停止 → 升级 5.0 → 启动并同步主节点)→ 切换主节点(哨兵自动/手动切换)→ 升级原主节点为从节点;
  • Redis Cluster :逐个升级从节点 → 通过 CLUSTER FAILOVER 安全切换主从 → 升级原主节点。
(3)回滚预案
  • 升级前全量备份 RDB/AOF 文件,若升级后出现异常,立即回滚至 4.0 版本,加载备份文件恢复数据;
  • 实时监控升级后核心指标(内存、CPU、响应时间、错误率),异常时切换至备用集群。

6. 其他注意事项

  • 模块兼容性:若使用 RedisJSON、RediSearch 等模块,需确保模块版本与 Redis 5.0 适配(模块通常绑定特定 Redis 版本);
  • 配置参数 :Redis 5.0 新增 stream-node-max-bytes 等配置,原有配置完全兼容,无需修改;
  • 日志与监控 :升级后关注 Redis 日志,及时处理 WARNING 级别的兼容性提示(如旧命令废弃),调整相关业务代码。

四、问题分类

在 Redis 服务升级过程中,核心问题集中在以下两类:

  1. 项目重启环节:Redis 相关配置未集中管理,散落在配置文件、启动命令行参数、Maven 的 pom 文件等位置,导致重启前配置核对不全面,易引发配置遗漏或错误;
  2. 自动重连验证环节:需通过 Redis 后台管理界面/工具查询与实例建立连接的服务器列表,以此判断业务服务的 Redis 自动重连机制在升级中断场景下是否正常触发。

五、实操步骤

步骤1:定位 Redis 连接客户端及所属主机

  1. 登录 Redis 实例所在服务器,执行 redis-cli 进入 Redis 客户端交互界面;

  2. 执行 CLIENT LIST 命令,获取所有已建立连接的客户端信息,提取关键字段:

    • addr:客户端 IP 与端口(格式为 IP:Port),用于定位客户端所属主机;
    • fd:文件描述符,辅助确认连接有效性;
    • age:连接时长,筛选长期活跃的业务客户端;
  1. 整理客户端 IP 列表,与服务器资产信息匹配,确定每个客户端对应的业务主机。

步骤2:排查主机上运行的 JVM 进程并核查 Redis 端口占用

  1. 登录目标业务主机,执行以下命令列出所有运行中的 JVM 进程:
bash 复制代码
jps -l  # 显示进程 PID 及对应主类/ Jar 包路径
  1. 针对每个 JVM 进程,核查是否占用 Redis 端口(默认 6379,需替换为实际端口):
perl 复制代码
ss -tunp | grep <JVM进程PID> | grep <Redis端口>
  1. 记录符合条件的进程信息:PID、进程名称、Jar 包路径/主类名,确认进程状态为 RUNNING

步骤3:解析 JVM 启动参数,确定服务工作目录

  1. 执行以下命令查看目标 JVM 进程的完整启动参数:
xml 复制代码
jinfo <JVM进程PID>  # 或 jps -v | grep <JVM进程PID>
bash 复制代码
# 如果是Tomcat项目,可使用jps -lvm和readlink -f获取对应PID和工程目录
jps -lvm
readlink -f /proc/<JVM进程PID>/cwd/
  1. 从启动参数中提取关键信息:
    • user.dir:JVM 工作目录(核心);
    • spring.config.location/--spring.config.name:配置文件路径,辅助确认服务归属;
    • classpath:类路径,验证服务代码目录;
  1. 进入 user.dir 目录,结合进程信息确认服务的部署路径与业务归属。

步骤4:锁定实际使用 Redis 的业务服务

  1. 基于 JVM 工作目录与进程信息,定位业务服务标识:
    • 查看目录下的 application.yml/redis.properties 等配置文件,确认 Redis 连接信息(地址、端口、密码)与目标实例匹配;
    • 核对 Jar 包名称/主类名,关联至具体业务服务(如订单服务、用户服务);
  1. 记录业务服务信息:服务名称、部署路径、负责人、代码仓库地址。

步骤5:检索项目代码验证 Redis 中断重连机制

  1. 拉取目标业务服务的源代码,检索 Redis 实例构造相关代码:
    • Jedis 客户端 :查找 JedisPoolConfig/JedisPool 初始化代码,重点检查以下参数:
scss 复制代码
// 重连相关核心参数
poolConfig.setMaxTotal(200);        // 最大连接数
poolConfig.setMaxIdle(50);          // 最大空闲连接
poolConfig.setTestOnBorrow(true);   // 借用连接时测试有效性
JedisPool jedisPool = new JedisPool(poolConfig, host, port, Protocol.DEFAULT_TIMEOUT, password, ssl);
// 手动重连逻辑:是否捕获 JedisConnectionException 并触发重连
    • Lettuce 客户端 :查找 LettuceConnectionFactory 初始化代码,重点检查:
scss 复制代码
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
    .commandTimeout(Duration.ofSeconds(5))
    .reconnectAttempts(3)                // 重连次数
    .reconnectInterval(Duration.ofMillis(1000))  // 重连间隔
    .build();
  1. 验证重连机制有效性:
    • 确认重连参数配置合理(如重连次数≥3、重连间隔 1-3 秒);
    • 检查代码中是否捕获 RedisConnectionFailureException 等连接异常,并实现自动重连逻辑;
    • 若存在自定义 Redis 连接池,核查池化策略是否支持连接失效后自动重建。

步骤6:处理 Redis 配置分散问题(前置/并行操作)

  1. 全量梳理目标服务的 Redis 配置存储位置:
配置位置 核查内容
配置文件(application.yml/redis.properties) Redis 地址、端口、密码、连接池参数
启动命令(如 start.sh 中的 java -jar 参数) 外部传入的 Redis 配置(如 --redis.host=xxx)
Maven POM 文件 Redis 客户端依赖版本、自定义配置依赖
  1. 统一核对配置值,确保所有位置的 Redis 连接信息一致;
  2. 升级前修正配置错误/不一致项,建议后续将配置迁移至配置中心(Nacos/Apollo),避免分散管理。

步骤7:验证自动重连机制(测试/生产验证)

  1. 测试环境验证
    • 模拟 Redis 升级中断:停止 Redis 实例 10 秒后重启;
    • 执行以下命令监控业务服务的 Redis 连接状态:
perl 复制代码
# 实时查看业务服务日志中的连接异常
tail -f <业务服务日志文件> | grep "Redis|connection"
# 查看 Redis 客户端连接恢复情况
redis-cli CLIENT LIST | grep <业务主机IP>
    • 验证核心业务流程(如数据读写、缓存更新)无异常,连接中断后自动恢复。
  1. 生产环境验证(升级后)
    • 通过 Redis 后台管理工具(RedisInsight/运维平台)查看客户端连接列表,确认业务主机重新建联;
    • 监控业务服务指标:Redis 调用成功率、响应时间、异常率,确保重连机制生效。

步骤8:升级后兜底验证

  1. 完成 Redis 4.0 → 5.0 升级后,执行以下命令确认版本:
perl 复制代码
redis-cli INFO server | grep "redis_version"
  1. 遍历所有关联业务服务,执行核心用例(如下单、支付、数据查询),验证 Redis 交互正常;

  2. 持续监控 24 小时,重点关注:

    • Redis 侧:内存占用、CPU 使用率、持久化日志、集群槽位分布(集群版);
    • 业务侧:Redis 调用异常日志、接口响应时间、服务可用性。

六、相关脚本

client list 解析

复制代码
client list
client_list.cl
bash 复制代码
id=43931324 addr=1xx.2xx.0.240:54695 fd=49 name= age=497974 idle=208 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r traffic-control=NULL cmd=exists type=vpc next_opid=-1 real_addr=
id=43931325 addr=1xx.2xx.0.240:54694 fd=58 name= age=497974 idle=151 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r traffic-control=NULL cmd=exists type=vpc next_opid=-1 real_addr=
id=43931326 addr=1xx.2xx.0.240:54705 fd=59 name= age=497974 idle=174 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r traffic-control=NULL cmd=eval type=vpc next_opid=-1 real_addr=
id=43931327 addr=1xx.2xx.0.240:54699 fd=60 name= age=497974 idle=651 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r traffic-control=NULL cmd=eval type=vpc next_opid=-1 real_addr=
id=43931328 addr=1xx.2xx.0.240:54703 fd=61 name= age=497974 idle=208 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r traffic-control=NULL cmd=eval type=vpc next_opid=-1 real_addr=
id=43931329 addr=1xx.2xx.0.240:54707 fd=62 name= age=497974 idle=651 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r traffic-control=NULL cmd=eval type=vpc next_opid=-1 real_addr=
id=43931330 addr=1xx.2xx.0.240:54709 fd=63 name= age=497974 idle=114 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r traffic-control=NULL cmd=hexists type=vpc next_opid=-1 real_addr=
id=43931331 addr=1xx.2xx.0.240:54719 fd=65 name= age=497974 idle=651 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r traffic-control=NULL cmd=hexists type=vpc next_opid=-1 real_addr=
client_list.py
python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Redis客户端列表响应解析器
用于解析redis client_list命令的输出结果
"""

import os
import socket
from typing import Dict, Set, Optional


class RedisClientInfo:
    """Redis客户端连接信息类

    用于存储和操作Redis客户端连接的详细信息
    每一行client_list数据对应一个实例
    """

    def __init__(self, **kwargs):
        """初始化客户端信息对象

        Args:
            **kwargs: 客户端信息的各个字段
        """
        # 基本连接信息
        self.id: Optional[str] = kwargs.get('id')
        self.addr: Optional[str] = kwargs.get('addr')
        self.fd: Optional[str] = kwargs.get('fd')
        self.name: Optional[str] = kwargs.get('name')

        # 状态信息
        self.age: Optional[str] = kwargs.get('age')
        self.idle: Optional[str] = kwargs.get('idle')
        self.flags: Optional[str] = kwargs.get('flags')
        self.db: Optional[str] = kwargs.get('db')

        # 订阅信息
        self.sub: Optional[str] = kwargs.get('sub')
        self.psub: Optional[str] = kwargs.get('psub')

        # 事务信息
        self.multi: Optional[str] = kwargs.get('multi')

        # 缓冲区信息
        self.qbuf: Optional[str] = kwargs.get('qbuf')
        self.qbuf_free: Optional[str] = kwargs.get('qbuf-free')
        self.obl: Optional[str] = kwargs.get('obl')
        self.oll: Optional[str] = kwargs.get('oll')
        self.omem: Optional[str] = kwargs.get('omem')

        # 事件和命令信息
        self.events: Optional[str] = kwargs.get('events')
        self.traffic_control: Optional[str] = kwargs.get('traffic-control')
        self.cmd: Optional[str] = kwargs.get('cmd')

        # 额外信息
        self.type: Optional[str] = kwargs.get('type')
        self.next_opid: Optional[str] = kwargs.get('next_opid')
        self.real_addr: Optional[str] = kwargs.get('real_addr')

    def __str__(self) -> str:
        """返回客户端信息的字符串表示"""
        return f"RedisClientInfo(id={self.id}, addr={self.addr}, cmd={self.cmd}, age={self.age}s)"

    def __repr__(self) -> str:
        """返回客户端信息的官方字符串表示"""
        return self.__str__()

    def __eq__(self, other) -> bool:
        """比较两个客户端信息是否相等(基于id)"""
        if not isinstance(other, RedisClientInfo):
            return False
        return self.id == other.id

    def __hash__(self) -> int:
        """返回客户端信息的哈希值(基于id)"""
        return hash(self.id)

    def to_dict(self) -> Dict[str, Optional[str]]:
        """将客户端信息转换为字典"""
        return {
            'id': self.id,
            'addr': self.addr,
            'fd': self.fd,
            'name': self.name,
            'age': self.age,
            'idle': self.idle,
            'flags': self.flags,
            'db': self.db,
            'sub': self.sub,
            'psub': self.psub,
            'multi': self.multi,
            'qbuf': self.qbuf,
            'qbuf-free': self.qbuf_free,
            'obl': self.obl,
            'oll': self.oll,
            'omem': self.omem,
            'events': self.events,
            'traffic-control': self.traffic_control,
            'cmd': self.cmd,
            'type': self.type,
            'next_opid': self.next_opid,
            'real_addr': self.real_addr
        }


def parse_client_line(line: str) -> Optional[RedisClientInfo]:
    """解析单行客户端信息

    Args:
        line: 单行client_list输出

    Returns:
        RedisClientInfo实例,如果解析失败则返回None
    """
    if not line or not line.strip():
        return None

    # 分割key=value对
    parts = line.strip().split()
    client_data = {}

    for part in parts:
        try:
            key, value = part.split('=', 1)
            client_data[key] = value
        except ValueError:
            # 忽略无效的格式
            continue

    # 确保有id字段才创建实例
    if 'id' in client_data:
        return RedisClientInfo(**client_data)

    return None


def parse_client_list_file(file_path: str) -> Set[RedisClientInfo]:
    """解析client_list文件

    Args:
        file_path: client_list文件路径

    Returns:
        RedisClientInfo实例的集合
    """
    clients_set = set()

    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            for line in f:
                client_info = parse_client_line(line)
                if client_info:
                    clients_set.add(client_info)
    except FileNotFoundError:
        print(f"错误:文件 {file_path} 不存在")
    except Exception as e:
        print(f"解析文件时出错:{e}")

    return clients_set


def get_client_list_input_path() -> str:
    """获取client_list文件的默认路径"""
    # 直接使用绝对路径,确保能找到文件
    return '/Users/ayuan/Documents/src/PycharmProjects/python/src/main/resources/data/redis/client_list/client_list.cl'

def get_client_list_output_path() -> str:
    """获取client_list文件的默认路径"""
    # 直接使用绝对路径,确保能找到文件
    return '/Users/ayuan/Documents/src/PycharmProjects/python/output/redis/client_list'

def get_current_host_ip() -> str:
    """获取当前主机的IP地址

    Returns:
        str: 当前主机的IP地址
    """
    try:
        # 创建一个UDP socket连接到外部地址,这样可以获取本地出口IP
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(('8.8.8.8', 80))
        ip = s.getsockname()[0]
        s.close()
        return ip
    except Exception:
        # 如果失败,返回127.0.0.1作为默认值
        return '127.0.0.1'


def split_by_ip(clients: Set[RedisClientInfo], output_dir: str, exclude_local_ip: bool = True) -> None:
    """按IP地址分割客户端信息到不同的文件

    Args:
        clients: RedisClientInfo实例的集合
        output_dir: 输出目录路径
        exclude_local_ip: 是否排除当前主机的IP地址
    """
    # 确保输出目录存在
    os.makedirs(output_dir, exist_ok=True)

    # 获取当前主机IP
    local_ip = get_current_host_ip() if exclude_local_ip else None
    if exclude_local_ip:
        print(f"正在排除当前主机IP: {local_ip}")

    # 按IP分组存储原始行数据
    ip_data = {}

    # 重新读取原始文件,以便保留完整的行数据
    file_path = get_client_list_input_path()
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            for line in f:
                client_info = parse_client_line(line)
                if client_info and client_info.addr:
                    ip = client_info.addr.split(':')[0] if ':' in client_info.addr else client_info.addr
                    # 如果需要排除本地IP且当前IP是本地IP,则跳过
                    if exclude_local_ip and ip == local_ip:
                        continue
                    if ip not in ip_data:
                        ip_data[ip] = []
                    ip_data[ip].append(line.strip())
    except Exception as e:
        print(f"读取原始文件时出错:{e}")
        return

    # 写入按IP分组的文件
    for ip, lines in ip_data.items():
        # 将IP地址中的点替换为下划线作为文件名
        safe_ip = ip.replace('.', '_')
        output_file = os.path.join(output_dir, f"client_list_{safe_ip}.cls")

        try:
            with open(output_file, 'w', encoding='utf-8') as f:
                for line in lines:
                    f.write(line + '\n')
            print(f"已写入 {len(lines)} 条记录到 {output_file}")
        except Exception as e:
            print(f"写入文件 {output_file} 时出错:{e}")

    print(f"\n按IP分组完成,共生成 {len(ip_data)} 个文件")

def client_list():
    """主函数,用于演示解析功能"""
    file_path = get_client_list_input_path()
    print(f"正在解析文件:{file_path}")

    clients = parse_client_list_file(file_path)
    print(f"成功解析 {len(clients)} 个客户端连接信息")

    # 获取当前主机IP
    local_ip = get_current_host_ip()
    print(f"当前主机IP: {local_ip}")

    # 过滤掉本地IP的客户端
    non_local_clients = {client for client in clients if client.addr and 
                        (client.addr.split(':')[0] if ':' in client.addr else client.addr) != local_ip}
    print(f"过滤后客户端数量: {len(non_local_clients)}")

    # 显示前5个非本地客户端信息作为示例
    print("\n前5个非本地客户端信息示例:")
    for i, client in enumerate(list(non_local_clients)[:5], 1):
        print(f"{i}. {client}")

    # 统计一些基本信息(仅对非本地客户端)
    print("\n基本统计信息(已排除本地IP):")
    # 按数据库分组统计
    db_count = {}
    cmd_count = {}
    ip_count = {}

    for client in non_local_clients:
        # 统计数据库使用情况
        if client.db:
            db_count[client.db] = db_count.get(client.db, 0) + 1

        # 统计命令使用情况
        if client.cmd:
            cmd_count[client.cmd] = cmd_count.get(client.cmd, 0) + 1

        # 统计IP地址使用情况(提取IP部分,忽略端口)
        if client.addr:
            ip = client.addr.split(':')[0] if ':' in client.addr else client.addr
            ip_count[ip] = ip_count.get(ip, 0) + 1

    print(f"数据库使用情况:{db_count}")
    print(f"最常用的5个命令:{sorted(cmd_count.items(), key=lambda x: x[1], reverse=True)[:5]}")

    # 显示按IP分组的统计信息
    print("\n按IP地址分组统计(已排除本地IP):")
    # 按连接数降序排序并显示
    sorted_ips = sorted(ip_count.items(), key=lambda x: x[1], reverse=True)
    print(f"共有 {len(sorted_ips)} 个不同的IP地址")
    print("前10个IP地址及其连接数:")
    for ip, count in sorted_ips[:10]:
        print(f"  {ip}: {count} 个连接")

    # 按IP分割文件(排除本地IP)
    print("\n开始按IP地址分割文件(已排除本地IP)...")
    output_dir = get_client_list_output_path()
    split_by_ip(non_local_clients, output_dir, exclude_local_ip=True)


if __name__ == '__main__':
    client_list()
client_list.cls
lua 复制代码
-- 本机IP地址
id=38124222 addr=127.0.0.1:42355 fd=222 name= age=7053385 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r traffic-control=NULL cmd=config type=localhost next_opid=-1 real_addr=

jps 解析

jps_lvm.input
javascript 复制代码
11828 org.apache.catalina.startup.Bootstrap start -Djava.util.logging.config.file=/home/tomcat/easyhome_workbench/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9191 -javaagent:/home/skywalking-agent/skywalking-agent.jar -Dskywalking.agent.service_name=uat-workbench -Dskywalking.collector.backend_service=10.231.58.172:11800 -Dignore.endorsed.dirs= -Dcatalina.base=/home/tomcat/easyhome_workbench -Dcatalina.home=/home/tomcat/easyhome_workbench -Djava.io.tmpdir=/home/tomcat/easyhome_workbench/temp
jps_lvm.py
python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
读取jps_lvm.txt文件,将空格替换为换行,并输出到jps_lvm.out文件
Python 3.9 兼容版本
"""


import os

def jps_lvm():
    # 源文件和目标文件路径
    source_file = '/Users/ayuan/Documents/src/PycharmProjects/python/src/main/resources/data/redis/jps_lvm/jps_lvm.input'
    target_file = '/Users/ayuan/Documents/src/PycharmProjects/python/output/redis/jps_lvm/jps_lvm.output'

    print(f"读取文件: {source_file}")

    try:
        # 读取源文件内容
        with open(source_file, 'r', encoding='utf-8') as f:
            content = f.read().strip()

        print("读取到内容长度: {} 字符".format(len(content)))

        # 将空格替换为换行
        processed_content = content.replace(' ', ' \n')

        # 确保目标文件所在的目录存在
        os.makedirs(os.path.dirname(target_file), exist_ok=True)
        print(f"确保目录存在: {os.path.dirname(target_file)}")

        # 写入目标文件
        with open(target_file, 'w', encoding='utf-8') as f:
            f.write(processed_content)

        print("已将处理后的内容写入: {}".format(target_file))
        print("处理后内容长度: {} 字符".format(len(processed_content)))

        # 验证结果
        with open(target_file, 'r', encoding='utf-8') as f:
            final_content = f.read()

        line_count = final_content.count('\n') + 1
        print("验证写入成功,最终文件行数: {}".format(line_count))

    except Exception as e:
        print("处理出错: {}".format(str(e)))


if __name__ == '__main__':
    jps_lvm()
jps_lvm.output
ini 复制代码
11828 
org.apache.catalina.startup.Bootstrap 
start 
-Djava.util.logging.config.file=/home/tomcat/easyhome_workbench/conf/logging.properties 
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager 
-Djdk.tls.ephemeralDHKeySize=2048 
-Djava.protocol.handler.pkgs=org.apache.catalina.webresources 
-Xdebug 
-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9191 
-javaagent:/home/skywalking-agent/skywalking-agent.jar 
-Dskywalking.agent.service_name=uat-workbench 
-Dskywalking.collector.backend_service=1x.2xx.5x.1xx:11800 
-Dignore.endorsed.dirs= 
-Dcatalina.base=/home/tomcat/easyhome_workbench 
-Dcatalina.home=/home/tomcat/easyhome_workbench 
-Djava.io.tmpdir=/home/tomcat/easyhome_workbench/temp

连接池参数

java 复制代码
        // 1. 配置连接池参数
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxTotal(20); // 最大连接数(同时与 Redis 建立的连接数)
        poolConfig.setMaxIdle(10);  // 最大空闲连接数(空闲时保留的连接数)
        poolConfig.setMinIdle(5);   // 最小空闲连接数(空闲时至少保留的连接数)
        poolConfig.setMaxWaitMillis(3000); // 从连接池获取连接的最大等待时间(超时抛异常)
java 复制代码
public class JedisPoolConfig extends GenericObjectPoolConfig {
    public JedisPoolConfig() {
        this.setTestWhileIdle(true); // 空闲连接检测(定期检测空闲连接,销毁无效连接),结合 timeBetweenEvictionRunsMillis
        this.setMinEvictableIdleTimeMillis(60000L); // 空闲连接最小存活时间:5分钟(空闲超过5分钟的连接会被销毁) 
        this.setTimeBetweenEvictionRunsMillis(30000L); // 空闲连接检测的间隔时间
        this.setNumTestsPerEvictionRun(-1); // 设置每次空闲连接检测时,最多校验的空闲连接数量
    }
}

作者:洞窝-孟源

相关推荐
LeeZhao@37 分钟前
【狂飙全模态】狂飙AGI-智能答疑助手
数据库·人工智能·redis·语言模型·aigc·agi
无代码专家44 分钟前
设备巡检数字化解决方案:构建高效闭环管理体系
java·大数据·人工智能
tanxiaomi1 小时前
Redisson分布式锁 和 乐观锁的使用场景
java·分布式·mysql·面试
零匠学堂20251 小时前
移动学习系统,如何提升企业培训效果?
java·开发语言·spring boot·学习·音视频
小杨快跑~1 小时前
从装饰者到桥接再到工厂:模式组合的艺术
java·开发语言·设计模式
i***11861 小时前
Windows环境下安装Redis并设置Redis开机自启
数据库·windows·redis
饕餮争锋1 小时前
Spring内置的Bean作用域介绍
java·后端·spring
却话巴山夜雨时i1 小时前
394. 字符串解码【中等】
java·数据结构·算法·leetcode
张人大 Renda Zhang2 小时前
Java 虚拟线程 Virtual Thread:让“每请求一线程”在高并发时代复活
java·jvm·后端·spring·架构·web·虚拟线程