写点什么

定时导出数据库数据并备份到 gitee 和邮箱

作者:JYeontu
  • 2024-11-03
    广东
  • 本文字数:5807 字

    阅读完需:约 19 分钟

定时导出数据库数据并备份到gitee和邮箱

说在前面

现在大多数人都有自己的服务器,也会利用服务器搭建一些个人网站,那么有多少人会定时备份自己服务器的数据库数据呢?为了防止突发情况的发生,我们还是需要定时对自己数据库数据进行备份,今天我们一起来看看怎么使用 node 编写一个备份脚本,一键将数据备份到 gitee 和邮箱中。

脚本编写

1、配置信息填写

邮箱配置

这里我使用的 qq 邮箱作为发件账号,需要开启邮箱授权,获取授权码。


{  host: "smtp.qq.com", // 主机  secureConnection: true, // 使用 SSL  port: 465, // SMTP 端口  auth: {    user: "jyeontu@qq.com", // 自己用于发送邮件的账号    pass: "jyeontu", // 授权码(这个是假的,改成自己账号对应即可,获取方法: QQ邮箱-->设置-->账户-->POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务-->IMAP/SMTP开启 复制授权码)  }}
复制代码


  • (1)打开 pc 端 qq 邮箱,点击设置,再点击帐户



  • (2)往下拉 可开启 POP3/SMTP 服务 根据提示即可获取 qq 邮箱授权码



  • (3)将获取到的授权码复制到配置信息里即可


数据库配置

填写数据库对应的配置信息。


{  host: "localhost",  user: "root", //数据库账号  password: "jyeontu", //数据库密码  database: "test", //数据库名称}
复制代码

gitee 仓库配置

这里我们可以使用 gitee 来做一个免费的备份仓库,把备份数据推送到 gitee 上,这里需要填写一下 gitee 仓库的配置信息。


  • (1)新建私有数据库



因为我之前做了一个书签同步和 tab 标签保存的功能,数据也都放在这个仓库里,所以直接新建了一个目录sqlData用于保存数据库备份数据。


  • (2)配置 ssh 公钥因为我们最后还需要将数据同步推送到远程仓库,不配置 ssh 认证的话,每次推送都需要输入密码,所以需要先配置下,配置好之后就可以不输密码,一键推送数据,不懂配置的同学可以 Google 一下,网上很多教程的,这里就不多说了。

  • (3)拉取仓库到本地并填写配置信息创建好仓库后我们现在本地拉取一下,然后将仓库的本地地址和数据库存放目录填写到配置信息即可。


{  giteePath: "E:/A_Gitee/jyeontu_info_storage", //用于保存数据的gitee仓库  dir: "sqlData", //存放数据库数据的目录名}
复制代码


2、数据库数据导出

(1)引入模块

const mysql = require("mysql");const fs = require("fs");const archiver = require("archiver");const { dbConfig } = require("./config.js");
复制代码


  • mysql:用于连接和操作 MySQL 数据库。

  • fs:文件系统模块,用于文件操作,如写入文件。

  • archiver:用于创建压缩文件。

  • ./config.js引入dbConfig,获取数据库连接配置信息。

(2)连接数据库

const connection = mysql.createConnection(dbConfig);
function connectDatabase() { return new Promise((resolve) => { connection.connect((error) => { if (error) throw error; console.log("成功连接数据库!"); resolve("成功连接数据库!"); }); });}
复制代码


  • 返回一个 Promise,在数据库连接成功时,打印“成功连接数据库!”并 resolve 一个成功连接的消息字符串。如果连接失败,抛出错误。

(3)导出表数据

function exportTableData(tableName) {  return new Promise((resolve) => {    connection.query(      `SHOW CREATE TABLE ${tableName}`,      (error, results, fields) => {        if (error) throw error;        const createTableStatement = results[0]["Create Table"];        connection.query(          `SELECT * FROM ${tableName}`,          (error, results, fields) => {            if (error) throw error;
const insertStatements = results .map((row) => { const keys = [], values = []; Object.entries(row).forEach((item) => { keys.push(item[0]); values.push(`'${item[1]}'`); }); return `INSERT INTO ${tableName} (${keys.join( "," )}) VALUES (${values.join(",")});`; }) .join("\n");
const sql = `${createTableStatement};\n\n${insertStatements}`;
fs.writeFile( `${dbConfig.database}/${tableName}.sql`, sql, (err) => { if (err) throw err; console.log(`表${tableName} 数据已成功导出.`); resolve(`表${tableName} 数据已成功导出.`); } ); } ); } ); });}
复制代码


  • 接受一个表名作为参数,返回一个 Promise。

  • 首先查询指定表的创建语句。

  • 然后查询表中的所有数据。

  • 将数据转换为插入语句格式,并与创建语句拼接成完整的 SQL 内容。

  • 将 SQL 内容写入以表名命名的.sql文件中,如果写入成功,打印表名和“数据已成功导出.”的消息并 resolve 相应消息字符串。如果写入失败,抛出错误。

(4)数据库查询

function mysqlQuery() {  return new Promise((resolve) => {    connection.query("SHOW TABLES", (error, results) => {      if (error) throw error;      resolve(results);    });  });}
复制代码


  • 返回一个 Promise,执行查询所有表名的 SQL 语句,将查询结果 resolve 出去。

(5)获取所有表数据

async function getAllTableData() {  const reqList = [];  const results = await mysqlQuery();  results.forEach((result, index) => {    const tableName = result[`Tables_in_${dbConfig.database}`];    reqList.push(exportTableData(tableName));  });  await Promise.all(reqList);  console.log("已全部导出!");}
复制代码


  • 首先调用mysqlQuery获取所有表名。

  • 遍历所有表名,对每个表名调用exportTableData函数,并将返回的 Promise 添加到reqList数组中。

  • 使用Promise.all等待所有表数据导出完成。

(6)打包成压缩文件

async function getZip() {  return new Promise((resolve) => {    const output = fs.createWriteStream(`${dbConfig.database}.zip`);    const archive = archiver("zip", {      zlib: { level: 9 }, // 设置压缩级别    });    output.on("close", function () {      console.log("压缩完成," + archive.pointer() + " total bytes");      resolve("finished");    });    archive.on("error", function (err) {      throw err;    });    archive.pipe(output);    // 要压缩的目录路径    const sourceDir = dbConfig.database;    archive.directory(sourceDir, false);    archive.finalize();  });}
复制代码


  • 使用fs.createWriteStream创建一个可写流output,用于将压缩后的数据写入一个以数据库名称命名的.zip文件中。

  • 使用archiver模块创建一个归档器archive,指定压缩格式为zip,并通过设置zlib选项的level属性为9来设置较高的压缩级别,以获得更好的压缩效果。

  • 为输出流output添加一个close事件监听器。当压缩完成,输出流关闭时,这个监听器会被触发。它会在控制台打印压缩完成的消息,并包含总共写入的字节数(通过archive.pointer()获取)。然后,调用resolve函数来通知 Promise 已成功完成,传递一个字符串"finished"作为成功的结果。

  • 为归档器archive添加一个error事件监听器。如果在压缩过程中发生错误,这个监听器会被触发,并抛出错误,以便在调用getZip函数的地方进行错误处理。

  • 使用archive.pipe(output)将归档器和输出流连接起来。这意味着归档器生成的压缩数据将被自动写入输出流。

  • 指定要压缩的目录路径为dbConfig.database所代表的目录。

  • 使用archive.directory(sourceDir, false)将指定目录添加到归档器中。第二个参数false表示不保留目录的完整路径在压缩包中(只包含目录内的内容)。

  • 最后,调用archive.finalize()开始压缩过程。这个方法会触发归档器开始将数据写入输出流,并在完成后触发输出流的close事件。

3、将数据库数据发送到邮箱

(1)引入模块和配置

const nodemailer = require("nodemailer");const { mail, dbConfig } = require("./config.js");
复制代码


  • 引入nodemailer模块,用于发送邮件。

  • ./config.js文件中引入maildbConfig对象,包含邮件发送的配置信息以及数据库相关的配置。

(2)创建邮件传输对象

const smtpTransport = nodemailer.createTransport(mail);
复制代码


  • 使用nodemailer.createTransport方法创建一个邮件传输对象smtpTransport

(3)定义发送邮件函数

const sendMail = () => {  return new Promise((resolve) => {    //...  });};
复制代码


  • 定义一个名为sendMail的函数,该函数返回一个 Promise,使得调用者可以异步等待邮件发送的结果。

(4)设置邮件选项

const mailOptions = {  from: "1311395125@qq.com", // 发件人地址  to: "jyeontu@163.com", // 收件人地址  subject: "数据库备份数据", // 邮件主题  text: dbConfig.database, // 邮件正文内容,这里可能是数据库的名称  attachments: [    {      filename: dbConfig.database + ".zip", // 附件文件名      path: dbConfig.database + ".zip", // 附件文件路径    },  ],};
复制代码


  • 创建一个mailOptions对象,设置邮件的发件人、收件人、主题、正文和附件。

  • 正文内容为从dbConfig中获取的数据库名称。

  • 附件设置为以数据库名称命名的.zip文件。

(5)发送邮件并处理结果

smtpTransport.sendMail(mailOptions, function (error, response) {  if (error) {    console.error("发送邮件失败:", error);  } else {    console.log("邮件发送成功");  }  smtpTransport.close(); // 发送完成关闭连接池  resolve(true);});
复制代码


  • 使用smtpTransport.sendMail方法发送邮件,传入mailOptions和一个回调函数。

  • 在回调函数中,如果有错误,打印错误信息;如果发送成功,打印“邮件发送成功”。

  • 发送完成后,调用smtpTransport.close关闭邮件传输对象的连接池。

  • 最后,调用resolve(true)通知 Promise 已成功完成。

4、将数据库数据同步到 gitee

以下是对这段代码的解释:

(1)引入模块和配置

const axios = require("axios");const child_process = require("child_process");const fs = require("fs");const { giteeConfig, dbConfig } = require("./config");
复制代码


  • 引入axios模块,用于发送 HTTP 请求。

  • 引入child_process模块,用于在 Node.js 中执行系统命令。

  • 引入fs模块,用于文件系统操作。

  • ./config文件中引入giteeConfigdbConfig对象,包含与 Gitee 相关的配置信息以及数据库配置信息。

(2)提取配置信息

const { giteePath, dir } = giteeConfig;const sourceFilePath = dbConfig.database + ".zip";const destinationFilePath = `${giteePath}/${dir}/${sourceFilePath}`;
复制代码


  • giteeConfig中提取giteePath(Gitee 仓库的本地路径)和dir(存放数据库文件的目录)。

  • 确定源文件路径为数据库名称加上.zip后缀。

  • 计算目标文件路径,即 Gitee 仓库路径下特定目录中的源文件路径。

(3)同步最新数据

// 同步最新数据console.log("正在同步最新数据……");child_process.execSync(`cd ${giteePath} && git pull`);console.log("已成功同步最新数据");
复制代码


  • 使用child_process.execSync执行命令,切换到 Gitee 仓库路径并执行git pull命令,以同步远程仓库的最新数据。

(4)更新数据库文件数据

// 更新数据库文件数据console.log("正在更新数据库文件数据……");fs.copyFileSync(sourceFilePath, destinationFilePath);console.log("已成功更新数据库文件数据");
复制代码


  • 使用fs.copyFileSync同步方式将源文件(数据库的压缩文件)复制到目标文件路径(Gitee 仓库中的特定位置)。

(5)推送到远程数据库

// 推送到远程数据库console.log("正在推送到远程数据库……");child_process.execSync(`cd ${giteePath} && git add.`);child_process.execSync(  `cd ${giteePath} && git commit -m 更新数据库${dbConfig.database}数据`);child_process.execSync(`cd ${giteePath} && git push`);console.log("已成功推送到远程数据库");
复制代码


  • 使用child_process.execSync依次执行命令,切换到 Gitee 仓库路径,执行git add.将所有更改添加到暂存区,执行git commit提交更改并提供提交信息,执行git push将更改推送到远程仓库。

5、定时备份

如果需要定时备份数据库数据的话,可以使用corn编写一个定时任务来定时执行脚本即可。


  • * * * * * *分别对应:秒、分钟、小时、日、月、星期。

  • 每个字段可以是具体的值、范围、通配符(*表示每一个)或一些特殊的表达式。例如:


0 0 * * *:每天午夜 0 点执行。0 30 9 * * 1-5:周一到周五上午 9:30 执行。
复制代码


你可以根据自己的需求设置合适的 cron 表达式来定时执行特定的任务。

测试

创建数据库

CREATE DATABASE test;
复制代码

创建表

CREATE TABLE `t_user` (  `id` int NOT NULL AUTO_INCREMENT,  `name` varchar(255) NOT NULL,  `age` int NOT NULL,  PRIMARY KEY (`id`))
复制代码

插入数据

INSERT INTO t_user (name,age) VALUES ('张三', 25);INSERT INTO t_user (name,age) VALUES ('李四', 24);
复制代码


导出数据

  • 本地导出数据


  • 邮件数据


  • gitee 仓库数据


使用

1、源码下载

git clone https://gitee.com/zheng_yongtao/node-scripting-tool.git
复制代码



2、依赖下载

npm install
复制代码

3、配置数据填写


这里的配置信息需要修改为你自己的信息,数据库、gitee 仓库、邮件配置。

4、脚本运行

node index.js
复制代码

更多脚本

该脚本仓库里还有很多有趣的脚本工具,有兴趣的也可以看看其他的:https://gitee.com/zheng_yongtao/node-scripting-tool




🌟觉得有帮助的可以点个 star~


🖊有什么问题或错误可以指出,欢迎 pr~


📬有什么想要实现的工具或想法可以联系我~



公众号

关注公众号『前端也能这么有趣』,获取更多有趣内容。

说在后面

🎉 这里是 JYeontu,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『前端也能这么有趣』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。

发布于: 刚刚阅读数: 4
用户头像

JYeontu

关注

技术~运动~分享 公众号:前端也能这么有趣 2023-02-07 加入

喜欢算法,GDCPC打过卡;热爱羽毛球,大运会打过酱油。毕业两年,三年前端开发经验,做过unity游戏开发,目前担任H5前端开发,算法业余爱好者。公众号:『前端也能这么有趣』

评论

发布
暂无评论
定时导出数据库数据并备份到gitee和邮箱_MySQL_JYeontu_InfoQ写作社区