在阅读本篇文章之前,你已经阅读了:

本篇视频

本篇学习内容

  • 学习使用MongoDB

 首先,我们要搞明白,为什么使用数据库?
 N个传感器时时刻刻都在上传数据,我们需要把数据存到硬盘里,等需要用的时候再取出来。我们可以选择创建一个文件,不断地把数据存进去,用的时候再读取出来。假设我们写数据写进文件长成这个样子的:

1
2
3
4
5
6
# 时间 - 设备名 - 数据名 - 数据值
[13/May/2018:06:26:31 +0800] - 深圳XX大厦XX楼ABC主板 - A传感器 - 24.5
[13/May/2018:06:28:38 +0800] - 广州XX学校XX楼EFG主板 - A传感器 - 26.5
[13/May/2018:06:30:44 +0800] - 深圳XX大厦XX楼ABC主板 - A传感器 - 24.5
[13/May/2018:06:31:56 +0800] - 广州XX学校XX楼EFG主板 - B传感器 - 24.5
# ...后面省略一百万条

 这个文件里有一百万条数据,当你想从中想显示A传感器在X日时的数据时,嗯,你就需要把这一百万条数据都读取出来,进行判断是A设备吗?那一天是X日吗?这样做的效率太差了,每次查询操作估计要卡住很长时间。当然你可以说,为了提高查询效率,可以把不同设备分别单独放一个文件呀,不同日期单独分开放一个文件呀,如此等等。
 数据库为了提高查询效率,存诸数据时会按预先设定的规则存放,查询时会十分高效。(上面讨论的那种直接写入文件,写入速度比写入数据库要快,所需空间更少,所以一般会用来写日志,记录操作历史与故障报告等等。)

安装MongoDB

MongoDB官网去下载,注意使用社区版本(community)的稳定版本即可。(不要选企业版,要收钱的。社区版本就是免费的。)本项目使用的是V4版本的。windows的安装在demo2跑起来演示过了,在linux安装也在部署到云服务器 演示过了,就不再演示。
mongodb-windows安装
 4.0以上版本的windows版本MongoDB会自动注册为windows服务,大家可自行启用或关闭服务。(注册为windows服务就意味着可以开机自动启动,可在任务管理栏里的服务查到。)
windows服务列表

linux安装一般直接使用命令行安装,具体看官方文档
mongodb-linux安装官方文档

使用可视化界面客户端连接MongoDB

 可视化界面客户端有官方的MongoDBCompass。由于数据库安装在本地,所以直接点连接即可,它会默认连接本地的数据库。

使用命令行操作MongoDB

 语法直接是JavaScript,这里简单演示一下如何使用命令来看数据,更多操作命令请看MongoDB 教程 | 菜鸟教程或其它教程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#进入mongo
mongo

# 显示数据库列表
show dbs

# 进入数据库demo2
use demo2

#显示集合
show tables

#显示文档数据
db['equipment-data'].find()

在Nodejs上操作MongoDB

 我们使用node-mongodb-native模块来操作数据库。具体API可以看它的文档,这里就不单独演示基础操作,基本语法与命令行操作差不多。
 在demo2\myapp\bin\mongodb.js里写了如何连接数据库,连接到哪个表等等:与SQL数据库不同,SQL数据库是需要先建立库、表结构才能使用。而在MongoDb,因为没有固定的表结构,所以只要插入数据,就会自动创建对应的库名、文档。

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
const MongoClient = require('mongodb').MongoClient;

// Connection URL
const url = 'mongodb://localhost:27017';
// Database Name
const dbName = 'demo2';

let mongodb ={
dbClient:null,
db:null,
insert:null,
find:null
}


// 插入数据
// data是一个对象
mongodb.insert = function(data,callback) {
if(mongodb.dbClient && mongodb.dbClient.isConnected()){
// Get the documents collection
const collection = mongodb.db.collection('equipment-data');
// Insert some documents

//添加插入时间
data.createdAt= new Date()
collection.insertOne(data, function(err, result) {
if(err){
callback(err)
}
callback(null,result);
});

}
else{
callback('mongodb is not connected!')
}
}

// 查找数据
const findOptions={
limit:10,//返回最多10条数据
sort:{createdAt:-1}//返回最晚生成的数据
}
mongodb.find=function (data,callback) {
if(mongodb.dbClient && mongodb.dbClient.isConnected()){
// Get the documents collection
const collection = mongodb.db.collection('equipment-data');

collection.find(data,findOptions).toArray(function(err, docs) {
if(err){
callback(err)
}
callback(null,docs);
});
}
else{
callback('mongodb is not connected!')
}

}

// 连接mongodb
MongoClient.connect(url,{useNewUrlParser:true}, function(err, client) {
if(err){
return console.log('mongodb err:',err)
}
console.log("mongodb client connected successfully to server.");

// 连接mongodb中的数据库
mongodb.dbClient = client
mongodb.db = client.db(dbName);

let collection = mongodb.db.collection('equipment-data');
// 检查有无创建TTL(time to live) 索引,用于删除过期的数据。
collection.indexExists("createdAt_1",(err,result)=>{
if(err){
return console.log(err)
}
else if(!result){
console.log("create index: 'createdAt':expireAfterSeconds")
//只保留一个小时内的数据
collection.createIndex( { "createdAt": 1 }, { expireAfterSeconds: 3600 } )
}
})
});


module.exports=mongodb;

demo2\myapp\bin\tcp-server.js里写了如何保存数据到数据库:

1
2
3
4
5
6
//保存所接收到的数据
mongodb.insert({id:socket.id,data:socket.lastValue},function (err) {
if(err){
console.log(socket.id,"保存数据失败:",err)
}
})

demo2\myapp\routes\index.js里写了如何从数据库拿历史数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mongodb.find({id:req.params.id},(err,docs)=>{
if(err){
res.send([])
console.log(err)
}
else{
let result = []
docs.forEach( (doc) => {
result.push({
time:moment(doc.createdAt).format('mm:ss'),
value:doc.data
})
});
result.reverse()

res.send(result)
}

})

聊聊TTL

 数据库里的数据可以设置生存时间索引(TTL:time to live),达到生存时间就会自动删除数据。在我的demo2里是设置了一个小时:

1
2
//只保留一个小时内的数据
collection.createIndex( { "createdAt": 1 }, { expireAfterSeconds: 3600 } )

 在实际商用环境,根据不同的业务需求来决定数据保存时间,有些数据会永远保存,那么就会定时备份,从数据库迁移出去。有些数据只需要保存一定时间,那么超出时间的数据直接删掉就好了。如果一直不删也不迁出备份,那么数据库里的数据会把磁盘空间占满,导致不能再写入新数据。

补充说明

 Nodejs里控制mongodb的模块还有mongoose,但实际使用时会额外学习很多新概念,学习可看文档或自行搜索相关。

FAQ

  1. 数据库到底要选MySQL还是MongoDB?
    答:两者的应用场景不一样,利用搜索引擎搜索两者的应用场景,自行判断自己的业务需要哪种服务。