Mongodb到mysql数据库的数据迁移(Java,Windows)
运行环境为windows
测试过260万的数据表,迁移大概要10分钟左右,当然肯定和网络,字段大小什么的有关系。
遇到的坑和注意点都用紫色标记了
PS:第一次写这么长的东西
一、Mongodb导出命令mongoexport
本地安装Mongodb,在安装目录的/bin下按住shift并右键“在此处打开命令窗口”,可执行以下语句进行导出。
mongoexport -h <ip:port> -d <database> -c <collection> -u <username> -p <password> --type <json/csv> -f <fileds> -o <outputfile> --limit %d --skip %d --noHeaderLine
-h host,主机ip+port
-d database,数据库名
-c collection,集合名(表名)
-u username,用户名
-p password,密码
--type 导出类型 json/csv
-f 当type为csv时必选,导出字段名,逗号分隔
-o outputfile,输出文件名
-q query,查询参数,为json字符串
--sort 排序参数,为json字符串
--limit 返回结果数,和skip分页时使用
--skip 跳过的记录数
--noHeaderLine 导出文件不包含首行字段名
示例:
mongoexport -h 10.10.10.10:27027 -d test -c Student -u mydb -p mydb --type csv -f "_id,stuno,stuname,age,sex" -o D:/Student.csv --limit 1000 --skip 0 -q {'stuno':'stu_11123'} --sort {age:1} --noHeaderLine
二、MySQL导入命令mysqlimport
本地安装MySQL,在安装目录的/bin下按住shift并右键“在此处打开命令窗口”,可执行以下语句进行导入。
mysqlimport -h <hostname> -P <port> -u <username> -p<password> --local <databasename> <importfile> -c <colums> --fields-terminated-by=, --fields-enclosed-by=\" --lines-terminated-by=\r\n --ignore-lines=1
-h hostname
-P port
-u username
-p password, 密码字符串和-p之间没有空格
--local 使用本地的文件导入
databasename 数据库名
importfile 导入文件路径,文件名被认为是表名,如下例中的Student
-c colums,文件中字段分割顺序,用逗号分割
--fields-terminated-by=字符串 设置字符串为字段之间的分隔符,可以为单个或多个字符。默认值为制表符“\t”。
--fields-enclosed-by=字符 设置字符来括住字段的值,只能为单个字符。
--fields-optionally-enclosed-by=字符 设置字符括住CHAR、VARCHAR和TEXT等字符型字段,只能为单个字符。
--fields-escaped-by=字符 设置转义字符,默认值为反斜线“\”。
--lines-terminated-by=字符串 设置每行数据结尾的字符,可以为单个或多个字符,默认值为“\n”。
--ignore-lines=n 表示可以忽略前n行。可以用来跳过首行的字段名。
示例:
mysqlimport -h 10.10.10.10 -P 3306 -u dbtest -pdbtest --local mydb D:/Student.txt -c "id,stuno,stuname,age,sex" --fields-terminated-by=, --fields-enclosed-by=\" --lines-terminated-by=\r\n
三、Java运行cmd命令工具类
package util; import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader; import org.apache.commons.lang.StringUtils; /**
*
* Cmd命令執行工具
*
* @author 2018.03.14
*/
public class CmdUtil {
private final static CmdUtil instance = new CmdUtil(); private final static Runtime cmd = Runtime.getRuntime(); public static CmdUtil getInstance() {
return instance;
} private CmdUtil() { } /**
*
* 用于执行cmd命令并返回执行结果
*
* @param commondStr 命令字符串
* @param dir 执行目录
* @return 执行结果
*/
public String exec(String commondStr, File dir) {
//System.out.println(">" + commondStr);
StringBuilder sb = new StringBuilder();
try {
// "/c"代表程序执行有参数,如果不加上就直接运行了cmd.exe; dir是程序执行目录
Process process = cmd.exec("cmd.exe /c" + commondStr, null, dir);
//接收执行结果字符串
String temp = "";
BufferedReader errBr = new BufferedReader(new InputStreamReader(process.getErrorStream()));
while (StringUtils.isNotEmpty(temp = errBr.readLine())) {
sb.append(temp).append("\r\n");
}
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
while (StringUtils.isNotEmpty(temp = br.readLine())) {
sb.append(temp).append("\r\n");
}
br.close();
errBr.close();
} catch (IOException e) {
e.printStackTrace();
}
//System.out.println(sb.toString());
return sb.toString();
}
}
四、文本文件处理工具类
在导入mysql数据库前,需要对文件进行一些操作,这里提供了对文件每一行字符进行处理并生成指定文件的类
package util; import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException; /**
*
* 文本操作工具
*
* @author 2018.03.14
*/
public class TextFileUtil {
private static final TextFileUtil instance = new TextFileUtil(); private TextFileUtil() { } public static TextFileUtil getInstance() {
return instance;
} /**
* 复制文件并处理每一行的字符串
*
* @param srcFile 源文件
* @param target 目标文件
* @param opertor 每行的字符串处理类
*/
public void transferLine(String srcFile, String target, OpertorInter opertor) {
if (srcFile.equals(target)) {
System.out.println("Warning : src file is same to target file");
return;
}
FileReader fr = null;
BufferedReader br = null; FileWriter fw = null;
BufferedWriter bw = null; String temp = null;
try {
fr = new FileReader(srcFile);
br = new BufferedReader(fr); fw = new FileWriter(target);
bw = new BufferedWriter(fw); while (null != (temp = br.readLine())) {
bw.write(opertor.transferLine(temp));
bw.newLine();
} } catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != br && null != fr) {
try {
br.close();
fr.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
br = null;
fr = null;
}
}
if (null != bw && null != fw) {
try {
bw.close();
fw.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
bw = null;
fw = null;
}
}
} } }
其中OpertorInter接口方法transferLine(str)提供了对每行数据的处理操作,这里是接口和其实现类:
package util; public interface OpertorInter {
String transferLine(String before);
}
package util; /**
* 对每条记录进行处理
*
* @author 2018.03.14
*/
public class ObjectIdOpertor implements OpertorInter { public String transferLine(String before) {
// 将空值设置为\N,否则在mysqlimport时会将空值的日期字段设置为0000-00-00 00:00:00
//首先是行尾的空值
before = before.replaceFirst(",$", ",\\\\N");
//再循环替换所有空值
String temp = "";
while (!temp.equals(before)) {
temp = before;
before = temp.replaceFirst(",,", ",\\\\N,");
} //mongodb导出文件中_id字段转换为字符串,只有ObjectId类型时才需要转换
if (before.startsWith("ObjectId")) {
return before.substring(9, 33) + before.substring(34);
}
return before;
}
}
五、Main方法
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern; import util.CmdUtil;
import util.ObjectIdOpertor;
import util.OpertorInter;
import util.TextFileUtil; /**
* 用于将mongodb中的表数据迁移到mysql数据库中
* @author 2018.03.14
*/
public class DBTransferTest {
public static void main(String[] args) {
// mongodb中集合字段,导出的文件以此为准
Map<String, String> fileds = new HashMap<String, String>();
fileds.put("Student", "_id,stuno,stuname,age,sex"); // mongo数据库信息
String host = "10.10.10.10:27027";
String database = "test";
String userName = "mydb";
String password = "mydb";
// mysql数据库信息
String hostForMysql = "10.10.10.10";
int portForMysql = 3306;
String userForMysql = "dbtest";
String passForMysql = "dbtest";
String databaseForMysql = "mydb"; // cmd命令運行
// mongodb运行目录
File dir = new File("D:/Program Files/MongoDB/Server/3.6/bin");
// mysql运行目录
File mySqlDir = new File("D:/Program Files/mysql-5.6.39-winx64/bin");
// mongodb导出命令 mongoexport -h <ip:port> -d <database> -c <collection> -u <username> -p <password> --type <json/csv> -f <fileds> -o <outputfile> --limit %d --skip %d
// --noHeaderLine 不输出列名
String exportSrcCmd = "mongoexport -h %s -d %s -c %s -u %s -p %s --type csv -f %s -o %s --limit %d --skip %d --noHeaderLine";
// mysql导入命令
// mysqlimport -h <ip> -P <port> -u <username> -p<password> --local <database> <inputfile> -c <columes> --fields-terminated-by=\\, --fields-enclosed-by=\\\" --lines-terminated-by=\\n --ignore-lines=1
// --ignore-lines=1 忽略首行的列名,上面导出时已忽略,这里就不再跳过第一行了
String importSrcCmd = "mysqlimport -h %s -P %d -u %s -p%s --local %s %s -c %s --fields-terminated-by=, --fields-enclosed-by=\\\" --lines-terminated-by=\\r\\n";
// 匹配命令执行结果中的导入导出数
String regForExport = "(\\d+) record";
String regForImport = "Records: (\\d+)";
// 分页导出的每页数据量大小
int limit = 10000;
// 分页参数
int skip = 0;
// 由于mongodb导出的文件中_id字段
OpertorInter opertor = new ObjectIdOpertor(); long st = System.currentTimeMillis();
for (Map.Entry<String, String> en : fileds.entrySet()) {
// 表名
String collection = en.getKey();
// 获取两数据库表的字段名 当前只有 _id --> id 不同
String filed = fileds.get(collection);
String filedForMysql = filed.substring(1);
// mongodb导出文件名
String output = "D:/" + collection + ".csv";
// mysql要导入的文件名,此处文件名决定了mysqlimport要导入的表名
String importFile = "D:/" + collection + ".txt"; long startTime = System.currentTimeMillis();
System.out.println("***********" + collection + "***********");
for (int pageNo = 0;; pageNo++) {
long pageSt = System.currentTimeMillis();
// pageNo这里从0开始
skip = pageNo * limit;
// mongoexport
System.out.print("----" + (skip + 1) + "~" + (skip + limit) + ":[Exporting:");
String exportCmd = String.format(exportSrcCmd, host, database, collection, userName, password, filed, output, limit, skip);
int exportNum = parseOperateNum(CmdUtil.getInstance().exec(exportCmd, dir), regForExport);
System.out.print(exportNum); // 处理文件将_id字段的Object()去除,只保留string类型的id
TextFileUtil.getInstance().transferLine(output, importFile, opertor); // mysqlimport
System.out.print("][Importing:");
String importCmd = String.format(importSrcCmd, hostForMysql, portForMysql, userForMysql, passForMysql, databaseForMysql, importFile, filedForMysql);
String reString = CmdUtil.getInstance().exec(importCmd, mySqlDir);
int importNum = parseOperateNum(reString, regForImport);
System.out.print(importNum + "]"); long pageEd = System.currentTimeMillis();
System.out.println("[Cost time:" + (pageEd-pageSt) + "ms]"); if (exportNum != importNum) {
System.out.println("Error and Break: importNum is not same to exportNum--" + reString);
break;
} // 如果导出数量为0或者导出数量还不到一页,则没有下一页
if (limit > exportNum || 0 == exportNum) {
System.out.println("----No records, export end.");
break;
}
}
long endTime = System.currentTimeMillis();
System.out.println("Cost time : " + (endTime - startTime) + "ms\r\n");
}
long et = System.currentTimeMillis();
System.out.println("***********TOTAL COST TIME : " + (et - st) / 60000f + "min***********");
} /**
*
* 获取匹配到的字符并转为int
* 这里用于获取cmd处理结果中的导入导出记录数,以判断是否最后一页数据
*
* @param re
* @param reg
* @return
*/
public static int parseOperateNum(String re, String reg) {
Pattern p = Pattern.compile(reg);
Matcher m = p.matcher(re);
if (m.find()) {
return Integer.parseInt(m.group(1));
}
return 0;
} }
Mongodb到mysql数据库的数据迁移(Java,Windows)的更多相关文章
- django 连接MYSQL时,数据迁移时报:django.db.utils.InternalError: (1366, "Incorrect string value: '\\xE9\\x97\\xAE\\xE9\\xA2\\x98' for column 'name' at row 5")
django 连接MYSQL时,数据迁移时报:django.db.utils.InternalError: (1366, "Incorrect string value: '\\xE9\\x ...
- Neo4j ETL工具快速上手:简化从关系数据库到图数据库的数据迁移
注:本文系从https://medium.com/neo4j/tap-into-hidden-connections-translating-your-relational-data-to-graph ...
- scrapy爬取海量数据并保存在MongoDB和MySQL数据库中
前言 一般我们都会将数据爬取下来保存在临时文件或者控制台直接输出,但对于超大规模数据的快速读写,高并发场景的访问,用数据库管理无疑是不二之选.首先简单描述一下MySQL和MongoDB的区别:MySQ ...
- CentOS6 更改Mysql数据库的数据存放位置
mysql使用yum安装时,默认的数据是存储在/var/lib/mysql下.一般情况下,为了数据的安全性,建议将mysql数据库的数据文件存储在系统的第二块磁盘上的目录下可以按照以下步骤进行操作: ...
- mysql数据库delete数据时不支持表别名
今天在帮同事查看一条删除的SQL语句执行出错的问题 SQL语句如下: 1 DELETE FROM LEAD_SYSTEM_MENU_ORG_REF as t WHERE t.resourceid='4 ...
- 转】mysql数据库delete数据时不支持表别名
原博文出自于: http://www.cnblogs.com/xdp-gacl/p/4012853.html 感谢! 今天在帮同事查看一条删除的SQL语句执行出错的问题 SQL语句如下: 1 DELE ...
- 使用python将mysql数据库的数据转换为json数据
由于产品运营部需要采用第三方个推平台,来推送消息.如果手动一个个键入字段和字段值,容易出错,且非常繁琐,需要将mysql的数据转换为json数据,直接复制即可. 本文将涉及到如何使用Python访问M ...
- 读取mysql数据库的数据,转为json格式
# coding=utf-8 ''' Created on 2016-10-26 @author: Jennifer Project:读取mysql数据库的数据,转为json格式 ''' import ...
- Loadrunner脚本优化-参数化之关联MySQL数据库获取数据
脚本优化-参数化之关联MySQL数据库获取数据 by:授客 QQ:1033553122 测试环境: Loadrunner 11 Win7 64位 实操: 1. 安装MySQL ODBC驱动程序 O ...
随机推荐
- apache做反向代理
实验目的 通过apache实现反向代理的功能,类似nginx反向代理和haproxy反向代理 环境准备 逻辑架构如下 前端是apche服务器,监听80端口,后端有两台web服务器,分别是node1和n ...
- Spring Boot的日志配置
一.配置logback日志 Spring Boot默认使用logback打印日志 需要增加依赖 <groupId>org.springframework.boot</groupId& ...
- 1080P60视频源---verilog
1080P60视频源---verilog `timescale 1ns / 1ps ////////////////////////////////////////////////////////// ...
- mysql导入sql文件出错的一种解决方法
转:https://blog.csdn.net/u011806486/article/details/60147358 本人在本地使用navicat for mysql可以连接到服务器数据库,但是从服 ...
- Winfrom BackgroundWorker
using System; using System.Collections.Generic; using System.ComponentModel; using System.Reflection ...
- win7和linux下利用命令查看文件md5、sha1、sha256
win7 certutil -hashfile <filename> MD5 certutil -hashfile <filename> SHA1 certutil -hash ...
- css3动画的简单学习
transform常用的属性(2D变化): translate(x,y) 定义 2D 转换. scale(x,y) 定义 2D 缩放转换 rotate(angle) 定义 2D 旋转,在参数中规定角度 ...
- ORC 文件存储格式
1.orc列式存储概念 a)列式存储:orc并不是纯粹的列式存储,也是先基于行对数据表进行分组(行组),然后对行组进行列式存储. b)查询数据的时候不需要扫描全部数据(磁盘IO),只需查询指定列即可. ...
- json2.js JSON解析程序
源码: /* http://www.JSON.org/json2.js 2010-03-20 Public Domain. NO WARRANTY EXPRESSED OR IMPLIED. USE ...
- Sysbench 1.0.17安装与测试
Sysbench安装与测试 1.安装: cd /usr/local/src wget https://codeload.github.com/akopytov/sysbench/tar.gz/1.0. ...