在RichCMS的系统中,可以使用数据库备份功能,分为自动备份和手动备份两种情况,备份后的数据库文件存放在 /backup目录中。关于此功能的使用,请参考文章《RichCMS的数据库备份功能使用详解》中的操作。

本文章只分析数据库备份的关键源代码

源文件所在地址:/src/cms/service/db_backup_service.go

package service

import (
	"archive/tar"
	"compress/gzip"
	"database/sql"
	"errors"
	"fmt"
	"github.com/go-sql-driver/mysql"
	"github.com/zituocn/richcms/common/repository/model"
	"github.com/zituocn/richcms/common/sdk/dump"
	"github.com/zituocn/richcms/common/utils"
	"github.com/zituocn/richcms/src/cms/conn"
	"io"
	"os"
	"path"
)

// DBBackupService 数据库备份服务
type DBBackupService struct {

	// 目录
	dir string

	// 文件名
	filename string

	// 是否压缩成tar.gz
	isCompress bool

	// 文件大小
	size int64

	uid int64

	username string

	remark string
}

// NewDBBackupService 返回一个备份的服务
//
//	dir = 目录
//	filename = 文件名,不包括扩展名
//	压缩时,生成 tar.gz
//	不压缩时,生成 .sql
func NewDBBackupService(dir, filename string, isCompress bool, uid int64, username string, remark string) (*DBBackupService, error) {
	if dir == "" || filename == "" {
		return nil, errors.New("参数错误")
	}
	dir = path.Clean(dir)
	if !utils.PathExists(dir) {
		err := os.MkdirAll(dir, os.ModePerm)
		if err != nil {
			return nil, err
		}
	}
	return &DBBackupService{
		dir:        dir,
		filename:   filename,
		isCompress: isCompress,
		uid:        uid,
		username:   username,
		remark:     remark,
	}, nil
}

// Start 开始备份
//
//	开始处理备份,并记录数据
func (m *DBBackupService) Start() (err error) {
	config, err := m.loadConfig()
	if err != nil {
		return fmt.Errorf("读取mysql配置文件错误:%v", err)
	}
	db, err := sql.Open("mysql", config.FormatDSN())
	if err != nil {
		return fmt.Errorf("打开数据库错误:%v", err)
	}
	dumper, err := dump.Register(db, m.dir, m.filename)
	if err != nil {
		return fmt.Errorf("注册数据库错误:%v", err)
	}
	defer func(dumper *dump.Data) {
		_ = dumper.Close()
	}(dumper)
	err = dumper.Dump()
	if err != nil {
		return fmt.Errorf("生成.sql文件错误 :%v", err)
	}

	if m.isCompress {
		err = m.compress()
		if err != nil {
			return err
		}
	}
	//保存数据库
	err = m.create()
	if err != nil {
		return err
	}

	return
}

/*
private
*/

// loadConfig 读取数据库配置文件
//
//	读取配置文件中的mysql信息
func (m *DBBackupService) loadConfig() (*mysql.Config, error) {
	conf, _, err := conn.GetDBConfig()
	if err != nil {
		return nil, err
	}
	config := mysql.NewConfig()
	config.User = conf.User
	config.Passwd = conf.Password
	config.DBName = conf.Name
	config.Net = "tcp"
	config.Addr = fmt.Sprintf("%s:%d", conf.Host, conf.Port)
	return config, nil
}

// compress 压缩sql文件为 tar.gz 格式
func (m *DBBackupService) compress() (err error) {
	sqlName := path.Join(m.dir, m.filename+".sql")
	tarName := path.Join(m.dir, m.filename+".tar.gz")

	f, err1 := os.Open(sqlName)
	if err1 != nil {
		return err1
	}
	defer func(f *os.File) {
		_ = f.Close()
	}(f)

	info, err2 := f.Stat()
	if err2 != nil {
		return err2
	}
	m.size = info.Size()
	file, err := os.OpenFile(tarName, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
	if err != nil {
		return err
	}
	defer func(file *os.File) {
		_ = file.Close()
	}(file)

	gz := gzip.NewWriter(file)
	defer func(gz *gzip.Writer) {
		_ = gz.Close()
	}(gz)

	tw := tar.NewWriter(gz)
	defer func(tw *tar.Writer) {
		_ = tw.Close()
	}(tw)

	header, err3 := tar.FileInfoHeader(info, "")
	if err3 != nil {
		return err3
	}

	err = tw.WriteHeader(header)
	if err != nil {
		return err
	}
	_, err = io.Copy(tw, f)
	if err != nil {
		return err
	}
	err = os.Remove(sqlName)
	if err != nil {
		return err
	}
	return nil
}

// create 生成数据库结果
func (m *DBBackupService) create() error {
	backupLog := &model.BackupLog{
		BackupType: 1,
		Uid:        m.uid,
		Username:   m.username,
		Dir:        m.dir,
		Size:       m.size,
		Filename:   m.filename,
		Remark:     m.remark,
		Created:    utils.NowUnixTime(),
	}
	if m.isCompress {
		backupLog.Filename = backupLog.Filename + ".tar.gz"
	} else {
		backupLog.Filename = backupLog.Filename + ".sql"
	}
	err := backupLog.Create()
	if err != nil {
		return err
	}
	return nil
}

源代码分析:

  • 使用 NewDBBackupService函数,可以返回一个新的备份服务;
  • 调用 备份服务的Start()方法,可以开始一次新的备份,如果有备份错误,通过返回的error处理错误;
  • compress() 函数是一个压缩函数,可的把备份的.sql文件,打包压缩成 .tar.gz文件;
  • create() 函数,是记录此次备份的信息到RichCMS的系统,备份后的记录,可以在 管理平台->工具->数据库备份处查阅到;

数据库备份功能,是RichCMS系统中的一个重要工具,建议站长养成手动备份或自动备份的好习惯。