mysql5.6最好的备份方案_Mysql 5.6迁移至PostgreSQL 9.6的实践小结
一、背景
實際生產(chǎn)中,發(fā)現(xiàn)mysql查詢性能存在抖動,同樣的sql,正常執(zhí)行時間是秒級,但是偶爾會有執(zhí)行上百秒的情況出現(xiàn),經(jīng)過DBA的排查,并沒有發(fā)現(xiàn)mysql的問題。考慮遷移一部分生成數(shù)據(jù)到PG中進行測試。(ps~個人覺得這個遷移背景有點牽強,還是應(yīng)該先定位性能抖動的原因比較好)
二、遷移方案
遷移的大致步驟如下:
從生產(chǎn)環(huán)境的mysql備份中拉取一個備份出來
在測試機上通過備份恢復(fù)生產(chǎn)庫
導(dǎo)出mysql的表定義和數(shù)據(jù)
通過自己開發(fā)的小工具,將mysql表定義語法轉(zhuǎn)換至PG的表定義語法
在PG中創(chuàng)建表
將數(shù)據(jù)導(dǎo)入PG
三、遷移步驟說明
3.1 拉取備份
這個沒什么好說的,scp指定的備份文件到測試機即可
考慮是生產(chǎn)環(huán)境,有防火墻和權(quán)限等的限制,可以臨時創(chuàng)建臨時用戶tmp,關(guān)閉防火墻,待拷貝完成,刪除用戶,重啟防火墻
3.2 恢復(fù)生產(chǎn)庫
生產(chǎn)上通過xtrabackup做的備份,恢復(fù)方法這里就不啰嗦了,不是本次的重點,自行百度~
3.3 導(dǎo)出mysql的表定義和數(shù)據(jù)
從這步開始就有坑了~
首先,導(dǎo)出表定義(只貼出測試數(shù)據(jù))
# 將名為test_db的庫中所有的ddl都導(dǎo)出到test_db.sql文件中
# 導(dǎo)出的定義以sql語句的形式寫入文件
[mysql@sndsdevdb01 ~]$ mysqldump -h127.0.0.1 -uroot -ppassword -d test_db > /mysql/test_db.sql
[mysql@sndsdevdb01 ~]$ cat /mysql/test_db.sql
...
/* 下面是導(dǎo)出的表定義部分 */
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 */;
...
導(dǎo)出表定義是為了之后人工檢查mysql到PG的ddl語法轉(zhuǎn)換的正確性
實際實施時,利用小工具直接連接mysql服務(wù)器即可完成mysql到PG的ddl語法轉(zhuǎn)換
關(guān)于小工具的說明,請見附錄~
然后,導(dǎo)出數(shù)據(jù)
考慮到數(shù)據(jù)格式,編碼的問題,決定統(tǒng)一將數(shù)據(jù)導(dǎo)出為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換行符導(dǎo)致原本的一條記錄分為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值會被轉(zhuǎn)義為"N的形式
1和2兩點,導(dǎo)致csv格式混亂,導(dǎo)入PG會出錯;datetime對應(yīng)PG的timestamp類型,而"0000-00-00 00:00:00"是不符合PG的時間戳類型的合法范圍的;PG也不認(rèn)識"N表示的NULL。。。
由于上述的坑都是在將數(shù)據(jù)導(dǎo)入PG的時候才發(fā)現(xiàn)的,所以我的做法是通過shell的sed,awk等命令,去人工替換這些內(nèi)容。因為生產(chǎn)數(shù)據(jù)量很大,一個庫大概200G,磁盤空間有限,加上導(dǎo)出數(shù)據(jù)需要較長時間,所以盡量不重復(fù)導(dǎo)數(shù)據(jù)
但是用shell處理大文件,效率也很低,150G的csv文件,遍歷sed多次,往往超過1小時,而且存在正則表達(dá)式寫的不精確,匹配出錯的情況
所以我個人推薦,select導(dǎo)出數(shù)據(jù)時,通過where條件過濾,用replace函數(shù)將需要處理的列直接處理掉,可以省去后面的麻煩,但是前提條件是需要知道有哪些列存在這些問題(生成中的表往往列很多,幾十甚至幾百列)
3.3 在PG中創(chuàng)建表并導(dǎo)入數(shù)據(jù)
首先創(chuàng)建相應(yīng)的業(yè)務(wù)庫
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
# 執(zhí)行轉(zhuǎn)換后的ddl,定義表
...
postgres=#\copy tb1 from '/pgsql/tb1.csv' with(format csv,encoding 'UTF8',NULL 'null')
# 通過copy命令導(dǎo)入數(shù)據(jù),通過指定NULL字符串來識別NULL值
如果導(dǎo)入過程不出現(xiàn)任何錯誤,那說明數(shù)據(jù)的遷移基本就完成了
3.4 其他
上述內(nèi)容只是單純的業(yè)務(wù)庫的數(shù)據(jù)遷移,如果想完整的把整個業(yè)務(wù)系統(tǒng)遷移至PG,還有很多的別的遷移工作
例如表的索引
PG提供了豐富的索引類型,索引詳情參考:
PG 9.6 手冊 http://www.postgres.cn/docs/9.6/indexes.html
需要根據(jù)業(yè)務(wù)需求重新定制,例如AP型業(yè)務(wù),gin索性就有很大的優(yōu)勢,除此之外,業(yè)務(wù)定義的存儲過程,上層的增刪改查接口等等也需要修改
另外,數(shù)據(jù)庫的備份方案,日志歸檔設(shè)置,高可用方案的設(shè)計這些也需要定制
附錄
關(guān)于DDL語法轉(zhuǎn)換的小工具
功能簡述
將mysql的表定義轉(zhuǎn)換為PG對應(yīng)的語法。主要完成數(shù)據(jù)類型的映射,列屬性語法的轉(zhuǎn)換,主鍵和部分類型索引的轉(zhuǎn)換
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. 索引
統(tǒng)一將mysql的索引轉(zhuǎn)換為PG的btree索引,這個在應(yīng)用中意義不大,因為多數(shù)情況,索引是需要根據(jù)業(yè)務(wù)需求重新定義的
實現(xiàn)方式
通過JDBC連接mysql服務(wù)器,通過元數(shù)據(jù)(metadata)獲取所有的表名,列名以及列的數(shù)據(jù)類型等等信息,然后在程序中做轉(zhuǎn)換,最后寫入sql文件
思考
其實這只是簡單的遷移方案,目前也有一些商用或者開源的遷移工具,例如:
mysql2pg:https://sourceforge.net/projects/mysql2pg/
另外,關(guān)于遷移數(shù)據(jù),用csv文件的方式,對磁盤空間的要求較高,而且有上述字符格式的問題。其實還可以考慮PG的插件mysql_fdw,可以直接用select into的方式將數(shù)據(jù)直接插入PG中,可以省去中間導(dǎo)出的步驟。但是9.6的PG,對foreign table的語法支持不完善,不支持like的方式建表,所以對寬表,create foreign table寫起來就比較麻煩,可以考慮用腳本自動化。
另外,生產(chǎn)中往往mysql和PG不在一臺機器上,mysql_fdw拉取和插入數(shù)據(jù)的效率還有待測試。我初步的嘗試發(fā)現(xiàn),速度是很慢的,不過沒有深入調(diào)查原因,有可能是網(wǎng)絡(luò)問題,也有可能是配置問題
mysql_fdw的說明參考德哥的博客:http://blog.163.com/digoal@126/blog/static/163877040201493145214445/
總結(jié)
以上是生活随笔為你收集整理的mysql5.6最好的备份方案_Mysql 5.6迁移至PostgreSQL 9.6的实践小结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql+索引优化+查询优化+存储优化
- 下一篇: mysql5.7.20linux安装,l