数据库备份脚本

  1. 语言:Python 3
  2. 数据库:PostgreSQL 12
  3. 系统:Windows 10

相关目录参数

将可能需要更改的目录参数等放在起始部分,根据实际情况进行修改即可。

import psycopg2
import datetime
import os
import shutil

# 可更改的参数
#######################################################################################################################
# data目录
src = 'D:\\Program Files\\PostgreSQL\\12\\data'
# 备份存放目录
dst = 'D:\\Program Files\\PostgreSQL\\backup'
# 归档存放目录
archive_dir = 'D:\\Program Files\\PostgreSQL\\backup\\archivelog'
# pg_archivecleanup.exe所在目录
archivecleancommand_dir = "D:\\" + '"' + "Program Files" + '"' + "\\PostgreSQL\\12\\bin\\pg_archivecleanup.exe"
# log_file文件的路径
log_file = dst + '\\' + 'log.history'
# 备份保留个数
num = 3
#######################################################################################################################

数据库操作类

class BackupDb:
    # 初始化
    def __init__(self, database, user, pwd, host, port):
        self.database = database
        self.user = user
        self.pwd = pwd
        self.host = host
        self.port = port
        self.conn = None
        self.cur = None

连接及关闭数据库连接

    # 连接数据库
    def connect_db(self):
        self.conn = psycopg2.connect(database=self.database, user=self.user, password=self.pwd, host=self.host,
                                     port=self.port)
        if self.conn:
            self.cur = self.conn.cursor()

    # 关闭数据库
    def close_db(self):
        if self.conn:
            if self.cur:
                self.cur.close()
            self.conn.close()

备份准备

    # 备份前的准备工作
    def prepare_backup(self):
        # 备份目录编号,初始为1,递增
        nums = 1
        # 如果log_file存在,得到行数,获取最后一行中的编号信息,表示备份编号已经到达该数值。如果不存在就创建log_file文件
        if os.path.exists(log_file):
            with open(log_file, 'r') as lf:
                log_lines = lf.readlines()
                length = len(log_lines)
                # 最后一行中的编号信息
                nums = int(log_lines[length - 1].split(',')[0])
        else:
            self.create_log()
        # 搜寻备份路径,找到最后一次备份的目录编号,然后编号+1,准备进行备份
        while os.path.exists(dst + '\\' + str(nums)):
            nums += 1
        return nums

备份数据库

    # 备份data目录及其他目录中的表空间
    def database_bakcup(self, b_num):
        # 如果已经开始pg_start_backup,会生成backup_label,此时需要pg_stop_backup
        if os.path.exists(src + '\\backup_label'):
            self.cur.execute("SELECT pg_stop_backup()")
        else:
            # 以日期为备份标签
            backup_label = day_info
            self.cur.execute("SELECT pg_start_backup('" + backup_label + "', true)")
            # 将data的整个文件目录复制到备份路径下
            shutil.copytree(src, dst + '\\' + str(b_num))
            # 备份位于其他路径的表空间
            self.cur.execute("select pg_catalog.pg_tablespace_location(oid) FROM pg_catalog.pg_tablespace")
            tbs = self.cur.fetchall()
            # 存在其他路径的表空间
            if len(tbs) > 2:
                # 对这些表空间依次进行备份
                for tb in range(2, len(tbs)):
                    # 表空间路径
                    tb_src = tbs[tb][0]
                    # 表空间备份路径
                    tb_dst = dst + '\\' + str(b_num) + '\\' + tbs[tb][0].replace(':', '').replace('\\\\', '')
                    # 将表空间的整个文件目录复制到备份路径下
                    shutil.copytree(tb_src, tb_dst)
            self.cur.execute("SELECT pg_stop_backup()")

备份删除

    # 删除旧的备份
    def delete_old_backup(self):
        # 读取log_file文件的行数,即备份脚本运行次数,但序号可能有错,所以取最后一行中的序号,以此为最后一次的备份号
        with open(log_file, 'r') as lf:
            lines = lf.readlines()
            line_numbers = len(lines)
            # 提取最后一行信息中的序号
            target_index = int(lines[line_numbers - 1].split(',')[0])
        # 只保留num次备份,删除较早的备份
        for n in range(1, target_index - num + 1):
            dst_dir = dst + '\\' + str(n)
            if os.path.exists(dst_dir):
                try:
                    # 删除整个路径
                    shutil.rmtree(dst_dir)
                except Exception as err:
                    print(err)
        # 清理归档
        self.archive_clean()

清理归档

    # 清理归档文件
    @ staticmethod
    def archive_clean():
        # 读取log_file文件的行数,即备份脚本运行次数
        with open(log_file, 'r') as lf:
            lines = lf.readlines()
            line_number = len(lines)
        # 找到行号为[行数-备份保留个数(num)+ 1]的行,读取其WAL写入位置
        for ele in range(1, line_number + 1):
            if ele == line_number - num:
                target = lines[ele].split(',')[1]
                # pg_archivecleanup命令语句
                clean_command = archivecleancommand_dir + ' "' + archive_dir + '" ' + target
                # 执行pg_archivecleanup命令
                os.system(clean_command)

日志文件相关

    # 首次运行脚本时,创建log_file文件
    def create_log(self):
        # 获得当前预写式日志(WAL)写入位置
        self.cur.execute("select pg_walfile_name(pg_current_wal_lsn())")
        xlog_path = self.cur.fetchall()
        # 将信息写入log_file文件
        with open(log_file, 'w') as lf:
            lf.write(str(1) + ',' + xlog_path[0][0] + ',' + day_info)
        # 每行格式类似于 1,000000010000000000000031,20181122111930

    # 已经有log_file文件之后,每次运行脚本将依次向下添加信息
    def modify_log(self, log_num):
        # 获得当前预写式日志(WAL)写入位置
        self.cur.execute("select pg_walfile_name(pg_current_wal_lsn())")
        xlog_path = self.cur.fetchall()
        # 将信息追加到log_file文件中
        with open(log_file, 'a') as lf:
            lf.write('\n' + str(log_num) + ',' + xlog_path[0][0] + ',' + day_info)

主程序

if __name__ == '__main__':
    # 创建postgresql数据库对象
    db = BackupDb(database='postgres', user='postgres', pwd='123456', host='127.0.0.1', port='5432')

    try:
        # 连接数据库
        db.connect_db()
        # 获取当前时间并对时间进行格式化
        day_info = datetime.datetime.now().strftime('%Y%m%d%H%M%S')

        # 获取备份编号
        num_res = db.prepare_backup()

        # 第一次运行脚本,直接执行备份
        if num_res == 1:
            db.database_bakcup(num_res)
        # 如果备份编号小于等于备份保留个数
        elif num_res <= num:
            # 执行备份
            db.database_bakcup(num_res)
            # 修改log_file,将信息追加到log_file文件中
            db.modify_log(num_res)
        # 如果备份编号大于备份保留个数,需要删除较早的备份
        else:
            # 执行备份
            db.database_bakcup(num_res)
            # 修改log_file,将信息追加到log_file文件中
            db.modify_log(num_res)
            # 删除较早的备份
            db.delete_old_backup()

        # 关闭postgresql数据库连接
        db.close_db()

    except Exception as e:
        print(e)

总结

整个脚本实现的是数据库的备份操作,并设置了最大保留个数。当备份数超过设定的阈值时,会将较早的备份进行删除。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值