news 2026/4/13 18:52:00

一次受限环境下的 MySQL 数据导出与“可交付化”实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
一次受限环境下的 MySQL 数据导出与“可交付化”实践

平时其实很少会专门写数据库导出的事情。

这种活本身并不复杂,零零散散也做过很多次,大多数时候也不会留下什么记录。

这一次之所以单独记下来,主要还是因为当时遇到了一些​比较具体、也比较现实的限制条件

我需要在比较短的时间里接手一个并不熟悉的 MySQL 实例,把里面的数据整理出来,而且这些数据最终并不是只给工程师看。


从一开始就意识到的一个问题

在动手之前,其实有一件事情我是比较明确的:

.sql文件对工程师很友好,但对非技术人员几乎没有可用性。

对工程师来说:

  • .sql是最可靠的备份形式
  • 可以恢复、可以校验、可以长期保存

但换一个视角:

  • 很多人甚至不知道怎么打开.sql
  • 就算打开了,也很难直接理解表结构
  • 想筛选、查某一条记录,几乎是不可能的

也就是说,​单纯把数据库备份下来,并不等于问题已经解决了

后面迟早还是要把数据整理成一种“能被直接使用”的形式。

所以我当时心里其实是把这件事拆成了两步:

  1. 先保证数据完整地留下来
  2. 再考虑怎么把数据变成别人也能看懂的样子

先做一份完整的数据库备份

基于这个判断,我做的第一件事,还是先把整个 MySQL 实例完整备份下来。

这一步本身并不复杂,也谈不上什么技巧,只是对我来说,​先有一份全量、可恢复的备份,会比较安心。后面无论怎么处理数据,至少不会有“回不去”的问题。

为了省事,我写了一个简单的 shell 脚本,用来:

  • 自动获取所有业务数据库
  • 排除系统库
  • 逐个数据库执行mysqldump
  • 直接流式压缩成.sql.gz

脚本本身也只是把平时常用的命令整理了一下:

#!/usr/bin/env bash## gunzip < app.sql.gz | mysql -u root -p## nohup ./dump_all_dbs.sh host port root 'password' > 备份日志.log 2>&1 &set-eHOST="$1"PORT="$2"USER="$3"PASS="$4"if[$#-ne4];thenecho"Usage:$0<host> <port> <user> <password>"exit1fiOUT_DIR="Mysql备份_$(date+%F_%H%M%S)"mkdir-p"$OUT_DIR"MYSQL="mysql -h${HOST}-P${PORT}-u${USER}-p${PASS}--batch --skip-column-names"DUMP_BASE_OPTS=" --single-transaction --routines --events --triggers --hex-blob --set-gtid-purged=OFF --default-character-set=utf8mb4 "echo"==> 正在从获取数据库列表${HOST}:${PORT}"DATABASES=$($MYSQL -e" SELECT schema_name FROM information_schema.schemata WHERE schema_name NOT IN ('mysql','information_schema','performance_schema','sys'); ")if[-z"$DATABASES"];thenecho"未找到数据库!"exit0fiecho"==> 要转储的数据库:"echo"$DATABASES"echoforDBin$DATABASES;doFILE="${OUT_DIR}/${DB}.sql.gz"echo"==> 转储数据库:${DB}"mysqldump\-h${HOST}-P${PORT}-u${USER}-p${PASS}\$DUMP_BASE_OPTS\--databases"$DB"\|gzip>"$FILE"echo" -> 完成:$FILE"doneechoecho"所有数据库均已成功转储."echo"输出目录:${OUT_DIR}"

做到这里,其实“数据有没有丢”这个问题就已经基本不用担心了。


按需导出某一部分数据

接下来遇到的,是更偏实际使用层面的问题。

在整理数据的过程中,经常会有一些很具体的需求,比如:

  • 只需要看某一张表
  • 或者想先筛选一部分数据出来看看

这时候,如果只剩下一堆.sql文件,其实并不太好用。

所以我写了一个很简单的 PHP CLI 脚本,用来把一条 SQL 查询的结果直接导出成 CSV。

这个脚本的目标也很单纯:

  • 能处理数据量比较大的表
  • 不一次性把数据全部读进内存
  • 导出的文件可以直接用 Excel 打开
<?php// 单文件 CLI:MySQL 导出 CSVif($argc<2){echo<<<HELPUsage: php export.php <output_csv_path> Example: php export.php /data/output/users.csvHELP;exit(1);}$outputCsv=$argv[1];// MySQL 配置$dbConfig=['host'=>'127.0.0.1','port'=>3306,'dbname'=>'dbname','username'=>'root','password'=>'password','charset'=>'utf8mb4',];// SQL$sql=<<<SQLselect * from bl_danmu_logsSQL;$dsn=sprintf('mysql:host=%s;port=%d;dbname=%s;charset=%s',$dbConfig['host'],$dbConfig['port'],$dbConfig['dbname'],$dbConfig['charset']);try{$pdo=newPDO($dsn,$dbConfig['username'],$dbConfig['password'],[PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_DEFAULT_FETCH_MODE=>PDO::FETCH_ASSOC,PDO::MYSQL_ATTR_USE_BUFFERED_QUERY=>false,]);}catch(PDOException$e){fwrite(STDERR,"数据库连接失败:{$e->getMessage()}\n");exit(1);}$dir=dirname($outputCsv);if(!is_dir($dir)){mkdir($dir,0777,true);}$fp=fopen($outputCsv,'w');if($fp===false){fwrite(STDERR,"无法写入 CSV 文件\n");exit(1);}fwrite($fp,"\xEF\xBB\xBF");$stmt=$pdo->prepare($sql);$stmt->execute();$rowCount=0;$headerWritten=false;while($row=$stmt->fetch()){if(!$headerWritten){fputcsv($fp,array_keys($row));$headerWritten=true;}fputcsv($fp,array_values($row));$rowCount++;if($rowCount%100000===0){echo"已导出{$rowCount}行\n";}}fclose($fp);echo"导出完成,共{$rowCount}行\n";

