Skip to content

before

mongoexport和mongoimport

先来看这两个命令的常用参数解释:

bash
# 使用 --help 来查看 mongoexport 的用法
[mongod@cs ~]$ mongoexport --help
-h:主机名
-u:用户名
-p:密码
--authenticationDatabase:认证库
-d:要导出的库名
-c:要导出库下的集合
-f:要导出的字段
-o:导出数据的文件名
-q:导出数据时的过滤条件
--type:导出数据的格式,默认为json格式,可以通过 --type=csv 导出数据为csv格式

# 使用 --help 来查看 mongoexport 的用法
[mongod@cs ~]$ mongoimport --help
-h:主机名
-u:用户名
-p:密码
--authenticationDatabase:认证库
-d:要导入的库名
-c:要导入库下的集合
-f:要导入的字段
--type:导入数据的格式
--headerline:如果导入的文件是csv文件,并且有title行,使用title行作为字段
 --file:导入文件的路径

导入导出json格式数据

导出json格式数据

准备数据:

bash
[mongod@cs ~]$ mongo -uroot -p1234
> use t1
> for(i=1;i<=100;i++){db.s1.insert({"id":i,"name":"zhangkai","age":i,"date":new Date()})}
WriteResult({ "nInserted" : 1 })
> db.s1.find({id:1})
{ "_id" : ObjectId("60010ac816e872408ff3630c"), "id" : 1, "name" : "zhangkai", "age" : 1, "date" : ISODate("2021-01-15T03:23:52.645Z") }
> db.s1.count()
100

现在,我们将上面t1库中的s1表中的数据导出为json格式的数据,并保存到磁盘。

bash
[mongod@cs ~]$ mongoexport -uroot -p1234 --port 27017 --authenticationDatabase admin -d t1 -c s1 -o /tmp/s1.json
2021-01-15T11:26:56.960+0800	connected to: localhost:27017
2021-01-15T11:26:56.963+0800	exported 100 records
[mongod@cs ~]$ wc -l /tmp/s1.json 		# wc -l 统计文件的行数
100 /tmp/s1.json
[mongod@cs ~]$ cat /tmp/s1.json | head -n 3		# 返回文件的前三行
{"_id":{"$oid":"60010ac816e872408ff3630c"},"id":1.0,"name":"zhangkai","age":1.0,"date":{"$date":"2021-01-15T03:23:52.645Z"}}
{"_id":{"$oid":"60010ac816e872408ff3630d"},"id":2.0,"name":"zhangkai","age":2.0,"date":{"$date":"2021-01-15T03:23:52.659Z"}}
{"_id":{"$oid":"60010ac816e872408ff3630e"},"id":3.0,"name":"zhangkai","age":3.0,"date":{"$date":"2021-01-15T03:23:52.660Z"}}

注意,MongoDB默认导出的是json格式的数据,并且一次只能将一个集合导出到一个文件。

导入json格式数据

bash
[mongod@cs ~]$ mongoimport -uroot -p1234 --port 27017 --authenticationDatabase admin -d t1 -c s2 /tmp/s1.json
2021-01-15T11:36:32.867+0800	connected to: localhost:27017
2021-01-15T11:36:32.877+0800	imported 100 documents
[mongod@cs ~]$ mongo -uroot -p1234
> use t1
switched to db t1
> db.s2.count()
100
> db.s2.find({id:1})
{ "_id" : ObjectId("60010ac816e872408ff3630c"), "id" : 1, "name" : "zhangkai", "age" : 1, "date" : ISODate("2021-01-15T03:23:52.645Z") }

导入和导出json格式数据相对简单。

导入导出csv格式数据

还是使用上面的t1库中的s1集合作为演示。

导出csv格式数据

bash
[mongod@cs ~]$ mongoexport -uroot -p1234 --port 27017 --authenticationDatabase admin -d t1 -c s1 --type=csv -f id,name,age,date -o /tmp/s1.csv 
2021-01-15T11:47:47.934+0800	connected to: localhost:27017
2021-01-15T11:47:47.936+0800	exported 100 records
[mongod@cs ~]$ wc -l /tmp/s1.csv 	# 多出来的一行是title行
101 /tmp/s1.csv
[mongod@cs ~]$ cat /tmp/s1.csv | head -n 3
id,name,age,date
1,zhangkai,1,2021-01-15T03:23:52.645Z
2,zhangkai,2,2021-01-15T03:23:52.659Z

