MongoDB非关系型数据库的使用
前言
传统的关系型数据库(如MySQL
),在数据操作的“三高”需求以及应对Web2.0
的网站需求面前,显得力不从心。
解释:“三高”需求:
• High performance
- 对数据库高并发读写的需求。
• Huge Storage
- 对海量数据的高效率存储和访问的需求。
• High Scalability && High Availability-
对数据库的高可扩展性和高可用性的需求。
而MongoDB
可应对“三高”需求。
具体的应用场景如:
1)社交场景,使用 MongoDB
存储存储用户信息,以及用户发表的朋友圈信息,通过地理位置索引实现附近的人、地点等功能。
2)游戏场景,使用 MongoDB
存储游戏用户信息,用户的装备、积分等直接以内嵌文档的形式存储,方便查询、高效率存储和访问。
3)物流场景,使用MongoDB
存储订单信息,订单状态在运送过程中会不断更新,以 MongoDB
内嵌数组的形式来存储,一次查询就能将订单所有的变更读取出来。
4)物联网场景,使用 MongoDB
存储所有接入的智能设备信息,以及设备汇报的日志信息,并对这些信息进行多维度的分析。
5)视频直播,使用 MongoDB
存储用户信息、点赞互动信息等。
这些应用场景中,数据操作方面的共同特点是:
(1)数据量大
(2)写入操作频繁(读写都很频繁)
(3)价值较低的数据,对事务性要求不高
对于这样的数据,我们更适合使用MongoDB
来实现数据的存储。
什么时候选择MongoDB
在架构选型上,除了上述的三个特点外,如果你还犹豫是否要选择它?可以考虑以下的一些问题:
应用不需要事务及复杂
join
支持新应用,需求会变,数据模型无法确定,想快速迭代开发
应用需要
2000-3000
以上的读写QPS
(更高也可以)应用需要
TB
甚至PB
级别数据存储应用发展迅速,需要能快速水平扩展
应用要求存储的数据不丢失
- 应用需要99.999%高可用
- 应用需要大量的地理位置查询、文本查询
如果上述有1个符合,可以考虑 MongoDB
,2个及以上的符合,选择 MongoDB
绝不会后悔。
思考:如果用MySQL
呢?
答:相对MySQL
,可以以更低的成本解决问题(包括学习、开发、运维等成本)
简介
MongoDB
是一个开源、高性能、无模式的文档型数据库,当初的设计就是用于简化开发和方便扩展,是NoSQL
数据库产品中的一种。是最像关系型数据库(MySQL
)的非关系型数据库。
它支持的数据结构非常松散,是一种类似于 JSON
的 格式叫BSON
,所以它既可以存储比较复杂的数据类型,又相当的灵活。
MongoDB
中的记录是一个文档,它是一个由字段和值对(field:value
)组成的数据结构。MongoDB
文档类似于JSON
对象,即一个文档认为就是一个对象。字段的数据类型是字符型,它的值除了使用基本的一些类型外,还可以包括其他文档、普通数组和文档数组。
体系结构
MySQL
和MongoDB
对比
SQL 术语/概念 |
MongoDB 术语/概念 |
解释/说明 |
---|---|---|
database |
database |
数据库 |
table |
collection |
数据库表/集合 |
row |
document |
数据记录行/文档 |
column |
field |
数据字段/域 |
index |
index |
索引 |
table joins |
表连接,MongoDB 不支持 |
|
嵌入文档 | MongoDB 通过嵌入式文档来替代多表连接 |
|
primary key |
主键,MongoDB 自动将_id字段设置为主键 |
MongoDB
特点
MongoDB
主要有如下特点:
(1)高性能:
MongoDB
提供高性能的数据持久性。特别是对嵌入式数据模型的支持减少了数据库系统上的I/O
活动。
索引支持更快的查询,并且可以包含来自嵌入式文档和数组的键。(文本索引解决搜索的需求、TTL索引解决历史数据自动过期的需求、地理位置索引可用于构建各种O2O
应用)
mmapv1
、wiredtiger
、mongorocks(rocksdb)
、in-memory
等多引擎支持满足各种场景需求。
Gridfs
解决文件存储的需求。
(2)高可用性:
MongoDB
的复制工具称为副本集(replica set
),它可提供自动故障转移和数据冗余。
(3)高扩展性:
MongoDB
提供了水平可扩展性作为其核心功能的一部分。
分片将数据分布在一组集群的机器上。(海量数据存储,服务能力水平扩展)
从3.4开始,MongoDB
支持基于片键创建数据区域。在一个平衡的集群中,MongoDB
将一个区域所覆盖的读写只定向到该区域内的那些片。
(4)丰富的查询支持:
MongoDB
支持丰富的查询语言,支持读和写操作(CRUD
),比如数据聚合、文本搜索和地理空间查询等。
(5)其他特点:如无模式(动态模式)、灵活的文档模型
部署安装
打开安装完毕后,在目录...\MongoDB\Server\5.0
下创建配置文件mongo.conf
1 | 数据库路径 |
打开cmd
输入mongo --version
返回版本信息说明安装成功
1. 数据库操作(db)
1.1 选择和创建数据库
选择和创建数据库的语法格式:
1 | use 数据库名称 |
如果数据库不存在则自动创建,例如,以下语句创建 spitdb
数据库:
1 | use spitdb |
查看有权限查看的所有的数据库命令
1 | show dbs |
注意 : 在
MongoDB
中,集合只有在内容插入后才会创建! 就是说,创建集合(数据表)后要再插入一个文档(记录),集合才会真正创建。
查看当前正在使用的数据库命令
1 | db |
MongoDB
中默认的数据库为 test
,如果你没有选择数据库,集合将存放在 test
数据库中。
有一些数据库名是保留的,可以直接访问这些有特殊作用的数据库。
admin
: 从权限的角度来看,这是”root”数据库。要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器。local
: 这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合config
: 当Mongo
用于分片设置时,config
数据库在内部使用,用于保存分片的相关信息。
1.2 数据库的删除
MongoDB
删除数据库的语法格式如下:
1 | db.dropDatabase() |
提示:主要用来删除已经持久化的数据库
2 集合操作(Collection)
集合,类似关系型数据库中的表。
可以显示的创建,也可以隐式的创建。
2.1 集合的显示创建
基本语法格式:
1 | db.createCollection("name") |
参数说明:
name
: 要创建的集合名称
例如:创建一个名为 mycollection
的普通集合。
1 | db.createCollection("mycollection") |
查看当前库中的表:show tables
命令
1 | show collections |
2.2 集合的隐示创建
当向一个集合中插入一个文档的时候,如果集合不存在,则会自动创建集合。
提示:通常我们使用隐式创建文档即可。
2.3 集合的删除
集合删除语法格式如下:
1 | db.collection.drop() |
返回值
如果成功删除选定集合,则 drop()
方法返回 true
,否则返回false
。
例如:要删除mycollection
集合
1 | db.mycollection.drop() |
3 文档基本的增删改查(Document)
文档(document
)的数据结构和JSON
基本一样。
所有存储在集合中的数据都是BSON
格式。
3.1 文档的插入
3.1.1 单个文档的插入
要向comment
的集合(表)中插入一条测试数据:
1 | db.comment.insert({"articleid":"100000","content":"今天天气真好,阳光明媚","userid":"1001","nickname":"Rose","createdatetime":new Date(),"likenum":NumberInt(10),"state":null}) |
提示:
1)comment
集合如果不存在,则会隐式创建
2)mongo
中的数字,默认情况下是double类型,如果要存整型,必须使用函数NumberInt
(整型数字),否则取出来就有问题了。
3)插入当前日期使用new Date()
4)插入的数据没有指定 _id
,会自动生成主键值
5)如果某字段没值,可以赋值为null
,或不写该字段。
执行后,如下,说明插入一个数据成功了。
1 | WriteResult({ "nInserted" : 1 }) |
3.1.2 批量插入
批量插入多条文章评论:
1 | db.comment.insertMany([ |
因为批量插入由于数据较多容易出现失败,因此,可以使用try catch进行异常捕捉处理,测试的时候可以不处理。如(了解):
1 | try { |
3.2 文档的基本查询
3.2.1 查询所有
如果我们要查询comment
集合的所有文档,我们输入以下命令
1 | db.comment.find() |
按一定条件来查询,比如查询userid
为1003
的记录
1 | db.comment.find({userid:'1003'}) |
只返回符合条件的第一条数据
1 | db.comment.findOne({userid:'1003'}) |
3.2.2 投影查询
如果要查询结果返回部分字段,则需要使用投影查询(不显示所有字段,只显示指定的字段)。
如:查询结果只显示 _id
、userid
、nickname
:
1 | >db.comment.find({userid:"1003"},{userid:1,nickname:1}) |
默认 _id
会显示。
如:查询结果只显示 、 userid
、nickname
,不显示_id
:
1 | >db.comment.find({userid:"1003"},{userid:1,nickname:1,_id:0}) |
再例如:查询所有数据,但只显示_id
、userid
、nickname
:
1 | >db.comment.find({},{userid:1,nickname:1}) |
3.3 文档的更新
3.3.1 覆盖的修改
如果我们想修改_id
为1的记录,点赞量为1001
,输入以下语句:
1 | db.comment.update({_id:"1"},{likenum:NumberInt(1001)}) |
执行后,我们会发现,这条文档除了 likenum
字段其它字段都不见了,
3.3.2 局部修改
为了解决这个问题,我们需要使用修改器$set
来实现,命令如下:
我们想修改_id
为2的记录,浏览量为889
,输入以下语句:
1 | db.comment.update({_id:"2"},{$set:{likenum:NumberInt(889)}}) |
3.3.3 批量修改
更新所有用户为1003
的用户的昵称为 凯撒大帝 。
1 | //默认只修改第一条数据 |
提示:如果不加后面的参数,则只更新符合条件的第一条记录
3.3.4 列值增长的修改
如果我们想实现对某列值在原有值的基础上进行增加或减少,可以使用 $inc
运算符来实现。
需求:对3号数据的点赞数,每次递增1
1 | db.comment.update({_id:"3"},{$inc:{likenum:NumberInt(1)}}) |
3.4 文档的删除
删除文档的语法结构:
1 | db.集合名称.remove(条件) |
以下语句可以将数据全部删除,请慎用
1 | db.comment.remove({}) |
如果删除_id=1
的记录,输入以下语句
1 | db.comment.remove({_id:"1"}) |
4 文档的分页查询
4.1 统计查询
统计查询使用count()
方法,语法如下:
1 | db.collection.count(query, options) |
4.1.1 统计所有记录数
统计comment
集合的所有的记录数:
1 | db.comment.count() |
4.1.2 按条件统计记录数
例如:统计userid
为1003
的记录条数
1 | db.comment.count({userid:"1003"}) |
提示:
默认情况下 count()
方法返回符合条件的全部记录条数。
4.2 分页列表查询
可以使用limit()
方法来读取指定数量的数据,使用skip()
方法来跳过指定数量的数据。
基本语法如下所示:
1 | db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER) |
如果你想返回指定条数的记录,可以在find
方法后调用limit
来返回结果(TopN
),默认值20
。
1 | db.comment.find().limit(3) |
skip
方法同样接受一个数字参数作为跳过的记录条数。(前N
个不要),默认值是0
1 | db.comment.find().skip(3) |
分页查询:需求:每页2个,第二页开始:跳过前两条数据,接着值显示3和4条数据
1 | //第一页 |
4.3 排序查询
sort()
方法对数据进行排序,sort()
方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而 -1 是用于降序排列。
语法如下所示:
1 | db.COLLECTION_NAME.find().sort({KEY:1}) |
例如:
对userid
降序排列,并对访问量进行升序排列
1 | db.comment.find().sort({userid:-1,likenum:1}) |
提示:
skip(), limilt(), sort()
三个放在一起执行的时候,执行的顺序是先sort()
, 然后是 skip()
,最后是显示的limit()
,和命令编写顺序无关。
5 文档的更多查询
5.1 正则的复杂条件查询
MongoDB
的模糊查询是通过正则表达式的方式实现的。格式为:
1 | db.collection.find({field:/正则表达式/}) |
提示:正则表达式是js
的语法,直接量的写法。
例如,我要查询评论内容包含“开水”的所有文档,代码如下:
1 | db.comment.find({content:/开水/}) |
如果要查询评论的内容中以 “专家”开头的,代码如下:
1 | db.comment.find({content:/^专家/}) |
5.2 比较查询
<, <=, >, >=
这个操作符也是很常用的,格式如下:
1 | db.集合名称.find({ "field" : { $gt: value }}) // 大于: field > value |
示例:查询评论点赞数量大于 700的记录
1 | db.comment.find({likenum:{$gt:NumberInt(700)}}) |
5.3 包含查询
包含使用$in
操作符。 示例:查询评论的集合中userid
字段包含1003或1004的文档
1 | db.comment.find({userid:{$in:["1003","1004"]}}) |
不包含使用 $nin
操作符。 示例:查询评论集合中userid
字段不包含1003和1004的文档
1 | db.comment.find({userid:{$nin:["1003","1004"]}}) |
5.4 条件连接查询
我们如果需要查询同时满足两个以上条件,需要使用$and
操作符将条件进行关联。(相当于SQL
的and
) 格式为:
1 | $and:[ { },{ },{ } ] |
示例:查询评论集合中 likenum
大于等于700 并且小于2000的文档:
1 | db.comment.find({$and:[{likenum:{$gte:NumberInt(700)}},{likenum:{$lt:NumberInt(2000)}}]}) |
如果两个以上条件之间是或者的关系,我们使用 操作符进行关联,与前面 and
的使用方式相同格式为:
1 | $or:[ { },{ },{ } ] |
示例:查询评论集合中 userid
为1003,或者点赞数小于1000的文档记录
1 | db.comment.find({$or:[ {userid:"1003"} ,{likenum:{$lt:1000} }]}) |
6 常用命令小结
1 | 选择切换数据库:use articledb |
7. Python操作MongoDB
数据库
7.1 下载pymongo
1 | pip install pymongo |
7.2 连接MongoDB
数据库
1 | from pymongo import MongoClient |
7.3 操作数据库
7.3.1 插入数据
1 | # ###################插入数据################ |
7.3.2 查询数据
1 | # ###############查询数据################# |
7.3.3 更新数据
1 | # ###############更新数据################# |
7.3.4 删除数据
1 | # ##############删除数据################# |
7.3.5 条件操作符
1 | # ##################条件操作符##################### |
7.3.6 数组操作
1 | # ################# 给数组增加值 ########### |
7.3.7 多级目录
1 | # ############# 多级目录 ################ |