dbstructsync -mysql表、字段、索引差异解析工具(原创)

最近写了一个工具(比较两套测试环境mysql数据库中表、表字段、索引的差异,基于python)通过文章简单介绍下工具的相关内容

  1. 工具名称
  2. 主要功能
  3. 具体使用方法
  4. 部分实现代码
  5. 后续

一、工具名称:

dbstructsync (python库)

二、主要功能:

比较两套环境中mysql指定库中表、表字段及索引的差异,返回同步的sql ,里面包含建表,修改索引,修改字段的sql .

A环境的数据库db 作为sourcedb, B环境的数据库db targetdb ,程序逻辑比较的是 sourcedb 与targetdb 的差异,然后返回一个list的数据类型 

list中包含新建表sql,修改、增加字段sql, 删除、新增索引sql 

现在总共有3个版本,0.0.1 和0.0.2 存在一定的bug, 所以请使用最新的0.0.3版本

 

 

其他说明:由于是刚完成不久的程序,所以暂时不对最终结果sql进行执行,避免对使用过程中产生不好的影响,这个版本大家可以通过python 自行选择需要执行哪些操作;随着之后程序的逐步深入修改和演变,会将执行sql这一步也都加进去

同时也会优化使用方式,让使用这个工具的小伙伴更容易操作

三、具体使用方法:

1、 pip install  -i https://pypi.python.org/pypi  dbstructsync   

在代码里引入使用,

from DbStructSync import cli 

result=cli.db_sync(sourcedb, targetdb) 
#sourcedb,targetdb是两个dict的参数,具体参数看下面
# 这里得到的    result = ['use 库;', 
#             'CREATE TABLE `test_async` (\n  `test_async` #varchar(30) NOT NULL,\n  `aa` varchar(400) DEFAULT NULL,\n  #PRIMARY KEY (`test_async`)\n) ENGINE=InnoDB DEFAULT #CHARSET=utf8;',
#             'drop index `index_chaxx` on chanxx_auto_puxx_conf;',
#             'create index `index_chaxx` on #chanxx_auto_puxx_conf(`channel_nxx`,`channel_prxx`) USING #BTREE;']
#result 中包含  use 库;
#              如果有少的表,会有 create table的数据; 如果有不同的索引,会#存在drop index 和create index的sql;
#              如果有不同的字段,会有alter table的sql ;
#只需要对这个结果,再通过pymysql的一些数据库操作就可以保证 sourcedb #的内容与taragetdb一致。

2、同时还支持命令行操作,代码写入到x.py代码中

result = cli.db_sync_commandline()
python x.py --source  host=10.1.1.x,port=3306,user=root,passwd=root,db=investx --target host=10.1.1.x,port=3306,user=root,passwd=root,db=investx

 

命令行中  --source  key=value;key2=value2;key3=value3  --target key=value;key2=value2;key3=value3

--source, --target 是两给必输的参数,后续的值会作为一个dict类型传入程序。 --source是源库的信息, --target是目标库的信息

还包括其他几个命令参数 --only-index , --only-fields ; --only-index 只比较索引差异, --only-fields 只比较字段差异, 非必填,默认都为False 

四、部分实现代码:

 

def diff_tables(sourcetable, targettable):
    '''

    :param sourcetable:  源数据库的表名列表
    :param targettable:  目的数据库的表名列表
    :return: 返回dict,包含三种结果,源库多的表,目标库多的表,相同的表
    '''
    logger.info('开始比较两个库中表的差异,源库表{},目标库表{}'.format(sourcetable, targettable))
    table_result={}
    if not isinstance(sourcetable, list) or not isinstance(targettable, list):
         raise  TypeError('sourcetable , targettable的类型不是list')
    source_diff = set(sourcetable) - set(targettable)
    target_diff = set(targettable) - set(sourcetable)
    same_tables = set(sourcetable)& set(targettable)
    table_result['source'] = source_diff
    table_result['target'] = target_diff
    table_result['same'] = same_tables
    logger.info('两个库中表的差异结果{}'.format(table_result))
    return  table_result
 

def  diff_indexs_fields(sourcesql, targetsql, type=1):
    '''
    :param sourcesql: 源数据库表的创建sql
    :param targetsql: 目标数据表的创建sql
    :return: 记录下索引不一致的地方
    '''
    result = {}
    logger.info('解析语句中的索引字段,并进行比较索引')
    sourcesql = parse_str(sourcesql)  # 从括号中提取需要的内容
    #logger.info('从括号中提取出来的信息数据{}'.format(sourcesql))
    sourcesql = lists2str(sourcesql)  #将list转换为str,并对数据的空格数据进行处理
    logger.info('解析完的数据的信息{}'.format(sourcesql))
    sourcesql = sourcesql.split('\n') #将str按照'\\n'进行分割
    logger.info('解析完数据之后的信息{}'.format(sourcesql))
    targetsql = parse_str(targetsql)
    targetsql = lists2str(targetsql)
    targetsql = targetsql.split('\n')
    if type ==1:
        source_index = parse_fields(sourcesql,type)
        target_index = parse_fields(targetsql,type)

        result= compare_indexs_field(source_index, target_index, type)
    elif type ==2:
        source_field_sql = parse_fields(sourcesql, type)
        target_field_sql = parse_fields(targetsql, type)
        result = compare_indexs_field(source_field_sql, target_field_sql, type)
    return  result