这个脚本更多是用来应对一些临时、零散的导出需求,本身也不复杂。


真正的难点在“交付”这一步

真正让我花时间的,其实是后面这一部分。

如果只是从工程角度看,.sql已经足够完整;

但从使用角度看,这些数据仍然很难被直接消费

问题包括:

  • 表很多,一个一个手工导出不现实
  • Excel 有行数限制,大表没法一次性打开
  • 字段名是英文或缩写,不看表结构根本不知道是什么意思

所以后来我又写了一个脚本,用来把整个数据库的数据,整理成一组 CSV 文件。

这个脚本做的事情也很朴素:

  • 遍历数据库中的所有表
  • 读取字段注释,作为 CSV 的表头
  • 数据量大的表按行数自动拆分文件
  • 所有文件都可以直接用 Excel 打开

这些逻辑都不复杂,只是把原本需要重复做的事情集中到了一起。

这段代码略长,我把它放在了我的个人网站中:点击查看


一点事后的感受

这次整理下来,我的感受其实挺明确的:

  • 技术本身并不复杂
  • 真正需要花心思的,是站在使用者的角度去看数据

对工程师来说,数据库和 SQL 已经很直观了;

但对不直接使用数据库的人来说,​Excel 才是他们真正熟悉的工具

这套脚本对我而言,并不是什么通用方案,只是当时在那个条件下,一种比较顺手、也能把事情做完的办法。

记录下来,也只是给自己以后再遇到类似情况时,留一个参考。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/10 21:21:25

SPI接口在Arduino创意作品中的传感器扩展应用

让你的Arduino项目“耳聪目明”&#xff1a;用SPI打通多传感器的高速通道 你有没有遇到过这样的窘境&#xff1f; 想做一个环境监测站&#xff0c;结果温度、湿度、气压、光照、振动……传感器一加&#xff0c;Arduino的引脚就不够用了&#xff1b; 想做个手势控制灯&#xf…

作者头像 李华
网站建设 2026/4/10 5:52:41

PaddlePaddle镜像如何实现离线环境部署?内网安装包制作

PaddlePaddle离线部署实战&#xff1a;构建内网可用的AI环境 在金融、政务和高端制造等对数据安全极为敏感的领域&#xff0c;生产系统往往运行于完全隔离的内网环境中。这种“断网”状态虽然保障了信息安全&#xff0c;却给深度学习框架的部署带来了巨大挑战——像PaddlePaddl…

作者头像 李华
网站建设 2026/4/8 1:55:29

百万 QPS 下的 Java 服务调优:JVM 参数、GC 策略与异步非阻塞编程

目标读者&#xff1a;中高级 Java 工程师、系统架构师、性能优化工程师在高并发场景下&#xff0c;如何让 Java 应用稳定支撑百万级 QPS&#xff08;Queries Per Second&#xff09;&#xff1f;这不仅是对代码质量的考验&#xff0c;更是对 JVM 调优、垃圾回收策略、线程模型和…

作者头像 李华
网站建设 2026/4/12 4:19:29

三极管工作状态与光电隔离电路的协同设计:项目应用

三极管驱动光耦的底层逻辑&#xff1a;如何让隔离电路真正“稳如泰山”&#xff1f; 在工业控制现场&#xff0c;你是否遇到过这样的问题——明明传感器已经断开&#xff0c;PLC输入点却还在“抖动”&#xff1f;或者远程信号时好时坏&#xff0c;查了半天发现是某路输入误触发…

作者头像 李华
网站建设 2026/4/10 6:49:42

硬件电路设计原理分析:实战案例剖析电源管理电路

从“供电”到“供好电”&#xff1a;电源管理电路设计的实战心法你有没有遇到过这样的场景&#xff1f;系统其他部分都调通了&#xff0c;结果一接电机或无线模块&#xff0c;MCU莫名其妙重启&#xff1b;ADC采样数据像心电图一样跳动不止&#xff1b;示波器一探&#xff0c;电…

作者头像 李华
网站建设 2026/4/9 19:36:27

ESP32接入大模型的语音交互流程:系统学习版

用ESP32打造会“思考”的语音助手&#xff1a;从录音到云端大模型的完整链路实战你有没有想过&#xff0c;一块成本不到30元的ESP32开发板&#xff0c;也能实现类似Siri或小爱同学那样的自然对话&#xff1f;它能听懂你说的话&#xff0c;理解语义&#xff0c;甚至讲个笑话、帮…

作者头像 李华