运行环境为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)的更多相关文章

  1. 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 ...

  2. Neo4j ETL工具快速上手:简化从关系数据库到图数据库的数据迁移

    注:本文系从https://medium.com/neo4j/tap-into-hidden-connections-translating-your-relational-data-to-graph ...

  3. scrapy爬取海量数据并保存在MongoDB和MySQL数据库中

    前言 一般我们都会将数据爬取下来保存在临时文件或者控制台直接输出,但对于超大规模数据的快速读写,高并发场景的访问,用数据库管理无疑是不二之选.首先简单描述一下MySQL和MongoDB的区别:MySQ ...

  4. CentOS6 更改Mysql数据库的数据存放位置

    mysql使用yum安装时,默认的数据是存储在/var/lib/mysql下.一般情况下,为了数据的安全性,建议将mysql数据库的数据文件存储在系统的第二块磁盘上的目录下可以按照以下步骤进行操作: ...

  5. mysql数据库delete数据时不支持表别名

    今天在帮同事查看一条删除的SQL语句执行出错的问题 SQL语句如下: 1 DELETE FROM LEAD_SYSTEM_MENU_ORG_REF as t WHERE t.resourceid='4 ...

  6. 转】mysql数据库delete数据时不支持表别名

    原博文出自于: http://www.cnblogs.com/xdp-gacl/p/4012853.html 感谢! 今天在帮同事查看一条删除的SQL语句执行出错的问题 SQL语句如下: 1 DELE ...

  7. 使用python将mysql数据库的数据转换为json数据

    由于产品运营部需要采用第三方个推平台,来推送消息.如果手动一个个键入字段和字段值,容易出错,且非常繁琐,需要将mysql的数据转换为json数据,直接复制即可. 本文将涉及到如何使用Python访问M ...

  8. 读取mysql数据库的数据,转为json格式

    # coding=utf-8 ''' Created on 2016-10-26 @author: Jennifer Project:读取mysql数据库的数据,转为json格式 ''' import ...

  9. Loadrunner脚本优化-参数化之关联MySQL数据库获取数据

    脚本优化-参数化之关联MySQL数据库获取数据 by:授客 QQ:1033553122 测试环境: Loadrunner 11 Win7 64位 实操: 1.   安装MySQL ODBC驱动程序 O ...

随机推荐

  1. 在mysql配置文件修改sql_mode或sql-mode 怎么办?

    很多在安装程序配置数据库这一步中会出现: 请在mysql配置文件修改sql_mode或sql-mode 这个问题处理很简单: mysql中修改my.cnf,找到sql_mode,修改值为: NO_AU ...

  2. spring事务详解(二)简单样例

    系列目录 spring事务详解(一)初探事务 spring事务详解(二)简单样例 spring事务详解(三)源码详解 spring事务详解(四)测试验证 spring事务详解(五)总结提高 一.引子 ...

  3. [转]微软商店 打开就显示无法加载该页面 代码0x80131500?

    在某博客看到的方法,供参考,可以尝试一下,我的也是这么解决的 1.打开“运行”输入 inetcpl.cpl (“WINDOWS”+“R”键,输入 inetcpl.cpl亦可) 2.点开高级往下拉,勾上 ...

  4. OpenStack控制节点上搭建Q版keystone服务(step3)

    keystone服务监听两个端口:5000和35357 一.安装和配置 1.安装keystone组件 yum install openstack-keystone httpd mod_wsgi -y ...

  5. WEB 性能优化导图

    看了一下网上对于web性能优化的一些帖子,不是很直观,花了点时间画了一个思维导图. refers: https://segmentfault.com/a/1190000011936772 https: ...

  6. consul配置参数大全、详解、总结

    命令行选项 以下选项全部在命令行中指定. -advertise - 通告地址用于更改我们通告给集群中其他节点的地址.默认情况下,-bind地址是通告的.但是,在某些情况下,可能存在无法绑定的可路由地址 ...

  7. spring boot websocket stomp 实现广播通信和一对一通信聊天

    一.前言 玩.net的时候,在asp.net下有一个叫 SignalR 的框架,可以在ASP .NET的Web项目中实现实时通信.刚接触java寻找相关替代品,发现 java 体系中有一套基于stom ...

  8. java 垃圾回收总结(1)(转)

    转自: http://www.cnblogs.com/aigongsi/archive/2012/04/06/2434771.html 另外可参考: http://ifeve.com/gc-orien ...

  9. Java垃圾回收(整理)

    Java垃圾回收 Garbage Collection:GC: 什么样的对象才是垃圾?怎样判断一个对象引用是不是垃圾? 垃圾回收算法:Mark-Sweep(标记-清除)算法,Copying(复制)算法 ...

  10. (详细)华为荣耀8X JSN-AL00的usb调试模式在哪里开启的教程

    经常我们使用Pc链接安卓手机的时候,如果手机没有开启usb开发者调试模式,Pc则没办法成功识别我们的手机,有时候,我们使用的一些功能比较强的的工具比如之前我们使用的一个工具引号精灵,老版本就需要开启u ...