最近写了一个工具(比较两套测试环境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、希望使用的小伙伴多提意见,未来成为一个好用的小工具

dbstructsync 多套mysql环境表、字段、索引的差异sql产出(原创)的更多相关文章

  1. MySQL InnoDB表和索引之聚簇索引与第二索引

    MySQL InnoDB表和索引之聚簇索引与第二索引 By:授客QQ:1033553122 每个InnoDB表都有一个称之为聚簇索引(clustered index)的特殊索引,存储记录行数据.通常, ...

  2. mysql创建表与索引

    -- ---------------------------- -- 商品属性表 -- AUTO_INCREMENT=1为设置了自增长的字段设置起点,1为起点 -- ENGINE选择:MyISAM类型 ...

  3. MySQL查看表的索引【转】

    查看表的索引: show index from table_name(表名) 结果列表中各字段的含义: · Non_unique 如果索引不能包括重复词,则为0.如果可以,则为1. · Key_nam ...

  4. 随笔编号-16 MySQL查看表及索引大小方法

    目标:阿里云OS数据库DMS,单个主库最大存储空间为2T.最近公司业务扩展很快,一天数据量达到7.9G左右.要求备份清理历史数据,备份到其他磁盘. 准备: 如果想知道MySQL数据库中每个表占用的空间 ...

  5. MySQL 回表查询 & 索引覆盖优化

    回表查询 先通过普通索引的值定位聚簇索引值,再通过聚簇索引的值定位行记录数据 建表示例 mysql> create table user( -> id int(10) auto_incre ...

  6. Mysql 修改数据库,mysql修改表类型,Mysql增加表字段,Mysql删除表字段,Mysql修改字段名,Mysql修改字段排列顺序,Mysql修改表名

    对于已经创建好的表,尤其是已经有大量数据的表,如果需要对表做一些结构上的改变,我们可以先将表删除(drop),然后再按照新的表定义重建表.这样做没有问题,但是必然要做一些额外的工作,比如数据的重新加载 ...

  7. mysql每个表总的索引大小

    /* 指定的数据库 每个表的索引 不包含主键索引的大小*/ ,),,),'mb') as index_size from information_schema.tables where TABLE_S ...

  8. mysql alter修改字段的长度 类型sql语句

    在mysql中alter命令可以修改字段类型,长度,名称或一些其它的参数,下面我来给大家介绍alter函数修改字段长度与类型的两个命令,希望文章来给各位带来帮助.     mysql 修改字段长度 a ...

  9. Mysql建表+创建索引

    创建表时可以直接创建索引,这种方式最简单.方便.其基本形式如下: CREATE TABLE 表名( 属性名 数据类型[完整性约束条件], 属性名 数据类型[完整性约束条件], ...... 属性名 数 ...

随机推荐

  1. PHP代码审计基础-中级篇

    初级篇更多是对那些已有的版本漏洞分析,存在安全问题的函数进行讲解,中级篇更多是针对用户输入对漏洞进行利用 中级篇更多是考虑由用户输入导致的安全问题. 预备工具首先要有php本地环境可以调试代码 总结就 ...

  2. Python编程系列---Python中装饰器的几种形式及万能装饰器

    根据函数是否传参  是否有返回值 ,可以分析出装饰器的四种形式: 形式一:无参无返回值 def outer(func): def wrapper(): print("装饰器功能1" ...

  3. NOIP2018货币系统

    题目大意 给出一组数,求出其中共有多少数不能被其他数表示 解题思路 法一:可爱的动态规划 这个思路还是比较好想的(也比较好写?) 有依赖关系的背包,思路这道题是差不多的 填满型01背包 (关于代码) ...

  4. 整理了适合新手的20个Python练手小程序

    100个Python练手小程序,学习python的很好的资料,覆盖了python中的每一部分,可以边学习边练习,更容易掌握python. 本文附带基础视频教程:私信回复[基础]就可以获取的 [程序1] ...

  5. Linux下zkui的安装

    前提:部署好java环境 1.拉取安装包 git clone https://github.com/DeemOpen/zkui.git 2.进入zkui目录 cd zkui/ 3.使用mvn命令进行编 ...

  6. Javascript对this对象的理解

    在JavaScript中this表示函数运行的时候自动生成的一个内部对象,只能在函数内部使用,下面是一个简单的例子: function test(){ alert(this == window); } ...

  7. Texture to texture2D以及texture2D像素反转

    private void SaveRenderTextureToPNG(Texture inputTex, string file) { RenderTexture temp = RenderText ...

  8. Apache中Cookie长度的设置 414 request-uri too large apache

    起因: 今天在调试Ucenter的同步登陆和同步登出的过程中,浏览器突然出现以下错误提示: Your browser sent a request that this server could not ...

  9. .Net Core WebApi(三)在Linux服务器上部署

    鸽了好久,终于有个时间继续写了,继上一篇之后,又写(水)了一篇,有什么不足之处请大家指出,多谢各位了. 下面有两个需要用到的软件,putty和pscp,我已经上传到博客园了,下载请点击这里. 一.准备 ...

  10. Android H5混合开发(3):原生Android项目里嵌入Cordova

    前言 如果安卓项目已经存在了,那么如何使用Cordova做混合开发? 方案1(适用于插件会持续增加或变化的项目): 新建Cordova项目并添加Android平台,把我们的安卓项目导入Android平 ...