数据的备份与恢复
实验准备
打开实验环境,在终端输入如下命令启动 MongoDB 服务,进入命令行交互客户端:
$ cd
$ sudo service mongodb start
$ mongo
这里打开交互客户端是为了验证数据迁移的结果。
数据库的备份与恢复
在 MongoDB 客户端中查看目前有哪些数据库:
> show dbs
admin 0.000GB
challenge 0.000GB
config 0.000GB
learn 0.000GB
local 0.000GB
query 0.000GB
shiyanlou 0.000GB
>
接下来我们使用 shiyanlou 数据库进行学习,查看其中的集合和文档:
> use shiyanlou
switched to db shiyanlou
> show collections
player
> db.player.find()
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f89"), "name" : "Wade", "age" : 34, "addr" : [ "Mim", "Chi" ] }
{ "_id" : ObjectId("5e4b7ab4e009482777d42425"), "name" : "Kobe", "age" : 37, "addr" : [ "Tor", "Los" ] }
>
数据库的备份
在 Mongodb 中我们使用 mongodump 命令来备份 MongoDB 数据。该命令可以导出数据库的所有集合到指定目录中。
mongodump 命令的格式如下:
$ mongodump -h dbhost -d dbname -o dbdirectory
-h 也可以写作 --host ,为主机名和端口号,默认为 localhost:27017 或者 127.0.0.1:27017
-d 也可以写作 --db ,指定数据库名字
-o 也可以写作 --out ,指定备份目录
打开一个新的终端标签,切换到 /home/shiyanlou/Code 目录:
$ cd ~/Code
执行如下命令即可将 shiyanlou 数据库中的全部集合(其实只有一个)数据存储到当前目录下的 shiyanlou 目录:
shiyanlou:Code/ $ mongodump -h localhost:27017 -d shiyanlou -o .
2020-02-21T11:09:46.731+0800 writing shiyanlou.player to
2020-02-21T11:09:46.750+0800 done dumping shiyanlou.player (2 documents)
shiyanlou:Code/ $ ll
总用量 4.0K
drwxr-xr-x 2 shiyanlou shiyanlou 4.0K 2月 21 11:09 shiyanlou
shiyanlou:Code/ $ tree shiyanlou
shiyanlou
├── player.bson
└── player.metadata.json
0 directories, 2 files
shiyanlou:Code/ $
如上所示,执行 mongodump 命令后,当前目录下出现了 shiyanlou 目录,这个目录结构就是存储 shiyanlou 数据库的目录。新增目录的名字与数据库名一致。其中 -o 选项后面的 . 表示当前目录,也可以规定一个目录,比如 test :
shiyanlou:Code/ $ mongodump -h localhost:27017 -d shiyanlou -o test
2020-02-21T11:12:09.171+0800 writing shiyanlou.player to
2020-02-21T11:12:09.200+0800 done dumping shiyanlou.player (2 documents)
shiyanlou:Code/ $ ll
总用量 8.0K
drwxr-xr-x 2 shiyanlou shiyanlou 4.0K 2月 21 11:09 shiyanlou
drwxr-xr-x 3 shiyanlou shiyanlou 4.0K 2月 21 11:12 test
shiyanlou:Code/ $ tree test/
test/
└── shiyanlou
├── player.bson
└── player.metadata.json
1 directory, 2 files
shiyanlou:Code/ $
这样的话,会在当前目录下新建 test 目录,test 目录里面是存储数据库的目录结构 shiyanlou 。
操作截图如下:
数据库的恢复
我们可以使用 mongorestore 命令来恢复数据库,也就是将上面备份得到的数据库目录结构导入到数据库中。
mongorestore 命令的格式如下:
$ mongorestore -h host -d dbname dir
-h 也可以写作 --host ,为主机名和端口号,默认为 localhost:27017 或者 127.0.0.1:27017
-d 也可以写作 --db ,指定数据库名字
dir 最后一个参数指定备份的目录
现在执行如下命令将当前目录下的 shiyanlou 目录导入到数据库中,这会新建一个数据库。因为目前已经存在 shiyanlou 数据库,所以我们新建一个 s1 数据库,将 shiyanlou 目录导入。
命令如下:
$ mongorestore -h localhost:27017 -d s1 shiyanlou
操作截图如下:
此时我们切回到 MongoDB 客户端查看操作结果:
> show dbs
admin 0.000GB
challenge 0.000GB
config 0.000GB
learn 0.000GB
local 0.000GB
query 0.000GB
s1 0.000GB
shiyanlou 0.000GB
> use s1
switched to db s1
> show collections
player
> db.player.find()
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f89"), "name" : "Wade", "age" : 34, "addr" : [ "Mim", "Chi" ] }
{ "_id" : ObjectId("5e4b7ab4e009482777d42425"), "name" : "Kobe", "age" : 37, "addr" : [ "Tor", "Los" ] }
>
一如预期,确实新增了 s1 数据库,且其中有一个 player 集合,集合中的数据与 shiyanlou.player 集合一致。
选择部分集合备份
有些时候,我们不想备份一个数据库里的全部集合。在使用 mongodump 命令的时候,可以增加一个 -c 选项选择某个集合进行备份:
$ mongodump -h dbhost -d dbname -c collection -o dbdirectory
-c 也可以写作 --collection ,指定集合
当前 MongoDB 中的 query 数据库里有两个集合,我们使用这个数据库来演示:
> show dbs
admin 0.000GB
challenge 0.000GB
config 0.000GB
learn 0.000GB
local 0.000GB
query 0.000GB
s1 0.000GB
shiyanlou 0.000GB
> use query
switched to db query
> show collections
stu
type
>
备份数据库 query ,且只备份 stu 集合:
shiyanlou:Code/ $ mongodump -h localhost:27017 -d query -c stu -o .
2020-02-21T11:29:31.801+0800 writing query.stu to
2020-02-21T11:29:31.841+0800 done dumping query.stu (7 documents)
shiyanlou:Code/ $ ll
总用量 12K
drwxr-xr-x 2 shiyanlou shiyanlou 4.0K 2月 21 11:29 query
drwxr-xr-x 2 shiyanlou shiyanlou 4.0K 2月 21 11:09 shiyanlou
drwxr-xr-x 3 shiyanlou shiyanlou 4.0K 2月 21 11:12 test
shiyanlou:Code/ $
检查备份结果,就要将新增的 query 目录导入到数据库中,指定新增数据库为 q1 :
$ mongorestore -h localhost:27017 -d q1 query
在 MongoDB 客户端中查看操作结果:
> show dbs
admin 0.000GB
challenge 0.000GB
config 0.000GB
learn 0.000GB
local 0.000GB
q1 0.000GB
query 0.000GB
s1 0.000GB
shiyanlou 0.000GB
> use q1
switched to db q1
> show collections
stu
> db.stu.find()
{ "_id" : ObjectId("5e4df26740faabaac0fd3de6"), "name" : "大红", "gender" : "男", "语文" : 83, "数学" : 81 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3de7"), "name" : "大黄", "gender" : "女", "语文" : 83, "数学" : 20 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3de8"), "name" : "大蓝", "gender" : "男", "语文" : 84, "数学" : 99 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3de9"), "name" : "大绿", "gender" : "女", "语文" : 85, "数学" : 34 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3dea"), "name" : "大橙", "gender" : "男", "语文" : 86, "数学" : 88 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3deb"), "name" : "大青", "gender" : "女", "语文" : 87, "数学" : 93 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3dec"), "name" : "大紫", "gender" : "男", "语文" : 99, "数学" : 20 }
>
果然新增的 q1 数据库中只有 stu 集合,且集合中的数据与 query.stu 集合一致。
数据库的备份与恢复操作就是以上这些,总结起来就是 mongodump 与 mongorestore 命令的使用。
集合的备份与恢复
集合的备份与恢复指的是针对一个集合中的文档进行备份,备份数据的文件格式有两种:JSON 和 CSV 。其中默认的备份格式为 JSON 。
接下来我们使用 query 数据库的 stu 集合分别介绍两种不同的文件格式的备份与恢复方法。
该集合中的内容如下:
> use query
switched to db query
> db.stu.find()
{ "_id" : ObjectId("5e4df26740faabaac0fd3de6"), "name" : "大红", "gender" : "男", "语文" : 83, "数学" : 81 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3de7"), "name" : "大黄", "gender" : "女", "语文" : 83, "数学" : 20 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3de8"), "name" : "大蓝", "gender" : "男", "语文" : 84, "数学" : 99 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3de9"), "name" : "大绿", "gender" : "女", "语文" : 85, "数学" : 34 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3dea"), "name" : "大橙", "gender" : "男", "语文" : 86, "数学" : 88 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3deb"), "name" : "大青", "gender" : "女", "语文" : 87, "数学" : 93 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3dec"), "name" : "大紫", "gender" : "男", "语文" : 99, "数学" : 20 }
>
使用 JSON 文件备份与恢复
导出数据
备份集合的操作命令是 mongoexport ,格式如下:
$ mongoexport -h host -d dbname -c collection --type json/csv -o file
-h 也可以写作 --host ,为主机名和端口号,默认为 localhost:27017 或者 127.0.0.1:27017
-d 也可以写作 --db ,指定数据库名字
-c 也可以写作 --collection ,指定集合名字
--type 指定导出文件的格式,有 json 和 csv 两种选择,默认为 json 格式
-o 也可以写作 --out ,指定备份目录
现在将 query 数据库的 stu 集合导出到 stu.json 文件,格式为 json ,默认可以省略 --type 选项。操作如下:
shiyanlou:Code/ $ mongoexport -h localhost:27017 -d query -c stu -o stu.json
2020-02-21T11:47:27.436+0800 connected to: localhost:27017
2020-02-21T11:47:27.441+0800 exported 7 records
shiyanlou:Code/ $ ll
总用量 16K
drwxr-xr-x 2 shiyanlou shiyanlou 4.0K 2月 21 11:29 query
drwxr-xr-x 2 shiyanlou shiyanlou 4.0K 2月 21 11:09 shiyanlou
-rw-r--r-- 1 shiyanlou shiyanlou 721 2月 21 11:47 stu.json
drwxr-xr-x 3 shiyanlou shiyanlou 4.0K 2月 21 11:12 test
shiyanlou:Code/ $ cat stu.json
{"_id":{"$oid":"5e4df26740faabaac0fd3de6"},"name":"大红","gender":"男","语文":83.0,"数学":81.0}
{"_id":{"$oid":"5e4df26740faabaac0fd3de7"},"name":"大黄","gender":"女","语文":83.0,"数学":20.0}
{"_id":{"$oid":"5e4df26740faabaac0fd3de8"},"name":"大蓝","gender":"男","语文":84.0,"数学":99.0}
{"_id":{"$oid":"5e4df26740faabaac0fd3de9"},"name":"大绿","gender":"女","语文":85.0,"数学":34.0}
{"_id":{"$oid":"5e4df26740faabaac0fd3dea"},"name":"大橙","gender":"男","语文":86.0,"数学":88.0}
{"_id":{"$oid":"5e4df26740faabaac0fd3deb"},"name":"大青","gender":"女","语文":87.0,"数学":93.0}
{"_id":{"$oid":"5e4df26740faabaac0fd3dec"},"name":"大紫","gender":"男","语文":99.0,"数学":20.0}
shiyanlou:Code/ $
操作截图:
导入数据
将文件中的数据导入到集合中使用 mongoimport 命令,格式如下:
$ mongoimport -h host -d dbname -c collection --file file
-h 也可以写作 --host ,为主机名和端口号,默认为 localhost:27017 或者 127.0.0.1:27017
-d 也可以写作 --db ,指定数据库名字
-c 也可以写作 --collection ,指定集合名字
--file 指定导入的文件
现在将当前目录下的 stu.json 文件导入到 query 数据库的 stu1 集合中:
$ mongoimport -h localhost:27017 -d query -c stu1 --file stu.json
切换到 MongoDB 数据库中查看结果:
> use query
switched to db query
> show collections
stu
stu1
type
> db.stu1.find()
{ "_id" : ObjectId("5e4df26740faabaac0fd3de6"), "name" : "大红", "gender" : "男", "语文" : 83, "数学" : 81 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3de7"), "name" : "大黄", "gender" : "女", "语文" : 83, "数学" : 20 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3de8"), "name" : "大蓝", "gender" : "男", "语文" : 84, "数学" : 99 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3de9"), "name" : "大绿", "gender" : "女", "语文" : 85, "数学" : 34 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3dea"), "name" : "大橙", "gender" : "男", "语文" : 86, "数学" : 88 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3deb"), "name" : "大青", "gender" : "女", "语文" : 87, "数学" : 93 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3dec"), "name" : "大紫", "gender" : "男", "语文" : 99, "数学" : 20 }
>
可以看到 query 数据库中新增了 stu1 集合,且集合中的数据与 query.stu 集合一致。
使用 CSV 文件备份与恢复
导出数据
与 JSON 文件不同的是,CSV 文件存在标题行。也就是在备份的时候,自动将文档的字段写入到文件的第一行。备份 CSV 文件需要使用 --type 选项指定备份格式。
现在将 query 数据库的 stu 集合备份到 stu.csv 文件中:
$ mongoexport -h localhost:27017 -d query -c stu1 \
> --type csv -f _id,name,gender,语文,数学 -o stu.csv
操作截图如下:
需要注意的是,如果选择 --type csv 格式,必须在后面添加 -f 选项,选项后面是导出的文档字段名,多个字段名用逗号隔开,逗号后面不允许有空格。
导入数据
导入数据也是使用 mongoimport 命令,与 JSON 格式的命令差不多,唯一的区别就是需要指定文件类型。此外还需要注意的是 CSV 文件的标题行,如果文件的第一行为标题行,需要增加 --headline 选项将其忽略。
现在将 stu.csv 文件导入到 query 数据库的 stu2 集合中:
$ mongoimport -h localhost:27017 -d query -c stu2 \
> --type csv --headerline --file stu.csv
注意此处因为使用了 --headerline 选项,MongoDB 会剪切文件的第一行,并从中读取字段名,在创建新的 stu2 集合时,会自动将数据按照顺序写入到相应的字段中。
在 MongoDB 客户端中查看操作结果:
> show collections
stu
stu1
stu2
type
> db.stu2.find()
{ "_id" : "ObjectId(5e4df26740faabaac0fd3de6)", "name" : "大红", "gender" : "男", "语文" : 83, "数学" : 81 }
{ "_id" : "ObjectId(5e4df26740faabaac0fd3de7)", "name" : "大黄", "gender" : "女", "语文" : 83, "数学" : 20 }
{ "_id" : "ObjectId(5e4df26740faabaac0fd3de8)", "name" : "大蓝", "gender" : "男", "语文" : 84, "数学" : 99 }
{ "_id" : "ObjectId(5e4df26740faabaac0fd3de9)", "name" : "大绿", "gender" : "女", "语文" : 85, "数学" : 34 }
{ "_id" : "ObjectId(5e4df26740faabaac0fd3dea)", "name" : "大橙", "gender" : "男", "语文" : 86, "数学" : 88 }
{ "_id" : "ObjectId(5e4df26740faabaac0fd3deb)", "name" : "大青", "gender" : "女", "语文" : 87, "数学" : 93 }
{ "_id" : "ObjectId(5e4df26740faabaac0fd3dec)", "name" : "大紫", "gender" : "男", "语文" : 99, "数学" : 20 }
>
没有标题行的 CSV 文件
终端执行如下命令复制 stu.csv 文件中的内容到 stu1.csv 文件,并删掉第 1 行:
$ cat stu.csv | sed '1d' > stu1.csv
操作截图如下:
现在要将 stu1.csv 导入到 query 数据库的 stu3 集合中,因为没有标题行,所以在 --type 后面需要增加 -f 选项提供字段值:
$ mongoimport -h localhost:27017 -d query -c stu3 \
> --type csv -f _id,name,gender,语文,数学 --file stu1.csv
在 MongoDB 客户端中查看操作结果:
> show collections
stu
stu1
stu2
stu3
type
> db.stu3.find()
{ "_id" : "ObjectId(5e4df26740faabaac0fd3de6)", "name" : "大红", "gender" : "男", "语文" : 83, "数学" : 81 }
{ "_id" : "ObjectId(5e4df26740faabaac0fd3de7)", "name" : "大黄", "gender" : "女", "语文" : 83, "数学" : 20 }
{ "_id" : "ObjectId(5e4df26740faabaac0fd3de8)", "name" : "大蓝", "gender" : "男", "语文" : 84, "数学" : 99 }
{ "_id" : "ObjectId(5e4df26740faabaac0fd3de9)", "name" : "大绿", "gender" : "女", "语文" : 85, "数学" : 34 }
{ "_id" : "ObjectId(5e4df26740faabaac0fd3dea)", "name" : "大橙", "gender" : "男", "语文" : 86, "数学" : 88 }
{ "_id" : "ObjectId(5e4df26740faabaac0fd3deb)", "name" : "大青", "gender" : "女", "语文" : 87, "数学" : 93 }
{ "_id" : "ObjectId(5e4df26740faabaac0fd3dec)", "name" : "大紫", "gender" : "男", "语文" : 99, "数学" : 20 }
>
挑战:数据查询与排序
本次挑战需要大家下载 JSON 文件,将其导入到数据库中,并从数据库中读取数据进行排序处理。
首先打开挑战环境,在终端启动 MongoDB 服务:
$ cd ~/Code
$ sudo service mongodb start
执行如下命令下载 JSON 文件到 Code 目录下:
$ wget https://labfile.oss.aliyuncs.com/courses/1364/users.json
这是一些用户完成挑战的数据。文件中共有 20 条数据,每条数据有 4 个字段,分别是:
user_id:用户 ID
challenge_id:挑战的 ID
score:用户完成本次挑战得到的分数
submit_time:用户完成本次挑战花费的时间
挑战要求
1、将 users.json 文件中的数据导入到 shiyanlou 数据库的 users 集合中。
2、在当前目录下创建 sort.py 文件,写入以下代码:
import sys
from pymongo import MongoClient
client = MongoClient(host='localhost', port=27017)
db = client.shiyanlou
def get_rank(user_id):
# TODO
if __name__ == '__main__':
user_id = int(sys.argv[-1])
print(get_rank(user_id))
3、完善 sort.py 文件中的 get_rank 函数,让函数可以正确返回 user_id 对应的用户排名、总分数和总时间。
注意事项
Python3 、MongoDB 、PyMongo 均无需下载,挑战环境中已经安装了它们
每个用户有多条记录,所得的分数和花费的时间为多条记录的总和
排名规则首先按分数排名,如果分数相同则花费的总时间越少则排名越高
完成后,终端执行程序的操作如下:
参考答案
import sys
from pymongo import MongoClient
from bson.son import SON
def get_rank(user_id):
client = MongoClient('localhost', 27017)
users = client.shiyanlou.users
s = users.aggregate([
{'$group': {
'_id': '$user_id',
'sum_score': {'$sum': '$score'},
'sum_minutes': {'$sum': '$submit_time'}
}},
{'$sort':
SON([
('sum_score', -1),
('sum_minutes', 1)
])
}
])
for i, j in enumerate(s, 1):
if j['_id'] == user_id:
return i, j['sum_score'], j['sum_minutes']
if __name__ == '__main__':
user_id = sys.argv[1]
print(get_rank(int(user_id)))