臭名昭著aTrust隔离计划1.臭名昭著aTrust隔离计划2.VPN原理与aTrust隔离网络实践3.docker-easyconnect到底做了什么4.TUN(tunnel-隧道-虚拟网卡)模式
个人项目
personal(个人展示)GitHub Actions 自动部署个人展示项目
基于Quartz搭建的个人博客1.Quartz个人博客使用教程2.使用 rsync 增量部署 Quartz 博客3.使用 GitHub Actions 自动部署 Quartz 博客5.域名绑定6.CDN加速7.Google 搜索收录排查:为什么关键词搜得到别人,却搜不到我的网站?Obsidian、双向链接与知识图谱
架构设计
分布式分布式事务RPC原理
旅行
行程攻略青岛三日游行程攻略
前端
NginxNginx配置与反向代理入门
Nodenpm与npx的区别
数据库
mysql01.MySQL基础语法与约束02.MySQL数据库类型03.MySQL多表查询04.MySQL 索引05.MySQL 事务06.MySQL 隔离级别07.MySQL MVCC08.MySQL 锁09.EXPLAIN10.慢查询与 SQL 优化
mysql面试题01.索引问题02.事务与隔离级别03.锁机制04.存储引擎与性能优化05.用户权限、字符集与备份恢复
mysql实战01.EXPLAIN实战02.测试环境菜单误删排查与恢复03.MySQL IN 详解
网络
网络安全1.无线监听网卡苹果M系列虚拟机安装Kali Linux教程3.kali怎么使用RTL8812AU4.kali基础命令
网络基础进程之间怎样通过网络传递数据
网址常用网址
中间键
esElasticsearch 入门
mq
RabbitMQRabbitMQ高级RabbitMQ基础
1. MQ 基础2. MQ 核心角色与代码对应3. MQ Consumer Group 与消费模式4. SOFAMQ 账户变更代码实践5. MQ 可靠性问题6. 消息幂等
redis缓存击穿、穿透与雪崩Redis 分布式锁Redis 缓存问题Redis 数据结构
AI
使用说明
第三方插件&技能简介Archify使用与安装指南geekgeekRun使用OfferFlow使用指南Ponytail使用指南Skills Manager使用指南
CodexCodex CLI与IDE区别及使用指南Codex Hook单独配置与提交通知Codex MCP安装与使用指南Codex第三方插件安装与使用指南
Agent开发0-学习路线1.LLM、Token、上下文窗口与模型参数2.大模型 API、请求参数与响应结构3.Spring AI ChatClient4.Prompt、System Prompt、Prompt 模板6.结构化输出、JSON Schema7.SSE 流式响应8.会话 ID、聊天记录、Redis9.超时、重试、限流、降级10.*完成可运行聊天接口11.Function Calling、Tool Calling 原理12.用 Java 方法定义 Tool13.工具参数设计和校验14.实现订单查询工具15.实现物流查询工具16.实现库存查询工具17.多个工具自动选择18.工具异常、超时、重试
diy主机
主机零件学习1.CPU2.主板3.内存4.显卡5.硬盘6.散热7.风扇8.机箱热门型号价格表
捡垃圾diy-13600kf+5060diy-14600kf+5070
English高频词汇英文基础语法简单句子音标
GitGit常用命令Sublime Merge 使用指南
Java
工具库Caffeine本地缓存Reactor教程
工作实践如何使用@Async线程池使用与排查线上服务 CPU 使用率过高排查压测下RPC接口大量失败排查记录
基础1.HashMap2.ConcurrentHashMap3.HashSet4.final5.static6.volatile7.AtomicInteger8.String动态代理反射锁与线程同步线程注解
面试题1.HashMap 存储在方法内是线程安全的吗2.ArrayList参数传递与引用重新赋值3.== 和 equals 的区别、hashcode4.重载和重写的区别5.String、StringBuffer、StringBuilder区别及使用场景6.接口和抽象类7.ArrayList 与 LinkedList 的区别和扩容机制8.HashMap 和 Hashtable 的区别及底层实现9.Object 和 Objects 的区别10.ThreadLocal的原理和使用场景11.字节码是什么及作用12.Java类加载器有哪些13.双亲委派模型14.Java异常体系15.GC是什么16.GC如何判断对象可以被回收
面试总结0.HR面试模板1.面试一2.面试二3.面试三笔试
网络与 IO(黑马程序员-纯理论)01-网络编程基础02-IO模型与零拷贝03-BIO04-NIO基础与缓冲区05-NIO通道与选择器06-AIO
juc00-目录01-进程与线程02-线程同步与通信03-并发设计模式04-Java内存模型05-CAS与原子类06-ThreadLocal07-阻塞队列08-线程池使用与原理09-任务调度10-ForkJoin与享元模式11-AQS12-ReentrantLock13-读写锁14-并发协作工具15-ConcurrentHashMap16-CopyOnWrite与同步容器17-SkipListMap18-非阻塞队列
juc(工作常见)Java并发编程三个重点拆解
jvm1. JVM运行时数据区
Spring1.Spring IoC、DI与构造方法注入CGLIB、Byte Buddy 与 Spring AOPSpring 常用注解详解Spring Bean 生命周期
Spring MVC异常处理机制
linux常用命令
mac快捷键终端常用操作
python
基础01.Python是什么,为什么要安装Python02.Python项目中.venv是什么03.Python基础语法
基础实战sql替换
HTTPXHRMS员工详情接口调用(Python HTTPX)
windowswindows激活