其中:

  • --type表示导出的数据为csv格式。
  • -f表示要导出哪些字段,且这些字段会称为csv文件的title。

导入csv格式数据

bash
[mongod@cs ~]$ mongoimport -uroot -p1234 --port 27017 --authenticationDatabase admin -d t1 -c s3 --type=csv --headerline --file /tmp/s1.csv 
2021-01-15T11:53:50.540+0800	connected to: localhost:27017
2021-01-15T11:53:50.547+0800	imported 100 documents
[mongod@cs ~]$ mongo -uroot -p1234
> use t1
switched to db t1
> db.s3.count()
100
> db.s3.find({id:1})
{ "_id" : ObjectId("600111cedcfab91917ebdbab"), "id" : 1, "name" : "zhangkai", "age" : 1, "date" : "2021-01-15T03:23:52.645Z" }

其中:

  • --type,因为源数据是csv数据,所以使用该参数进行声明。
  • --headerline,因为s1.csv文件的首行是title,所以,使用这个参数声明使用文件中的title行作为字段。

注意,如果源数据csv文件没有title行,那么你导入的时候,就要手动指定了。

bash
# 如果 csv 文件没有 title
18,zhangkai,18,2021-01-15T03:23:52.645Z
29,zhangkai,19,2021-01-15T03:23:52.645Z

# 导入时要通过 -f 参数指定字段,否则导入失败
mongoimport -uroot -p1234 --port 27017 --authenticationDatabase admin -d t1 -c s4 --type=csv -f id,name,age,date --file /tmp/s1.csv

除此之外,还有一种情况,需要我们注意,那就是集合中存储的文档是嵌套结构的,比如:

json
{
	"_id" : ObjectId("6001157f93473d2a5f0938e7"),
	"id" : 1,
	"name" : "zhangkai",
	"age" : 1,
	"date" : ISODate("2021-01-15T04:09:35.238Z"),
	"info" : {
		"tel" : 13245546776,
		"city" : "北京"
	}
}

那这种嵌套结构的集合该如何导出?导出的结果又是什么样的?一起来看看:

bash
[mongod@cs ~]$ mongo -uroot -p1234
> use t1
switched to db t1
> db.s5.insert({"id" : 1, "name" : "zhangkai", "age" : 1, "date" : new Date(), "info": {"tel":13245546776, "city": "北京"}})
WriteResult({ "nInserted" : 1 })
> exit
bye
[mongod@cs ~]$ mongoexport -uroot -p1234 --port 27017 --authenticationDatabase admin -d t1 -c s5 --type=csv -f id,name,age,date,info -o /tmp/s5.csv 
2021-01-15T12:11:23.089+0800	connected to: localhost:27017
2021-01-15T12:11:23.089+0800	exported 1 record
[mongod@cs ~]$ cat /tmp/s5.csv
id,name,age,date,info
1,zhangkai,1,2021-01-15T04:09:35.238Z,"{""tel"":1.3245546776e+10,""city"":""北京""}"

可以看到,嵌套结构整体被当作为一个字符串了,但这种数据在导入到MongoDB中就不那么美丽了:

bash
[mongod@cs ~]$ mongoimport -uroot -p1234 --port 27017 --authenticationDatabase admin -d t1 -c s6 --type=csv --headerline --file /tmp/s5.csv 
2021-01-15T12:16:11.281+0800	connected to: localhost:27017
2021-01-15T12:16:11.290+0800	imported 1 document
[mongod@cs ~]$ mongo -uroot -p1234
> use t1
switched to db t1
> db.s6.find({id:1}).pretty()
{
	"_id" : ObjectId("6001170bdcfab91917ebdc59"),
	"id" : 1,
	"name" : "zhangkai",
	"age" : 1,
	"date" : "2021-01-15T04:09:35.238Z",
	"info" : "{\"tel\":1.3245546776e+10,\"city\":\"北京\"}"
}

由结果看到,嵌套结构并没有自动处理,而是原封不动的处理为字符串了。怎么解决?对于嵌套结构的数据,导出时选择json,避免这种文件,或者Python手动来处理和导入。

