一、背景

实际生产中,发现mysql查询性能存在抖动,同样的sql,正常执行时间是秒级,但是偶尔会有执行上百秒的情况出现,经过DBA的排查,并没有发现mysql的问题。考虑迁移一部分生成数据到PG中进行测试。(ps~个人觉得这个迁移背景有点牵强,还是应该先定位性能抖动的原因比较好)

二、迁移方案

迁移的大致步骤如下:

从生产环境的mysql备份中拉取一个备份出来

在测试机上通过备份恢复生产库

导出mysql的表定义和数据

通过自己开发的小工具,将mysql表定义语法转换至PG的表定义语法

在PG中创建表

将数据导入PG

三、迁移步骤说明

3.1 拉取备份

这个没什么好说的,scp指定的备份文件到测试机即可

考虑是生产环境,有防火墙和权限等的限制,可以临时创建临时用户tmp,关闭防火墙,待拷贝完成,删除用户,重启防火墙

3.2 恢复生产库

生产上通过xtrabackup做的备份,恢复方法这里就不啰嗦了,不是本次的重点,自行百度~

3.3 导出mysql的表定义和数据

从这步开始就有坑了~

首先,导出表定义(只贴出测试数据)

# 将名为test_db的库中所有的ddl都导出到test_db.sql文件中

# 导出的定义以sql语句的形式写入文件

[mysql@sndsdevdb01 ~]$ mysqldump -h127.0.0.1 -uroot -ppassword -d test_db > /mysql/test_db.sql

[mysql@sndsdevdb01 ~]$ cat /mysql/test_db.sql

...

/* 下面是导出的表定义部分 */

DROP TABLE IF EXISTS `tb1`;

/*!40101 SET @saved_cs_client = @@character_set_client */;

/*!40101 SET character_set_client = utf8 */;