测试环境菜单误删排查与恢复

需求背景

测试环境的菜单被误删,需要排查是否存在备份,找回菜单及必要的关联数据,并在核对后还原。

目前尚未确认数据库实例、库表、误删时间、菜单范围及备份情况。以下为排查方案,不代表已经找到备份或执行恢复。

1. 先确定误删范围

向操作人确认并记录:

  • 测试环境对应的数据库实例和库名。
  • 菜单表、角色菜单关联表,以及其他实际涉及的关联表。
  • 大概误删时间、操作入口、菜单名称或 ID。
  • 是否包含子菜单、按钮权限,是否同时删除了角色授权关系。
  • 是物理删除,还是通过删除标记实现的逻辑删除。

先只读查看表结构和现有数据。若为逻辑删除,优先核查删除标记及关联数据状态;不要直接套用物理删除恢复流程。

其他环境没有对应数据时,优先查本环境的备份、快照、日志归档和 binlog。其他环境即使存在相似菜单,也要核对版本、ID 和权限关系,不能直接复制。

2. 优先排查备份

向 DBA 或环境维护人员确认:

  • 是否有删除前的全量备份、快照或逻辑导出文件。
  • 是否支持按时间点恢复,保留时间是否覆盖误删时间。
  • 是否有 binlog 归档,以及对应实例和时间段。

有备份时,先恢复到独立临时库或临时实例,再提取目标菜单及关联数据。不要直接用旧备份覆盖整个测试库,以免覆盖误删之后的其他有效变更。

只有较早备份时,可评估结合连续 binlog,将临时实例恢复到误删操作之前。具体恢复方式取决于备份类型和数据库平台能力。

3. 查询 binlog 配置和文件

以下 SQL 均为只读查询,可在目标测试实例执行:

SELECT VERSION(), @@hostname, @@port, DATABASE();
 
SHOW VARIABLES WHERE Variable_name IN (
  'log_bin',
  'binlog_format',
  'binlog_row_image',
  'log_bin_basename',
  'binlog_expire_logs_seconds',
  'expire_logs_days'
);
 
SHOW BINARY LOGS;

部分变量随 MySQL 版本不同而不存在,查询结果中没有对应项不代表查询失败。

配置排查含义
log_bin当前是否开启 binlog,还需确认误删时是否开启
binlog_formatROW 可记录行变更;STATEMENT 通常记录执行语句;MIXED 需要查看实际事件
binlog_row_image行模式下,FULL 通常记录完整删除前镜像;MINIMAL 可能仅有定位字段,无法仅靠该事件还原整行
log_bin_basename当前日志文件的基础路径,读取通常需要服务器文件权限
日志过期配置用于了解保留策略,实际可用范围以现存文件和归档为准

