在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系统中的一个重要工具,建议站长养成手动备份或自动备份的好习惯。