CREATE TABLE `tb1` (

`c1` int(11) DEFAULT NULL,

`c2` char(5) DEFAULT NULL,

`c3` varchar(10) DEFAULT NULL,

`c4` datetime DEFAULT NULL

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

/*!40101 SET character_set_client = @saved_cs_client */;

SET @@SESSION.SQL_LOG_BIN = @MYSQLDUMP_TEMP_LOG_BIN;

/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

...

导出表定义是为了之后人工检查mysql到PG的ddl语法转换的正确性

实际实施时,利用小工具直接连接mysql服务器即可完成mysql到PG的ddl语法转换

关于小工具的说明,请见附录~

然后,导出数据

考虑到数据格式,编码的问题,决定统一将数据导出为UTF8编码的csv文件

为了说明坑的地方,我插入了5条记录

mysql> delete from tb1;

Query OK, 3 rows affected (0.01 sec)

mysql> insert into tb1 values(1,'qqq','www',current_time);

Query OK, 1 row affected (0.02 sec)

mysql> insert into tb1 values(1,'qq\nq','www',current_time);

Query OK, 1 row affected (0.01 sec)

mysql> insert into tb1 values(1,'qq\r\nq','www',current_time);

Query OK, 1 row affected (0.00 sec)

mysql> insert into tb1 values(1,'qqq','www','0000-00-00 00:00:00');

Query OK, 1 row affected (0.00 sec)

mysql> insert into tb1 values(1,'qqq','www',null);

Query OK, 1 row affected (0.00 sec)

mysql> select * from tb1;

+------+-------+------+---------------------+

| c1 | c2 | c3 | c4 |

+------+-------+------+---------------------+

| 1 | qqq | www | 2017-07-14 17:36:25 |

| 1 | qq

q | www | 2017-07-14 17:36:30 |

| 1 | qq

q | www | 2017-07-14 17:36:36 |

| 1 | qqq | www | 0000-00-00 00:00:00 |

| 1 | qqq | www | NULL |

+------+-------+------+---------------------+

5 rows in set (0.00 sec)

mysql> select * from tb1 into outfile '/mysql/tb1.csv' fields terminated by ',' optionally enclosed by '"' escaped by '"' lines terminated by '\n';

其中第二条和第三条中,c2列分别包含了换行符和windows的特殊换行符

然后再通过vi 打开tb1.csv

1,"qqq","www","2017-07-14 17:36:25"

1,"qq"

q","www","2017-07-14 17:36:30"

1,"qq^M"

q","www","2017-07-14 17:36:36"

1,"qqq","www","0000-00-00 00:00:00"

1,"qqq","www","N

坑点如下

\n换行符导致原本的一条记录分为2行

\r是特殊字符,vi模式下就表示为^M

datetime类型可以存储"0000-00-00 00:00:00",但是官方手册上datetime的合法范围是'1000-01-0100:00:00' to '9999-12-31 23:59:59',感觉是bug。。

NULL值会被转义为"N的形式

1和2两点,导致csv格式混乱,导入PG会出错;datetime对应PG的timestamp类型,而"0000-00-00 00:00:00"是不符合PG的时间戳类型的合法范围的;PG也不认识"N表示的NULL。。。

由于上述的坑都是在将数据导入PG的时候才发现的,所以我的做法是通过shell的sed,awk等命令,去人工替换这些内容。因为生产数据量很大,一个库大概200G,磁盘空间有限,加上导出数据需要较长时间,所以尽量不重复导数据

但是用shell处理大文件,效率也很低,150G的csv文件,遍历sed多次,往往超过1小时,而且存在正则表达式写的不精确,匹配出错的情况

所以我个人推荐,select导出数据时,通过where条件过滤,用replace函数将需要处理的列直接处理掉,可以省去后面的麻烦,但是前提条件是需要知道有哪些列存在这些问题(生成中的表往往列很多,几十甚至几百列)

3.3 在PG中创建表并导入数据

首先创建相应的业务库

postgres=# create database test_db;

CREATE DATABASE

postgres=# \c test_db

You are now connected to database "test_db" as user "postgres".

postgres=#\i /pgsql/pg.sql

# 执行转换后的ddl,定义表

...

postgres=#\copy tb1 from '/pgsql/tb1.csv' with(format csv,encoding 'UTF8',NULL 'null')

# 通过copy命令导入数据,通过指定NULL字符串来识别NULL值

如果导入过程不出现任何错误,那说明数据的迁移基本就完成了

3.4 其他

上述内容只是单纯的业务库的数据迁移,如果想完整的把整个业务系统迁移至PG,还有很多的别的迁移工作

例如表的索引

PG提供了丰富的索引类型,索引详情参考:

PG 9.6 手册 http://www.postgres.cn/docs/9.6/indexes.html

需要根据业务需求重新定制,例如AP型业务,gin索性就有很大的优势,除此之外,业务定义的存储过程,上层的增删改查接口等等也需要修改

另外,数据库的备份方案,日志归档设置,高可用方案的设计这些也需要定制

附录

关于DDL语法转换的小工具

功能简述

将mysql的表定义转换为PG对应的语法。主要完成数据类型的映射,列属性语法的转换,主键和部分类型索引的转换

1.1. 类型映射

case "tinyint":

case "tinyint unsigned":

case "smallint":

if (col_is_auto_increment.equals("YES")){//increment type

mysql_type.add("smallserial");

}else{

mysql_type.add("smallint");

}

break;

case "mediumint":

case "smallint unsigned":

case "mediumint unsigned":

case "integer":

case "int":

if (col_is_auto_increment.equals("YES")){//increment type

mysql_type.add("serial");

}else{

mysql_type.add("int");

}

break;

case "int unsigned":

case "bigint":

if (col_is_auto_increment.equals("YES")){//increment type

mysql_type.add("bigserial");

}else{

mysql_type.add("bigint");

}

break;

case "bigint unsigned":

mysql_type.add("decimal");

mysql_type.add("20");

mysql_type.add("0");

break;

case "double":

mysql_type.add("double precision");

break;

case "decimal":

mysql_type.add("decimal");

mysql_type.add(precision.toString());

mysql_type.add(scale.toString());

break;

case "float":

mysql_type.add("real");

break;

case "binary":

case "char":

mysql_type.add("char");

mysql_type.add(precision.toString());

break;

case "varbinary":

case "varchar":

mysql_type.add("varchar");

mysql_type.add(precision.toString());

break;

case "tinyblob":

case "mediumblob":

case "longblob":

case "blob":

mysql_type.add("bytea");

break;

case "date":

mysql_type.add("date");

break;

case "datetime":

case "year":

case "timestamp":

mysql_type.add("timestamp");

break;

case "time":

mysql_type.add("time");

break;

/*case "bit":

pg_type.add("bit");

break;*/

case "tinytext":

case "text":

case "mediumtext":

case "longtext":

mysql_type.add("text");

break;

default:

mysql_type.add("This type may be user deifned type,confirm for yourself please!");

break;

1.2. 列属性

* not null属性

* column注释

* 自增属性

1.3. 索引

统一将mysql的索引转换为PG的btree索引,这个在应用中意义不大,因为多数情况,索引是需要根据业务需求重新定义的

实现方式

通过JDBC连接mysql服务器,通过元数据(metadata)获取所有的表名,列名以及列的数据类型等等信息,然后在程序中做转换,最后写入sql文件

思考

其实这只是简单的迁移方案,目前也有一些商用或者开源的迁移工具,例如:

mysql2pg:https://sourceforge.net/projects/mysql2pg/

另外,关于迁移数据,用csv文件的方式,对磁盘空间的要求较高,而且有上述字符格式的问题。其实还可以考虑PG的插件mysql_fdw,可以直接用select into的方式将数据直接插入PG中,可以省去中间导出的步骤。但是9.6的PG,对foreign table的语法支持不完善,不支持like的方式建表,所以对宽表,create foreign table写起来就比较麻烦,可以考虑用脚本自动化。

另外,生产中往往mysql和PG不在一台机器上,mysql_fdw拉取和插入数据的效率还有待测试。我初步的尝试发现,速度是很慢的,不过没有深入调查原因,有可能是网络问题,也有可能是配置问题

mysql_fdw的说明参考德哥的博客:http://blog.163.com/digoal@126/blog/static/163877040201493145214445/

mysql5.6最好的备份方案_Mysql 5.6迁移至PostgreSQL 9.6的实践小结相关推荐

  1. mysql 数据备份方案_MySQL常见备份方案

    MySQL常见备份方案有以下三种: mysqldump + binlog lvm + binlog xtrabackup 本例为方便演示,数据库里面数据为空.下面开始动手 mkdir /opt/bac ...

  2. mysql 每日数据备份方案_mysql数据库备份方案

    方案 以一天一次的频率,在每天凌晨2:00备份生产环境数据库至文件服务器.为保证备份文件不过多占用文件服务器空间,备份文件以tgz压缩包格式保存且只保存7天的备份文件.另外每次备份都保存备份日志,一旦 ...

  3. mysql冷热备份方案_MySQL双机热备份实施方案

    MySQL双机热备份实施方案 1.MySQL数据库没有增量备份的机制,当数据量太大的时候备份是一个很大的问题.还好MySQL数据库提供了一种主从备份的机制,其实就是把主数据库的所有的数据同时写到备份数 ...

  4. mysql数据库备份方案_MySQL平台数据库备份方案详细说明

    在数据库表丢失或损坏的情况下,备份你的数据库是很重要的.如果发生系统崩溃,你肯定想能够将你的表尽可能丢失最少的数据恢复到崩溃发生时的状态.有时,正是MySQL管理员造成破坏.管理员已经知道表已破坏,用 ...

  5. mysql 高效备份_Mysql高性能备份方案解决数据不间断访问(LVM快照方式备份)

    Mysql高性能备份方案解决数据不间断访问(LVM快照方式备份) mysql LVM快照备份特点: 1.在大多数情况下,这种方式几乎算得上是热备.它无需关闭服务,只需要设置只读或者类似这样的限制. 2 ...

  6. mysql5.6主从复制(读写分离)方案_MySQL5.6主从复制(读写分离)方案

    MySQL5.6主从复制(读写分离)方案 一.前言:为什么MySQL要做主从复制(读写分离)? 通俗来讲,如果对数据库的读和写都在同一个数据库服务器中操作,业务系统性能会降低. 为了提升业务系统性能, ...

  7. MySQL5.6主从复制(读写分离)方案

    MySQL5.6主从复制(读写分离)方案 https://yq.aliyun.com/articles/24255 摘要: 一.前言:为什么MySQL要做主从复制(读写分离)? 通俗来讲,如果对数据库 ...

  8. MySQL备份系列--备份方案总结性梳理

    mysql数据库备份有多么重要已不需过多赘述了,废话不多说!以下总结了mysql数据库的几种备份方案: 一.binlog二进制日志通常作为备份的重要资源,所以再说备份方案之前先总结一下binlog日志 ...

  9. 服务器维护简单的备份方案,服务器备份方案计划.doc

    服务器备份方案计划 服务器备份方案 一.备份服务器硬件配置 备份服务器型号IBM X206,其硬件配置如下: CPU:P4 2.8G 内存:DDR ECC512M 硬盘:3*72G SCSI硬盘. 磁 ...

最新文章

  1. 一周焦点 | 陆奇“入驻” YC;TensorFlow 2.0 即将发布
  2. git命令行完全解读
  3. python的核心数据类型_Python核心数据类型-集合
  4. UOJ #587. 天天和不可描述
  5. 认识J2EE规范或标准以及J2EE和JEE有什么不同?
  6. 在Eclipse中搭建Python Django
  7. Python+tkinter实现验证码输入和切换
  8. Python破解百度翻js代码
  9. 23种设计模式(9):访问者模式(转)
  10. java obix_Gson序列化多态对象列表
  11. STM32软件IIC速度
  12. 思源黑体对应font-weight
  13. 解读微信多开技巧,Python tk 实现微信多开脚本exe工具
  14. python爬虫IP地址解析爬取(IP38.com)
  15. Mat 无法解析dump文件:Dominator tree not available. Open the Dominator Tree or delete indices and parse aga
  16. win7网络里面没有计算机,Win7没有无线网络选项教你完美解决
  17. python strftime时分秒_Python time.strftime()用法及代碼示例
  18. 开发的激光测距仪PCBA方案设计
  19. 教你玩转自己的机械键盘
  20. 2022年京东618万券齐发活动入口, 京东618红包领取地址

热门文章

  1. RPM ,yum工具
  2. VMware 当中出现:无法将 Ethernet0 连接到虚拟网络VMnet8的问题
  3. ASP.NET MVC开发,编辑页面和添加页面基本相同,我们控制器 Add Edit是共用同一个View吗?...
  4. Storm中的LocalState 代码解析
  5. 使用jquery在新窗口中打开外部链接
  6. CycleGAN非配对图像生成,定制你的卡通照
  7. CVPR 2019笔迹识别论文:逆鉴别网络+八路Attention
  8. php curl安装检查,如何判断php的curl是否已安装
  9. 最坏情况为线性时间的选择算法
  10. npm run serve后台运行的命令写法