dbstructsync -mysql表、字段、索引差异解析工具(原创)
最近写了一个工具(比较两套测试环境mysql数据库中表、表字段、索引的差异,基于python)通过文章简单介绍下工具的相关内容
- 工具名称
- 主要功能
- 具体使用方法
- 部分实现代码
- 后续
一、工具名称:
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、希望使用的小伙伴多提意见,未来成为一个好用的小工具