def dict2sql(dict_str):
    '''
    将解析完成的数据转换为对应的可执行sql
    :param dict_str:
    :return:
    '''
    dict_str = copy.deepcopy(dict_str) # 做一个深度copy,可以放心的进行相关数据处理

    if not isinstance(dict_str, dict):
        raise  TypeError('调用方法{}参数不是dict类型,请确认'.format('dict2sql'))
    #获取db名字
    for key ,value in dict_str.items():
        dbname = key
        logger.info('数据库名{}'.format(dbname))
        for table, table_desc  in  value.get('source').items():
               if table =='create_table':
                   #create_table_sql = lists2str(table_desc)
                   dict_str[dbname]['source'][table] = table_desc
                   #其他的都是table的名字
                   logger.info('数据库的修改语句:{}'.format(table_desc))
               else:
                  logger.info('对于索引和字段的解析原始数据{}'.format(table_desc))
                  if table_desc.get('index'):
                      create_index_sql_lists=[]
                      #create_index_sql_lists.append('use {};'.format(dbname))
                      index_lists= (table_desc.get('index'))
                      result_index= parse_comma_split(str(index_lists)[1:-1])
                      for i in result_index:
                           if i.strip().startswith('\'KEY'):
                               #print(i.strip())
                               index_values = parse_space_split(i.strip())
                               drop_index_sql= 'drop index {} on {}'.format(index_values[1],table )
                               if len(index_values)<=3:
                                  create_index_sql='create index {} on {}{} '.format(index_values[1], table, index_values[2])
                               else:
                                  create_index_sql='create index {} on {}{} {}'.format(index_values[1], table, index_values[2], ' '.join(index_values[3:]))
                               create_index_sql_lists.append(drop_index_sql)
                               create_index_sql_lists.append(create_index_sql)

                           if i.strip().startswith('\'UNIQUE KEY'):
                                 index_values = parse_space_split(i.strip())
                                 drop_index_sql = 'drop index {} on {}'.format(index_values[2], table)
                                 if len(index_values) <= 4:
                                     create_index_sql = 'create unique index {} on {}{} '.format(index_values[2], table,
                                                                                          index_values[3])
                                 else:
                                     create_index_sql = 'create unique index {} on {}{} {}'.format(index_values[2], table,
                                                                                               index_values[3],
                                                                                               ' '.join(index_values[4:]),
                                                                                               )
                                 create_index_sql_lists.append(drop_index_sql)
                                 create_index_sql_lists.append(create_index_sql)
                      logger.info('表{}解析出来的索引的修改sql{}'.format(table, create_index_sql_lists))
                      dict_str[dbname]['source'][table]['index'] = create_index_sql_lists
                  if table_desc.get('fields'):
                      create_fields_sql_lists=[]
                      #create_fields_sql_lists.append('use {};'.format(dbname))
                      modify_field_sqls = table_desc.get('fields').get('modify',None)
                      create_field_sqls=table_desc.get('fields').get('lose',None)

                      if modify_field_sqls:
                           for modify_field_sql in modify_field_sqls:
                               sql_indexs = parse_space_split(str(modify_field_sql)[0:-1])
                               #print(sql_indexs)
                               alter_fields_sql='alter table {} modify column {} {} {}'.format(table, sql_indexs[0],sql_indexs[1],' '.join(sql_indexs[2:]))
                               create_fields_sql_lists.append(alter_fields_sql)
                      if create_field_sqls:
                           for  create_field_sql in create_field_sqls:
                                sql_indexs = parse_space_split(str(create_field_sql)[0:-1])
                                create_fields_sql='alter table {} add column {} {}'.format(table, sql_indexs[0],' '.join(sql_indexs[2:]))
                                create_fields_sql_lists.append(create_fields_sql)
                      logger.info('表{}解析出来的字段的修改sql{}'.format(table,create_fields_sql_lists))
                      dict_str[dbname]['source'][table]['fields'] = create_fields_sql_lists

    return  dict_str  # 返回给一个全部是可执行sql的dict

五、后续:

1、对使用过程中遇到对bug进行修复

2、对代码进行优化

3、增加其他相关功能,让工具越来越好用

4、希望使用的小伙伴多提意见,未来成为一个好用的小工具

posted on 2019-11-23 22:48  yingchen  阅读(548)  评论(0编辑  收藏  举报

导航