2020-06-18 更新: 增加数据库的迁移工作

MongoDB GUI

虽然我偏爱命令行, 但是数据库中, 使用GUI增删改查真的很方便, 可惜MongoDB没有 像phpMyAdmin这样的好工具, 我在试用了许多工具之后, 最后选择了Robo3t.

增删改查部分

最基础的增删改查一定是工作中用的最多的, 虽然工作中用的是PyMongo, 但是这里我只列出Mongo shell中的动作, 与PyMongo的动作基本是类似的.

1
2
3
4
5
6
7
8
9
10
11
12
// 增
db.new_doc.insert({'x': 123, 'y': 456});

// 删
db.new_doc.deleteOne({'x': 123});

// 改
db.new_doc.insert({'x': 123, 'y': 456});
db.new_doc.update({'x': 123}, {'x': 234, 'y':456, 'z': 789});

// 查
db.new_doc.find({'x': 234});

索引的建立

  • 查看已经建立好的索引: db.xxx.getIndexKeys()或是db.xxx.getIndexes()

MongoDB也是使用B+树做为索引的, 所以加入索引后, 给定区间的查找 可以获得不错的效率, 多点随机查询会有一点慢.

详细官方文档可以查看这里: MonoDB Indexes.

我挑几个对我来说属于新知识的来讲, 但请大家一定以官方文档为准, 如果有任何错误, 请一定指出, 我会及时更新:

  1. MongoDB在建立表时, 会自动为_id字段增加索引.
  2. 对于单个字段的索引, 如何排序并不重要, 无论正序还是逆序, MongoDB查询时都会使用; 对于多个字段的索引, 将会按照建立索引时字段的顺序 以及各个字段的指定顺序进行组织, 假如查询的顺序与索引的组织顺序不同, 索引 会失效.
  3. 分区索引(3.2版本以上), 符合某些条件的条目才会被索引记录.
  4. 稀疏索引: 存在某个字段的条目才会被索引记录, 官方推荐这个功能最好使用分区索引实现
  5. TTL索引, 索引字段只能是Date类型或是Date数据组成的数组, 只要有一个Date数据 达到了过期时间, 该条记录就会被删除.

Explain的使用

MySQL中, explain绝对是优化查询的好帮手, 最主要的就是查看内层的搜索是否使 用了索引, 这是官方explain的使用, 比较详细的explain使用

目前来说, 我只看winningPlan中的stage, 为IXSCAN的话基本就放心了, 并不代表其他字段不重要, 如果未来遇到, 会更新此博客.

Javascript与Mongo shell

Mongo shell中是可以使用Javascript代码的, 但是Mongo shell真的太难用了, 建议大家在Robo3t中加一个文件来运行. 下面是一套简单的代码, 使用Javascript 模拟了一次join操作(跑跑看, 应该是容易理解的, 旨在说明Javascript代码可以使用):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
db.example.insert({'name': 'a', 'code': 1})
db.example.insert({'name': 'b', 'code': 2})
db.example.insert({'name': 'c', 'code': 3})
db.example.insert({'name': 'd', 'code': 1})
db.expjoin.insert({'code': 1, 'content': 'This is for code 1'})
db.expjoin.insert({'code': 2, 'content': 'This is for code 2'})

// 查找某些name
var expArr = db.example.find(
{
'name': {$in: ['a', 'b']}
}
).map((item) => {
return {'id': item._id, 'name': item.name, 'code': item.code};
}
);

// 针对每个元素进行查找
expArr.forEach((e) => {
e.details = db.expjoin.find({'code': e.code}).toArray();
});
print(expArr);

// 函数定义:
var myFunction = function () {
var x = db.example.count();
print(x);
}
myFunction();

Aggregate的一些用法

MongoDB中许多操作单条语句是无法做到的, aggregate就适合处理简单的计算并返回结果.

Aggregation官方文档

仿Join($lookup)

$lookup操作是MongoDB在3.2以后的版本中加入的Lookup官方文档, 类似于MySQL中的join操作.

lookup要与aggregate语句配合使用Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 与上面的js代码可以达到一致的效果
db.example.aggregate([
{
$match: {'name': {$in: ['a', 'b']}}
},
{
$lookup: {
"from": "expjoin",
"localField": "code",
"foreignField": "code",
"as": "details"
}
}
])

仿Group by

官方文档中作了详细的解释, groupby的也是Pipeline的第一个例子.

我遇到的版本相关问题(不定期更新)

** 查看数据库版本: db.version(); **

aggregate与Mongo版本问题

不同版本的MongoDB shellMongoDB server会提出warning, aggregate语句 也有可能会执行错误

1
2
3
4
MongoDB shell version v4.0.2
connecting to: mongodb://127.0.0.1:27000/test
MongoDB server version: 2.4.8
WARNING: shell and server versions do not match

我在执行中也遇到了下面的错误, 如果你也遇到了这样的TypeError. 你该考虑用服务器 上原始版本的MongoDB shell了.

1
2
3
4
5
> db.product.aggregate([{$group: {_id: "$p_id",count: { $sum: 1 }},},{$sort: {count: 1,},} ])
2018-10-09T20:43:24.460+0800 E QUERY [js] TypeError: invalid assignment to const `res' :
DB.prototype._runAggregate@src/mongo/shell/db.js:250:19
DBCollection.prototype.aggregate@src/mongo/shell/collection.js:1056:12
@(shell):1:1

$ne, $eq 版本问题

$eq在3.0之前的版本中是没有的, 想要实现{<field>: { $eq: <value> }} 这样的 效果, 就只能{"field": {'$not': {'$ne': <value>}}}. 不过讲真, 这样用还是 有一点机械.

$lookup 版本问题

上面已经说过了, lookup是在3.2之后才支持, 处于3.2之前的版本, 只能自己手动的join!!!

explain 版本问题

3.0之后:

可以使用db.comment.explain().aggregate([...]) 可以使用db.comment.explain().find({...})

2.6 ~ 3.0:

db.comment.aggregate想要查看explain信息, 就要这样用了:

1
2
3
4
5
6
7
db.comment.aggregate([
...
],
{
explain: true
}
)

db.comment.find想要使用explain: 要这样:db.admin.find({...}).explain()

2.6之前:

aggregate不能explain. db.comment.find想要使用explain: 要这样:db.admin.find({...}).explain()

真的, 升级MongoDB是很有必要的, 也许许多人认为站着说话不腰疼. 但是, 尝试一下 平滑过渡数据库应该也算是工作的一部分, 重要的是将迁移流程以及主备做好, 能够即时的恢复.

数据的迁移工作

MongoDB的导入导出工作需要下载一些数据库工具, 选择对应的系统版本下载就可以了,

1
2
3
4
5
# 导出数据至users.json
./mongoexport mongodb://<user>:<pass>@<old_host>:<old_port>/admin --collection users --out users.json

# 导入数据到新的数据库
./mongoimport mongodb://<user>:<pass>@<new_host>:<new_port>/admin --collection users --file users.json

MongoDB的事务

4.0之后才支持, 公司没有用这么新的版本, 也不用MongoDB的事务, 真的用到了再和大家分享吧.