当前配置不能证明误删时的配置。日志存在也不保证一定包含目标操作,还需要核对实例、时间、记录内容和是否存在日志过滤等情况。

参考:MySQL 二进制日志配置。

4. 解析误删时间段

以下为终端命令,不是在 SQL 窗口执行。由有权限的人员使用与服务器版本兼容的 mysqlbinlog 工具,将路径和时间替换为实际值:

mysqlbinlog \
  --base64-output=DECODE-ROWS \
  -vv \
  --start-datetime="2026-10-28 09:00:00" \
  --stop-datetime="2026-10-28 12:00:00" \
  /var/lib/mysql/binlog.000123 > /tmp/menu_binlog_check.txt

上面的时间仅为示例。时间参数按运行工具的机器本地时区解释,需要与误删时间统一。若时间段跨多个文件,应按顺序解析全部相关文件,避免遗漏事务或表映射事件。

在结果中搜索实际库名、表名和菜单 ID,检查附近的事务边界、事件时间及删除内容。行模式示例:

### DELETE FROM `example_db`.`menu`
### WHERE
###   @1=12345
###   @2='示例菜单'
###   @3=100

这里是删除前行数据的伪 SQL 展示,并非一定等于原始执行 SQL。@1、@2、@3 是字段序号,需要结合删除时的表结构解释,不能只按当前表结构盲目转换。

注意同时排查菜单表和授权关联表。行模式日志通常不能直接确定业务操作人;需要追查“谁删的”时,还要结合已留存的数据库审计日志或应用操作日志。

参考:mysqlbinlog 使用说明、行事件展示说明。

5. 准备恢复数据

从临时恢复库或完整的删除前行镜像中提取数据后,先只读核对:

  • 原菜单 ID 是否已被重新使用,是否已经有人手工补建。
  • 父菜单是否存在,子菜单与按钮权限是否完整。
  • 路由、组件、权限编码、排序、启用状态及所属系统或租户是否正确。
  • 角色菜单关联是否也被删除;只恢复菜单不一定能让原用户重新看到菜单。
  • 当前表结构、字段约束与备份时是否一致。

保留原 ID 通常有助于恢复关联关系,但必须先排除主键冲突和业务冲突。恢复 SQL 应明确列出字段和目标数据,不直接回放整段 binlog,也不批量恢复同一事务内与菜单无关的变更。

如果只有 MINIMAL 删除事件中的主键,需要继续寻找备份或其他可信历史数据,不能据此推断缺失字段。

6. 执行前确认与恢复后验证

所有数据库写入都需要先展示以下内容,并等待用户明确回复 “确认执行”:

  1. 目标环境、实例、数据库和表。
  2. 完整 SQL 或操作内容,包括实际使用的事务控制语句。
  3. 预计新增或修改的行数,以及菜单和关联记录范围。
  4. 冲突核查结果、备份方式和回退方案。

对于支持事务的表,优先使用事务并核对影响行数;事务执行及提交仍需在上述确认范围内。备份若需要建表或写入数据,也必须先获得确认。

恢复后验证数据库记录、菜单父子关系、前端菜单展示、路由访问和对应角色权限。若系统有菜单缓存,按实际机制刷新并验证。

7. 没有备份或 binlog 时

继续排查云平台日志归档、历史导出、初始化脚本、代码中的菜单配置、应用审计日志等来源。审计日志可能只有操作语句或 ID,不一定包含完整删除前数据。

若仍没有完整数据,只能根据需求重建菜单及授权关系,并明确这是重建,无法保证与误删前完全一致。现在开启 binlog 不能补回历史记录。

当前待补充信息

  • 测试数据库实例与库名。
  • 菜单及关联表名。
  • 误删时间段、菜单名称或 ID。
  • 备份、快照、binlog 的可用范围。
  • 是否有人已经手工补建或修改相关数据。