运行环境为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. Java基础随记-不定时更新

    一.hashMap与hashTable与ConcurrentHashMap: 1.HashMap是继承自AbstractMap类,而HashTable是继承自Dictionary类.不过它们都同时实现 ...

  2. taro 报错及解决

    1.解决:taro 升级到最新版(npm install -g @tarojs/cli) 错误 组件编译 组件src/pages/xxx/xxx.tsx编译失败! TypeError: callee. ...

  3. ie 9.10 兼容性问题 遇到的坑

    1.ie9 中ajax 跨域调用时 error报错信息为”No Transport”   原因是 ajax跨域 本人用的是 cors解决方案 但是ie9一下版本 对cors默认是不允许的所以需要我们自 ...

  4. CentOS下Redis的安装(转)

    目录 CentOS下Redis的安装 前言 下载安装包 解压安装包并安装 启动和停止Redis 启动Redis 停止Redis 参考资料 CentOS下Redis的安装 前言 安装Redis需要知道自 ...

  5. Kivy中文编程指南--https://cycleuser.gitbooks.io/kivy-guide-chinese/content/

    Kivy 是一个开源的 Python 框架,用于快速开发应用,实现各种当前流行的用户界面,比如多点触摸等等. + Kivy 可以运行于 Windows, Linux, MacOS, Android, ...

  6. php7 pdo抽象类操作数据库

    查询 <?php try { $dbconnect = new PDO('mysql:host=localhost;dbname=pdodatabase','root','753951'); } ...

  7. HTML与CSS:结构与表现

    在HTML和CSS里存在着部分重复的功能,例如两者都可以设定一段文字的字体属性.既然如此,为啥还要CSS呢(至少,为啥CSS里存在着和HTML标签属性重复的东西呢)? 这是因为,HTML和CSS的用途 ...

  8. CSS Core Technology

    1. Selector Different types of selectors: Selectors can be divided into the following categories: Si ...

  9. CSS的块级元素和内联元素的概念

    三生有幸,偶然之下知道了<CSS世界>这本书,让我产生了探究 CSS 的想法. 这里对 CSS 中的块级元素和内联元素的概念做一个简单的整理. 可能对于我们前端开发人员来讲,一般接触到的元 ...

  10. CentOS初次安装基本配置

    在虚拟机中安装CentOS7碰到的问题以及解决方法 1.安装之后想通过CRT远程连接获,输入ifconfig查看系统ip报错误:ifconfig command not found,报此错误是由于初次 ...