跨库数据备份还原、迁移工具

前言

数据开发过程中,为了确保生产数据库安全,一般将实时数据同步、备份到本地测试数据库完成开发工作,最后部署应用。

本文实际业务场景:需要从客户处的生成数据库(如mysql数据库)备份数据表到本地的一个sqlserver库中,将生产数据库表复制还原过来。为了快速完成数据备份操作,于是我写了个同步迁移的代码。同样的使用需求下,只需修改配置settings就可以直接用。

当然也可以用数据库客户端界面可视化完成数据库的备份还原。

测试数据准备

本文还是利用上一篇文章的数据表,该数据存放在mysql数据库中。

Python搭建一个系统信息实时监控数据可视化大屏

先查看system_info表结构。

SHOW FULL COLUMNS FROM system_info 

代码结构

主函数db_backup.py完成数据库表同库、垮库的备份、同步、迁移功能,logging_conf.py日志记录模块;settings.py设置两个数据库配置参数;tools.py完成路径获取和数据库连接功能。

代码部分

  • 数据库备份、迁移-db_backup.py

该模块主要方法:copy_to_from_mysql和mysql_sync_to_sqlserver。 

1、copy_to_from_mysql用于把一个表的内容复制到一个文件。

2、mysql_sync_to_sqlserver从文件复制数据到插入到目标数据库表中。

import datetime
import logging.config

from tools import get_local_path,get_conn
from logging_conf import  log_config,local_data

logging.config.dictConfig(log_config)
logger = logging.getLogger(__name__)


def copy_to_from_mysql(table_name):
    """
    从Mysql导出数据文件到本地
    """
    start = datetime.datetime.now()
    full_data_name = get_local_path(table_name)
    conn = None
    try:
        conn = get_conn('SOURCE')
        if conn is None:
            raise Exception('获取数据库连接失败')
        logger.debug(full_data_name)

        sql = 'Select * from {0}'.format(table_name)
        with conn.cursor() as cur:
            cur.execute(sql)
            number = cur.fetchall()
            loan_count = 0
            for loanNumber in number:
                tuple_list = tuple([str(i) for i in list(loanNumber)][1:])
                loan_count += 1
                with open(full_data_name, mode='a', encoding='utf-8') as f:
                    f.write(str(tuple_list) + "\n")
                f.close()
                cur.close()
                print("写入完成,共写入%d条数据!" % loan_count)

    finally:
        if conn:
            conn.close()
        end = datetime.datetime.now()
        s = (end - start).total_seconds()
        logger.info('数据导出: %s, 耗时: %s 秒' % (table_name, s))

    return number

def mysql_sync_to_sqlserver(table_name):
    """
    把mysql数据同步到sqlserver数据库里面
    从本地导入数据文件到本地数据库
    :return:
    """
    start = datetime.datetime.now()
    full_data_name = get_local_path(table_name)
    conn = None
    try:
        conn = get_conn('LOCAL')
        with conn:
            # 数据文件导入
            with conn.cursor() as cur:
                with open(full_data_name, mode='r', encoding='utf-8') as lst:
                    for line in lst:
                        sql = "insert into system_info values {0}".format(line)
                        print("插入成功",line)
                        cur.execute(sql)
            conn.commit()

    finally:
        if conn:
            conn.close()
        end = datetime.datetime.now()
        s = (end - start).total_seconds()
        logger.info('数据导入: %s, 耗时: %s 秒' % (table_name, s))


if __name__ == '__main__':
    table_name = 'system_info'
    # 从mysql导出数据文件到本地
    copy_to_from_mysql(table_name)
    # 从本地导入数据文件到sqlserver数据库
    mysql_sync_to_sqlserver(table_name) 
  • 数据库参数配置-settings.py

    填写数据库备份、还原数据库相关配置参数

db_param = {
    "LOCAL": {
        'host': 'localhost',
        'port': 1433,
        'dbname': 'test',
        'user': 'xxxx',
        'password': 'xxxx',
        'DBType': 'SQLServer',
        'remark': '本地数据库',
    },
    "SOURCE": {
         'host': 'localhost',
        'port':3306,
        'dbname': 'mydb',
        'user': 'xxxx',
        'password': 'xxxx',
        'DBType': 'Mysql',
        'remark': '目标数据库',
    }
}
  • 日志记录模块-logging_conf.py

import os.path
import logging.handlers

BASE_DIR = r'D:\myProjectfile\database_backup\logs'

log_level = logging.DEBUG

# 日志文件位置
log_home = os.path.join(BASE_DIR, 'log', 'test')
if not os.path.exists(log_home):
    os.makedirs(log_home, exist_ok=True)

log_config = {
    'version': 1,
    'formatters': {
        'generic': {
            'format': '%(asctime)s %(levelname) -5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s',
        },
        'simple': {
            'format': '%(asctime)s %(levelname) -5.5s %(message)s',
        },
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',   #输出到终端
            'formatter': 'generic',
        },
        'file': {
            'class': 'logging.FileHandler',  #输出到文件
            'filename': os.path.join(log_home, 'test.log'),
            'encoding': 'utf-8',
            'formatter': 'generic',

        },
    },
    'root': {
        'level': log_level,
        'handlers': ['console', 'file'],
    }
}

# 数据文件位置(数据临时存放位置)
local_data = os.path.join(BASE_DIR, 'data')
if not os.path.exists(local_data):
    os.makedirs(local_data, exist_ok=True)
  • 数据库连接和路径获取工具-tools.py

import os
import pymysql
import pymssql
from settings import db_param
from logging_conf import  local_data

def get_file_name(table_name, suffix='txt'):
    """
    返回文件名
    """
    return table_name.lower() + '.' + suffix

def get_local_path(table_name):
    """
    本地文件存放路径
    """
    path = os.path.join(local_data, table_name)
    if not os.path.exists(path):
        os.makedirs(path, exist_ok=True)
    full_data_name = os.path.join(path, get_file_name(table_name))
    return full_data_name

def get_conn(sys_code='SOURCE'):
    """
    数据库连接获取,此处给出我常用的三种数据库连接
    """
    params = db_param[sys_code]
    host = params['host']
    port = params['port']
    database = params['dbname']
    user = params['user']
    password = params['password']
    db_type = params['DBType'].upper()

    if db_type == "Mysql".upper():
        return pymysql.connect(database=database, user=user, password=password, host=host, port=port)
    elif db_type == "Oracle".upper():
        os.environ['NLS_LANG'] = 'SIMPLIFIED CHINESE_CHINA.UTF8'
        dsn = cx_Oracle.makedsn(host, port, service_name=database)
        conn = cx_Oracle.connect(user, password, dsn=dsn)
        return conn
    elif db_type == 'SQLServer'.upper():
        return pymssql.connect(host=host, user=user, password=password, database=database, charset="utf8")
    else:
        raise Exception("%s数据库连接失败. " % sys_code)
同步完成

 

 

 

 

转自:微信公众号: Python数据分析实例

原文:https://mp.weixin.qq.com/s?__biz=MzI0NzY2MDA4MA==&mid=2247498197&idx=1&sn=4d0ef699f48482e48c1ed2c111efad3c&scene=21#wechat_redirect

posted @ 2022-03-06 11:27  阿布_alone  阅读(156)  评论(0编辑  收藏  举报
TOP