虽然csv文件固然有这样的缺点,但它也有点,那就是可以和关系型数据库打交道,实现异构平台数据迁移。

异构平台数据迁移

在实际开发中,通常会遇到将MySQL中的数据迁移到MongoDB中,所以,这里我们再来演示下,如何将MySQL数据迁移到MongoDB中。

首先,在MySQL中设置安全目录,使允许导入导出:

bash
[root@cs tmp]# vim /etc/my.cnf

[mysqld]
secure-file-priv=/tmp

[root@cs tmp]# systemctl restart mysqld

然后准备数据,并将数据导出为csv文件。

sql
-- 创建SQL文件
[root@cs tmp]# vim class.sql

DROP DATABASE IF EXISTS school;
CREATE DATABASE school CHARSET utf8;
USE school
SET AUTOCOMMIT=0;
SET NAMES utf8;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
--  Table structure for `class`
-- ----------------------------
DROP TABLE IF EXISTS `class`;
CREATE TABLE `class` (
  `cid` int(11) NOT NULL AUTO_INCREMENT,
  `caption` varchar(32) NOT NULL,
  PRIMARY KEY (`cid`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

-- ----------------------------
--  Records of `class`
-- ----------------------------
BEGIN;
INSERT INTO `class` VALUES ('1', '三年二班'), ('2', '三年三班'), ('3', '一年二班'), ('4', '二年九班');
COMMIT;

-- 将数据导入到MySQL中,此时会在 school 库中创建 class 表,并生成4条记录
[root@cs tmp]# mysql -uroot -p123 <class.sql
[root@cs tmp]# mysql -uroot -p123
3306 [(none)]>select * from school.class;
+-----+--------------+
| cid | caption      |
+-----+--------------+
|   1 | 三年二班     |
|   2 | 三年三班     |
|   3 | 一年二班     |
|   4 | 二年九班     |
+-----+--------------+
4 rows in set (0.00 sec)

-- 这一步将 class 表中的数据导出到 /tmp/class.csv,并且字段之间以逗号分隔,默认是空格
3306 [(none)]>select * from school.class into outfile '/tmp/class.csv' fields terminated by ',';
Query OK, 4 rows affected (0.01 sec)

3306 [(none)]>exit
Bye
[root@cs tmp]# wc -l class.csv 
4 class.csv
[root@cs tmp]# cat class.csv 
1,三年二班
2,三年三班
3,一年二班
4,二年九班

由上述cat查看文件,发现csv文件并没有title行,所以,后续我们导入到MongoDB时,要注意这点。

现在,我们将csv文件导入到MongoDB:

bash
[mongod@cs ~]$ mongoimport -uroot -p1234 --port 27017 --authenticationDatabase admin -d t1 -c s7 --type=csv -f cid,caption --file /tmp/class.csv
2021-01-15T15:06:35.270+0800	connected to: localhost:27017
2021-01-15T15:06:35.278+0800	imported 4 documents
[mongod@cs ~]$ mongo -uroot -p1234
MongoDB shell version v3.6.12
connecting to: mongodb://127.0.0.1:27017/?gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("c40b96fc-6501-4cdc-98a7-76eaa9868692") }
MongoDB server version: 3.6.12
> use t1
switched to db t1
> db.s7.find()
{ "_id" : ObjectId("60013efbdcfab91917ebdc98"), "cid" : 1, "caption" : "三年二班" }
{ "_id" : ObjectId("60013efbdcfab91917ebdc99"), "cid" : 3, "caption" : "一年二班" }
{ "_id" : ObjectId("60013efbdcfab91917ebdc9a"), "cid" : 2, "caption" : "三年三班" }
{ "_id" : ObjectId("60013efbdcfab91917ebdc9b"), "cid" : 4, "caption" : "二年九班" }

可以看到,非常成功。

而一般很少有将MongoDB中的数据迁移到MySQL中,所以,这里不再多表。

mongodump和mongorestore

mongodump能够在MongoDB运行期间进行数据备份,它可以:

  • 全备,对MongoDB中所有的数据进行备份。
  • 备份指定库,单独对某个库的数据进行备份。
  • 备份指定集合,单独对某个库下的某个集合进行备份。
  • 压缩备份,可以在备份时,进行数据压缩。

mongodump工作原理就是对运行的MongoDB做查询,将查询结果写入磁盘,达到备份目的。但如果在备份时仍有写入操作,这些数据将不会被备份。所以,mongodump备份的数据完全和MongoDB的实时数据相等,并且在备份时会对性能有一定影响。建议在晚间进行备份,同时避开balancer的工作时间窗口。

mongodump的用法如下:

bash
# 使用 --help 来查看 mongodump 的用法
[mongod@cs ~]$ mongodump --help
-h:主机名
-u:用户名
-p:密码
--authenticationDatabase:认证库
-d:指定库
-c:指定集合
-o:指定导出文件名
-q:导出数据的过滤条件
-j, --numParallelCollections:并发备份,默认值是4
--oplog:备份的同时备份oplog
--gzip:压缩备份

再来看mongorestore,它能:

  • 从全备中恢复全部数据。
  • 从全备中恢复指定库。
  • 从全备中恢复指定库下指定集合。

mongorestore的用法如下:

bash
# 使用 --help 来查看 mongorestore 的用法
[mongod@cs ~]$ mongorestore --help
-h:主机名
-u:用户名
-p:密码
--authenticationDatabase:认证库
-d:恢复到指定库
-c:恢复到指定集合
--drop:(谨慎使用)覆盖恢复

来看具体怎么用。

备份

准备一些目录用于存放被备份:

bash
[mongod@cs ~]$ mkdir -p /data/mongodb_data/backup/{full,full_zip,t1,t1_zip,s1,s1_zip}
[mongod@cs ~]$ ll /data/mongodb_data/backup/
total 0
drwxrwxr-x. 2 mongod mongod 6 Jan 15 16:35 full		# 存放全备数据
drwxrwxr-x. 2 mongod mongod 6 Jan 15 16:35 full_zip	# 存放压缩全备数据
drwxrwxr-x. 2 mongod mongod 6 Jan 15 16:35 s1		# 存放s1集合的备份数据
drwxrwxr-x. 3 mongod mongod 6 Jan 15 16:35 s1_zip	# 存放s1集合的压缩备份数据
drwxrwxr-x. 2 mongod mongod 6 Jan 15 16:35 t1		# 存放t1库的备份数据
drwxrwxr-x. 3 mongod mongod 6 Jan 15 16:35 t1_zip	# 存放t1库的压缩备份数据

来看具体备份命令:

bash

# 备份指定库,--gzip表示开启压缩备份
mongodump -uroot -p123 --port 27017 --authenticationDatabase admin -d t1 -o /data/mongodb_data/backup/t1
mongodump -uroot -p123 --port 27017 --authenticationDatabase admin -d t1 -o /data/mongodb_data/backup/t1 --gzip

# 备份指定库下指定集合,--gzip表示开启压缩备份
mongodump -uroot -p123 --port 27017 --authenticationDatabase admin -d t1 -c s1 -o /data/mongodb_data/backup/s1
mongodump -uroot -p123 --port 27017 --authenticationDatabase admin -d t1 -c s1 -o /data/mongodb_data/backup/s1 --gzip

全库备份

bash
# 全库备份,--gzip表示开启压缩备份
mongodump -uroot -p123 --port 27017 --authenticationDatabase admin -o /data/mongodb_data/backup/full
mongodump -uroot -p123 --port 27017 --authenticationDatabase admin -o /data/mongodb_data/backup/full_zip --gzip

[mongod@cs ~]$ mongodump -uroot -p1234 --port 27017 --authenticationDatabase admin -o /data/mongodb_data/backup/full
[mongod@cs ~]$ ls /data/mongodb_data/backup/full
admin  t1  t2  t3
[mongod@cs ~]$ ls /data/mongodb_data/backup/full/admin/
system.users.bson  system.users.metadata.json  system.version.bson  system.version.metadata.json
[mongod@cs ~]$ ls /data/mongodb_data/backup/full/t1/
s1.bson           s2.bson           s3.bson           s4.bson           s5.bson           s6.bson           s7.bson
s1.metadata.json  s2.metadata.json  s3.metadata.json  s4.metadata.json  s5.metadata.json  s6.metadata.json  s7.metadata.json

由备份结果可以看到,mongodump备份了MongoDB实例中的:

  • 系统库只备份admin库,因为该库存储了用户信息。
  • 所有的应用库。

然后每个库备份到本地是个目录,其目录内存储了库中的各个集合,而每个集合会保存为两个:

  • collection.bson,就是二进制的json文件。
  • collection.metadata.json是元数据,存储集合的字段信息。

再来看压缩备份有啥区别:

bash
[mongod@cs ~]$ mongodump -uroot -p1234 --port 27017 --authenticationDatabase admin -o /data/mongodb_data/backup/full_zip --gzip
[mongod@cs ~]$ ls /data/mongodb_data/backup/full_zip/
admin  t1  t2  t3
[mongod@cs ~]$ ls /data/mongodb_data/backup/full_zip/admin/
system.users.bson.gz  system.users.metadata.json.gz  system.version.bson.gz  system.version.metadata.json.gz
[mongod@cs ~]$ ls /data/mongodb_data/backup/full_zip/t1/
s1.bson.gz           s2.metadata.json.gz  s4.bson.gz           s5.metadata.json.gz  s7.bson.gz
s1.metadata.json.gz  s3.bson.gz           s4.metadata.json.gz  s6.bson.gz           s7.metadata.json.gz
s2.bson.gz           s3.metadata.json.gz  s5.bson.gz           s6.metadata.json.gz

区别就是,将原备份文件压缩了!

备份指定库

通过-d参数来指定要备份的库:

bash
# 备份指定库,--gzip表示开启压缩备份 
mongodump -uroot -p1234 --port 27017 --authenticationDatabase admin -d t1 -o /data/mongodb_data/backup/t1
mongodump -uroot -p1234 --port 27017 --authenticationDatabase admin -d t1 -o /data/mongodb_data/backup/t1_zip --gzip

# 示例
[mongod@cs ~]$ mongodump -uroot -p1234 --port 27017 --authenticationDatabase admin -d t1 -o /data/mongodb_data/backup/t1
[mongod@cs ~]$ ls /data/mongodb_data/backup/t1
t1
[mongod@cs ~]$ ls /data/mongodb_data/backup/t1/t1/
s1.bson           s2.bson           s3.bson           s4.bson           s5.bson           s6.bson           s7.bson
s1.metadata.json  s2.metadata.json  s3.metadata.json  s4.metadata.json  s5.metadata.json  s6.metadata.json  s7.metadata.json

[mongod@cs ~]$ mongodump -uroot -p1234 --port 27017 --authenticationDatabase admin -d t1 -o /data/mongodb_data/backup/t1_zip --gzip
[mongod@cs ~]$ ls /data/mongodb_data/backup/t1_zip/
t1
[mongod@cs ~]$ ls /data/mongodb_data/backup/t1_zip/t1/
s1.bson.gz           s2.metadata.json.gz  s4.bson.gz           s5.metadata.json.gz  s7.bson.gz
s1.metadata.json.gz  s3.bson.gz           s4.metadata.json.gz  s6.bson.gz           s7.metadata.json.gz
s2.bson.gz           s3.metadata.json.gz  s5.bson.gz           s6.metadata.json.gz

备份指定库也就是备份所有库中的其中一个库,但思路和全备一样。

备份指定集合

通过-d指定备份的库,通过-c指定备份该库下的指定集合:

bash
# 备份指定库下指定集合,--gzip表示开启压缩备份
mongodump -uroot -p1234 --port 27017 --authenticationDatabase admin -d t1 -c s1 -o /data/mongodb_data/backup/s1
mongodump -uroot -p1234 --port 27017 --authenticationDatabase admin -d t1 -c s1 -o /data/mongodb_data/backup/s1_zip --gzip

# 示例
[mongod@cs ~]$ ls /data/mongodb_data/backup/s1
t1
[mongod@cs ~]$ ls /data/mongodb_data/backup/s1/t1/
s1.bson  s1.metadata.json
[mongod@cs ~]$ ls /data/mongodb_data/backup/s1_zip/
t1
[mongod@cs ~]$ ls /data/mongodb_data/backup/s1_zip/t1/
s1.bson.gz  s1.metadata.json.gz

备份指定集合,思路跟备份指定库一样,只不过只备份该库中的其中一个集合。

恢复

恢复全库备份

我这里准备了一个新的MongoDB实例,并监听28020端口,这里演示如何将上面的全备数据恢复到28020这个库中:

bash
[mongod@cs ~]$ mongorestore -uroot -p1234 --port 28020 --authenticationDatabase admin /data/mongodb_data/backup/full
[mongod@cs ~]$ mongo -uroot -p1234 --port 28020
> show dbs;
admin   0.000GB
config  0.000GB
local   0.000GB
t1      0.000GB
t2      0.000GB
t3      0.000GB
> use t1
switched to db t1
> show tables;
s1
s2
s3
s4
s5
s6
s7
> db.s1.count()
100

由查询结果可以发现,我们完整了恢复了全备数据。

再来看,恢复带压缩的备份怎么恢复:

bash
mongorestore -uroot -p1234 --port 28020 --authenticationDatabase admin /data/mongodb_data/backup/full_zip --gzip
2021-01-15T18:37:06.938+0800	no indexes to restore
2021-01-15T18:37:06.938+0800	finished restoring t1.s3 (100 documents)
2021-01-15T18:37:06.938+0800	restoring users from /data/mongodb_data/backup/full_zip/admin/system.users.bson.gz
2021-01-15T18:37:06.957+0800	done

因为之前已经恢复了全部数据,所以这里提示恢复失败,也就是恢复时,默认无法覆盖恢复数据,那么能否设置覆盖恢复呢?答案是可以的,就是使用--drop参数来声明覆盖恢复数据:

bash
[mongod@cs ~]$ mongorestore -uroot -p1234 --port 28020 --authenticationDatabase admin --drop /data/mongodb_data/backup/full_zip --gzip
[mongod@cs ~]$ mongo -uroot -p1234 --port 28020
> use t1
switched to db t1
> db.s1.count()
100

由查询结果可以发现,覆盖恢复数据也没问题。

注意,请谨慎使用--drop参数。

从全库备份中恢复指定库

bash
# 从全备中恢复指定库,如果从压缩备份中恢复,还需要携带 --gzip 如果覆盖恢复,需要添加参数 --drop
mongorestore -uroot -p1234 --port 28020 --authenticationDatabase admin -d t1 /data/mongodb_data/backup/full/t1
mongorestore -uroot -p1234 --port 28020 --authenticationDatabase admin -d t1 /data/mongodb_data/backup/full_zip/t1 --gzip

从全库备份中恢复指定库下的指定集合

bash
# 从全备中恢复指定库下的指定集合,如果从压缩备份中恢复,还需要携带 --gzip 如果覆盖恢复,需要添加参数 --drop
mongorestore -uroot -p1234 --port 28020 --authenticationDatabase admin -d t1 -c s1 /data/mongodb_data/backup/full/t1/s1.bson
mongorestore -uroot -p1234 --port 28020 --authenticationDatabase admin -d t1 -c s1 /data/mongodb_data/backup/full_zip/t1/s1.bson.gz --gzip

上面演示的都是从全备数据中恢复数据,接下来在简要说说从备份的指定库、指定集合如何恢复数据,其实思路都是一样的。

从备份指定库或者指定集合中恢复数据

bash
# 从备份的库中恢复数据到指定库,如果从压缩备份中恢复,还需要携带 --gzip 如果覆盖恢复,需要添加参数 --drop
mongorestore -uroot -p1234 --port 28020 --authenticationDatabase admin -d t1 --drop /data/mongodb_data/backup/t1
mongorestore -uroot -p1234 --port 28020 --authenticationDatabase admin -d t1 --drop /data/mongodb_data/backup/t1_zip --gzip


# 从备份的集合中恢复数据,如果从压缩备份中恢复,还需要携带 --gzip 如果覆盖恢复,需要添加参数 --drop
mongorestore -uroot -p1234 --port 28020 --authenticationDatabase admin -d t1 --drop /data/mongodb_data/backup/s1/t1/s1.bson
mongorestore -uroot -p1234 --port 28020 --authenticationDatabase admin -d t1 --drop /data/mongodb_data/backup/s1_zip/t1/s1.bson.gz --gzip

Linux统计文件行数 | Linux命令:查看某个文件指定行信息(前n行,后n行)