MongoDB数据库
1. 简介
1.1 关系型数据库与非关系数据库
1. nosql
- “NoSQL”一词最早于1998年被用于一个轻量级的关系数据库的名字
- 随着web2.0的快速发展, NoSQL概念在2009年被提了出来
- NoSQL在2010年发展起来, 现在国内外众多网站, 如、 淘宝、 京东、 百度、google等, 都在使用nosql开发高性能的产品
对于一名程序员来讲, 使用nosql已经成为一条必备技能
NoSQL最常用的解释是“non-relational”, “Not Only SQL”也被很多人接受, 指的是非关系型的数据库
2. 优点与缺点
- 关系型数据库
- 优点
- 严谨
- 成熟稳定
- 缺点
- 扩展性差
- 性能相对内存型数据库较差
- 适应场景
- 金融系统
- 财务系统
- 优点
- 非关系型数据库
- 优点
- 易扩展: NoSQL数据库种类繁多, 但是一个共同的特点都是去掉关系数据库的关系型特性。 数据之间有关系, 这样就非常容易扩展
- 大数据量, 高性能: NoSQL数据库都具有非常高的读写性能, 尤其在大数据量下, 同样表现优秀。 这得益于它的非关系性, 数据库的结构简单
灵活的数据模型: NoSQL无需事先为要存储的数据建立字段, 随时可以存储定义的数据格式。 但在关系数据库中, 增删字段是一件非常麻烦的事情。 如果是非常大数据量的表, 增加字段简直就是一个噩梦
- 缺点
- 数据存在丢失的风险
- 优点
3. MongoDB 简介
- MongoDB (名称来自「humongous (巨大无比的)」), 是一个可扩展的高性能,开源,模式*,面向文档的NoSQL,由 C++ 语言编写,设计之初旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。
- MongoDB属于内存型数据, 使用的是内存映射存储引擎,它会把磁盘IO操作转换成内存操作,如果是读操作,内存中的数据起到缓存的作用,如果是写操作,内存还可以把随机的写操作转换成顺序的写操作,大幅度提升性能。
- MongoDB 既拥有Key-Value存储方式的高性能,也拥有传统的RDBMS系统的丰富的功能,集两者的优势于一身。 介于关系数据库和NoSQL之间,也是功能最丰富、最像关系数据库的的NoSQL。
MongoDB中文文档:https://www.mongodb.org.cn/
MongoDB中文社区:http://www.mongoing.com
4. SQL和NoSQL的主要区别
-
SQL中层级关系: 数据库>表>数据
-
NoSQL中层级关系: 数据库>集合>文档
5. mongodb的安装
以ubuntu18.04为例
mongodb具有两种安装方式:命令安装 或 源码安装
- 命令安装:适应于各种有界面的操作系统
- 源码安装:适合公司部署服务器-*面
5.1 命令安装:
Ubuntu
sudo apt-get install -y mongodb-org
或参考官方文档 :https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/
Mac
# 首先tap一个仓库
brew tap mongodb/brew
# 安装社区版
brew install mongodb-community
centos
yum -y install mongodb mongodb-devel mongodb-server
5.2 源码安装
1.选择相应版本和操作系统并下载
https://www.mongodb.com/download-center/community?jmp=docs
2.解压
tar -zxvf mongodb-linux-x86_64-ubuntu1804-4.0.3.tgz
*3.移动到/usr/local/目录下
sudo mv -r mongodb-linux-x86_64-ubuntu1804-4.0.3/ /usr/local/mongodb
4.在shell的初始化脚本.bashrc中添加mongodb可执行文件到环境变量PATH中
a. 进入.bashrc文件中
cd ~
sudo vi .bashrc
b. 在.bashrc文件的最后添加:
export PATH=/usr/local/mongodb/bin:$PATH
5. 适用场景
-
网站数据
: 适合实时的插入,更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性。 -
缓存
: 由于性能很高,也适合作为信息基础设施的缓存层。在系统重启之后,搭建的持久化缓存可以避免下层的数据源过载。 -
大尺寸、低价值的数据
: 使用传统的关系数据库存储一些数据时可能会比较贵,在此之前,很多程序员往往会选择传统的文件进行存储。 -
高伸缩性的场景
: 非常适合由数十或者数百台服务器组成的数据库。 -
用于对象及JSON数据的存储
: MongoDB的BSON数据格式非常适合文档格式化的存储及查询。
6. 不适用的场景
-
高度事物性的系统
: 例如银行或会计系统。传统的关系型数据库目前还是更适用于需要大量原子性复杂事务的应用程序。 -
传统的商业智能应用
: 针对特定问题的BI数据库会对产生高度优化的查询方式。对于此类应用,数据仓库可能是更合适的选择。 - `需要使用SQL语句解决的场景: MongoDB不支持SQL语句。
7. 商业应用
-
京东
: 使用MongoDB存储商品信息,支持比价和关注功能. -
百度云
: 使用MongoDB管理百度云盘中500亿条关于文件源信息的记录. -
CERN
: 著名的粒子物理研究所,欧洲核子研究中心大型强子对撞机的数据使用MongoDB存储。 -
The New York Times
: 世界领先的在线新闻门户网站之一,使用MongoDB作为内容存储。 -
sourceforge.net
: 资源网站查找,创建和发布开源软件免费,使用MongoDB做后端存储。
2. 使用
1. mongodb服务端的启动
-
默认端口:
27017
-
默认配置文件的位置:
/etc/mongod.conf
-
默认日志的位置:
/var/log/mongodb/mongod.log
mongodb服务端启动分别两种方式: -
本地测试方式的启动(只具有本地数据增删改查的功能)
-
生产环境启动(具有完整的全部功能)
1.1 测试方式启动
- 启动:
sudo service mongod start (sudo service mongod start)
- 停止:
sudo service mongod stop
- 重启:
sudo service mongod restart
1.2 生产环境正式的启动方式
启动: sudo mongod [–auth --dbpath=dbpath --logpath=logpath --append --fork] [-–f logfile ]
- 只以 sudo mongod 命令启动时,默认将数据存放在了 /data/db 目录下,需要手动创建
-
--dbpath
: 指定数据库的存放路径 -
--logpath
: 指定日志的存放路径 -
--append
: 或–logappend 设置日志的写入形式为追加模式 -
--fork
: 或-fork 开启新的进程运行mongodb服务 -
--f
: 或-f 配置文件路径(可以将上述配置信息写入文件然后通过该文件中的参数进行加载启动) -
--auth
: 以权限认证的方式启动,我们会在后边的课程中学习该内容
1.3 查看是否启动成功
ps aux | grep mongod
2. 启动mongodb的客户端
- 启动本地客户端:
mongo
- 查看帮助:
mongo –help
- 退出:
exit
或者ctrl+c
3. mongodb的简单使用
开启mongodb server的情况下,在进入mongo shell后,就可以做简单的使用了
3.1 mongodb数据库的命令
- 查看当前的数据库:
db
(没有切换数据库的情况下默认使用test数据库) - 查看所有的数据库:
show dbs /show databases
切换数据库:use db_name-
db_name
为show dbs
后返回的数据库名
-
- 删除当前的数据库:
db.dropDatabase()
3.2 mongodb集合的命令
-
无需手动创建集合: 向不存在的集合中第一次添加数据时,集合会自动被创建出来
-
手动创建集合:
db.createCollection(name,options)
- ·db.createCollection(“stu”)·
db.createCollection("sub", { capped : true, size : 10 } )
- 参数
capped
:默认值为false表示不设置上限,值为true表示设置上限
-
参数
size
:集合所占用的字节数。 当capped值为true时,需要指定此参数,表示上限大小,当文档达到上限时, 会将之前的数据覆盖,单位为字节 -
查看集合:
show collections
-
删除集合:
db.集合名称.drop()
-
检查集合是否设定上限:
db.集合名.isCapped()
3.3 简单练习
在mongo shell中输入下列命令,查看结果
show dbs
use test
show collections
db
db.stu.insert({'name':'郭靖', 'age':22})
show dbs
show collections
db.stu.find()
db.stu.drop()
show collections
db.dropDatabase()
show dbs
exit
3.3 mongodb中常见的数据类型
1. 常见类型
- Object ID: 文档ID/数据的ID,数据的主键
- String: 字符串,最常用,必须是有效的UTF-8
- Boolean: 存储一个布尔值,true或false
- Integer: 整数可以是32位或64位,这取决于服务器
- Double: 浮点数
- Arrays: 数组/列表
- Object: mongodb中的一条数据/文档,即文档嵌套文档
- Null: 存储null值
- Timestamp: 时间戳,表示从1970-1-1到现在的总秒数
- Date: 存储当前日期或时间的UNIX时间格式
2. 注意点
- 每个文档都有一个属性,为
_id
,保证每个文档的唯一性,mongodb默认使用_id作为主键- 可以手动设置
_id
的值,如果没有提供,那么MongoDB为每个文档提供了一个独特的_id
, 类型为objectID
- 可以手动设置
- objectID是一个12字节的十六进制数,每个字节两位,一共是24位的字符串:
- 前4个字节为当前时间戳
- 接下来3个字节的机器ID
- 接下来的2个字节中MongoDB的服务进程id
- 最后3个字节是简单的增量值
3. 增删改查
前提,必须进入数据库
use 数据库名
1. mongodb插入数据
命令:db.集合名称.insert(document)
# 单数据插入
db.stu.insert({name:'curry', gender: man})
db.stu.insert(_id:"20200520", name:"kobe", number:"24")
# 多条数据插入
data = [
{"name": "james"},
{"number": 23},
{"team": "lakers"}
]
db.stu.insertMany(data)
插文档时,如果不指定_id
参数,MongoDB会为文档自动分配一个唯一的ObjectId
2. mongodb的保存
命令:db.集合名称.save(document)
db.stu.save({_id:'20200520', name:'curry', gender:female})
db.stu.find()
如果文档的_id已经存在则修改,如果_id不存在则添加
3. mongodb的查询
命令:db.集合名称.find()
可以使用以下数据进行练习
db.stu.insert([{"name" : "郭靖", "hometown" : "蒙古", "age" : 20, "gender" : true },
{"name" : "黄蓉", "hometown" : "桃花岛", "age" : 18, "gender" : false },
{"name" : "华筝", "hometown" : "蒙古", "age" : 18, "gender" : false },
{"name" : "黄药师", "hometown" : "桃花岛", "age" : 40, "gender" : true },
{"name" : "段誉", "hometown" : "大理", "age" : 16, "gender" : true },
{"name" : "段王爷", "hometown" : "大理", "age" : 45, "gender" : true },
{"name" : "洪七公", "hometown" : "桃花岛", "age" : 50, "gender" : true }])
3.1 简单查询
- 方法find(): 查询
db.集合名称.find({条件文档})
- 方法findOne():查询,只返回第一个
db.集合名称.findOne({条件文档})
- 方法pretty(): 将结果格式化;不能和findOne()一起使用!
db.集合名称.find({条件文档}).pretty()
4.更新数据
db.集合名称.update({query}, {update}, {multi: boolean})
- 参数query:查询条件
- 参数update:更新操作符
- 参数multi:可选,默认是false,表示只更新找到的第一条数据,值为true表示把满足条件的数据全部更新
默认更新是整体更新,局部更新使用 $set,默认更新是一条数据,如果需要批量更新添加第三个参数设置更新方式使用 multi:true
# 覆盖式更新
db.stu.update(
# 更新条件
{
age:18
},
# 更新内容
{
name:"余欢水"
}
)
# 局部更新
db.stu.update(
# 更新条件
{
age:18
},
# 更新内容
{
$set:{
name:"林有有"
}
}
)
# 批量更新
db.stu.update(
# 更新条件
{
hometown:"大理"
},
# 更新内容
{
$set:{
name:"黑马程序员"
}
},
# 设置更新方式
{
multi:true
}
)
注意:"multi update only works with $ operators"
* multi参数必须和$set一起使用!
5. 删除数据
db.stu.remove(
# 删除条件
{
"hometown":"大理"
},
# 删除一条
{
justOne:true
}
)
# 清空数据
db.stu.remove({})
注意:db.stu.remove({}) 删除所有里面括号不能少
5.高级查询
5.1 比较运算符
-
等于: 默认是等于判断, 没有运算符
-
小于:
$lt (less than)
-
小于等于:
$lte (less than equal)
-
大于:
$gt (greater than)
-
大于等于:
$gte
-
不等于:
$ne
# 查询年龄大于18的所有英雄
db.stu.find({age:{$gt:18}})
5.2逻辑运算符
逻辑运算符主要指与、或逻辑
- and:在json中写多个条件即可
# 查询年龄大于或等于18, 并且性别为true的英雄
db.stu.find({age:{$gte:18},gender:true})
- 逻辑或: 使用$or, 值为数组, 数组中每个元素为json
# 查询年龄大于18, 或性别为false的英雄
db.stu.find({$or:[{age:{$gt:18}},{gender:false}]})
5.3 范围运算符
- 使用
$in
,$nin
判断数据是否在某个数组内
# 查询年龄为18、 40的英雄
db.stu.find({age:{$in:[18, 40]}})
5.4 支持正则表达式
- 使用
$regex
编写正则表达式
# 查询name以'黄'开头的英雄
db.stu.find({name:{$regex:'^黄'}})
5.5 skip和limit
- 方法limit(): 用于读取指定数量的文档
db.集合名称.find().limit(NUMBER)
# 查询2条学生信息
db.stu.find().limit(2)
- 方法skip(): 用于跳过指定数量的文档
db.集合名称.find().skip(NUMBER)
# 跳过前两条
db.stu.find().skip(2)
- 同时使用
db.stu.find().skip(2).limit(2)
5.6 投影
在查询到的返回结果中, 只选择必要的字段
命令:db.集合名称.find({},{字段名称:1,...})
参数为字段与值, 值为1表示显示, 值为0不显 特别注意:
- 对于_id列默认是显示的, 如果不显示需要明确设置为0
- 对于其他不显示的字段不能设置为0
# id不显示,只显示name和age字段
db.stu.find({},{_id:0,name:1,age:1})
5.7 排序
方法sort()
, 用于对查询结果按照指定的字段进行排序
命令:db.集合名称.find().sort({字段:1,...})
参数1为升序排列 参数-1为降序排列
# 根据年龄降序
db.stu.find().sort({age:-1})
5.8 统计个数
方法count()
用于统计结果集中文档条数
命令:db.集合名称.find({条件}).count()
命令:db.集合名称.count({条件})
# 查询年龄为18岁的数据一共有多少条
db.stu.find({age:18}).count()
db.stu.count({age:18})
4. 聚合操作
4.1 简介
聚合(aggregate)是基于数据处理的聚合管道,每个文档通过一个由多个阶段(stage)组成的管道,可以对每个阶段的管道进行分组、过滤等功能,然后经过一系列的处理,输出相应的结果。
聚合语法
db.集合名称.aggregate([
{管道名 : {表达式}},
{管道名 : {表达式}},
{管道名 : {表达式}},
...
])
4.2 mongodb的常用管道和表达式
1. 管道命名
-
$group
: 按照某个字段进行分组,依次对于每个组进行统计计算。 -
$match
: 匹配出符合条件的数据,和find
操作一致 -
$project
: 和投影作用一样,可以控制字段不显示 -
$sort
: 将输出文档排序后输出 -
$limit
: 限制聚合管道返回的⽂档数 -
$skip
: 跳过指定数量的文档, 并返回余下的文档
2. 表达式
表达式:处理输出文档并输出 语法:表达式:'$列名'
常用表达式:
-
$sum
: 计算总和, $sum:1 表示以1累加计数相当于count统计数量 -
$avg
: 计算平均值 -
$min
: 获取最小值 -
$max
: 获取最大值 -
$push
:把分组后的内容放入到列表中,关键词$$ROOT
整条数据
3. 管道命令之$group
按照某个字段进行分组,依次对每个组进行统计计算。
_id
表示按照某个字段进行分组,但是内部的字段名称前面必须添加$字符,id
设置为null
表示把所有数据作为一组看待。
使用示例如下
# 统计家乡数量
db.stu.aggregate([
{
$group: {
_id: "$hometown",
"家乡数量统计":{$sum: 1},
}
}
])
# 设置分组字段: _id: "$hometown"
# 编写统计表达式: "家乡数量统计":{$sum: 1},
# $sum 统计总数表达式
# 统计平均年龄
db.stu.aggregate([{
$group: {
_id: null,
"平均年龄": {$avg: "$age"}
}
}])
# 家乡分组的平均年龄
db.stu.aggregate([{
$group: {
_id: "$hometown",
"平均年龄": {$avg: "$age"}
}
}])
# "最大年龄":{$max:"$age"},
# "最小年龄":{$min:"$age"}
# "姓名":{$push:"$name"},
其中注意点:
-
db.db_name.aggregate
是语法,所有的管道命令都需要写在其中 -
_id
表示分组的依据,按照哪个字段进行分组,需要使用$gender
表示选择这个字段进行分组 -
$sum:1
表示把每条数据作为1进行统计,统计的是该分组下面数据的条数
4. 数据透视
正常情况在统计的不同性别的数据的时候,需要知道所有的name,需要逐条观察,如果通过某种方式把所有的name放到一起,那么此时就可以理解为数据透视
使用示例如下:
- $push 将分组后的数据装入列表
# 以hometown分组,统计每一组中的名字以列表的形式展示
db.stu.aggregate([{
$group: {
_id: "$hometown",
"name_list":{$push: "$name"}
}
}])
返回结果:
{ “_id” : “大理”, “name_list” : [ “段誉”, “段王爷” ] } { “_id” : “桃花岛”, “name_list” : [ “黄蓉”, “黄药师”, “洪七公” ] } { “_id” : “蒙古”, “name_list” : [ “郭靖”, “华筝” ] }
- 使用
$$ROOT
可以将整条文档放入列表中
db.stu.aggregate([{
$group: {
_id: "$hometown",
"name_list":{$push: "$$ROOT"}
}
}])
5. 管道命令之$match
$match
用于进行数据的过滤,是在能够在聚合操作中使用的命令,和find
区别在于$match
操作可以把结果交给下一个管道处理,而find
不行
使用示例如下:
- 查询年龄大于20的数据
db.stu.aggregate({
$match: {
age:{$gt:20}
}
})
- 查询年龄大于20, 以hometown分组, 统计数量
db.stu.aggregate([
{
$match:{
age:{$gt:20}
}
},
{
$group: {
_id: "$hometown",
"counter": {$sum: 1}
}
}
])
6. 管道命令之$project
$project
类似于投影操作用于修改文档的输出结构,控制字段的显示
使用示例如下:
- 查询英雄的年龄和姓名,仅输出年龄和姓名
db.stu.aggregate(
{$project:{_id:0,name:1,age:1}}
)
- 通过性别分组,统计人数,但最后只展示统计人数
db.stu.aggregate([
{
$group:{
_id: "$gender",
counter: {$sum:1}
}
},
{
$project:{_id:0, counter:1}
}
])
7. 管道命令之$sort
$sort
用于将输入的文档排序后输出
使用示例如下
- 查询男性英雄信息,按照年龄降序排序
db.stu.aggregate([
{$match: {gender:true}},
{$sort: {age:-1}}
])
8. 管道命令之$skip
和 $limit
-
$limit
限制返回数据的条数 -
$skip
跳过指定的文档数,并返回剩下的文档数
使用示例如下: -
查询前两条英雄信息
db.stu.aggregate({
$limit:2
})
查询从第三条开始的英雄信息
db.stu.aggregate({
$skip:3
})
- 统计男女生人数,按照人数降序,返回第二条数据
db.stu.aggregate([
{$group:{
_id: "$gender",
gender_count:{$sum:1}
}},
{$sort: {gender_count: -1}},
{$skip: 1},
{$limit:1}
])
5. 索引操作
5.1 作用
- 加快查询速度
- 进行数据的去重
5.2 mongodb创建简单的索引方法
- 语法:
db.集合名.ensureIndex({属性:1})
,1表示升序, -1表示降序
5.3 创建索引前后查询速度对比
测试:插入10万条数据到数据库中
插入数据:
for(i=0;i<100000;i++){db.t1.insert({name:‘test’+i,age:i})}
创建索引前:
db.t1.find({name:‘test10000’})
db.t1.find({name:‘test10000’}).explain(‘executionStats’) # 显示查询操作的详细信息
创建索引:
db.t1.ensureIndex({name:1})
创建索引后:
db.t1.find({name:‘test10000’}).explain(‘executionStats’)
前后速度对比
5.4 索引的查看
默认情况下_id是集合的索引 查看方式:db.集合名.getIndexes()
5.5 删除索引
语法:db.集合名.dropIndex({'索引名称':1})
db.t1.dropIndex({name:1})
db.t1.getIndexes()
6. mongodb创建唯一索引
在默认情况下mongdb的索引域的值是可以相同的,创建唯一索引之后,数据库会在插入数据的时候检查创建索引域的值是否存在,如果存在则不会插入该条数据,但是创建索引仅仅能够提高查询速度,同时降低数据库的插入速度。
6.1. 添加唯一索引的语法:
db.集合名.ensureIndex({"字段名":1}, {"unique":true})
6.2 利用唯一索引进行数据去重
根据唯一索引指定的字段的值,如果相同,则无法插入数据
db.t1.ensureIndex({"name":1}, {"unique":true})
db.t1.insert({name: 'test10000'})
7. 建立复合索引
在进行数据去重的时候,可能用一个域来保证数据的唯一性,这个时候可以考虑建立复合索引来实现。
例如:抓全贴吧信息,如果把帖子的名字作为唯一索引对数据进行去重是不可取的,因为可能有很多帖子名字相同
建立复合索引的语法:db.collection_name.ensureIndex({字段1:1,字段2:1})
8. 建立索引注意点
- 根据需要选择是否需要建立唯一索引
- 索引字段是升序还是降序在单个索引的情况下不影响查询效率,但是带复合索引的条件下会有影响
- 数据量巨大并且数据库的读出操作非常频繁的时候才需要创建索引,如果写入操作非常频繁,创建索引会影响写入速度
例如:在进行查询的时候如果字段1需要升序的方式排序输出,字段2需要降序的方式排序输出,那么此时复合索引的建立需要把字段1设置为1,字段2设置为-1
6. 和Python交互
6.1 pymongo模块
pymongo
提供了mongdb和python交互的所有方法 安装命令:
pip install pymongo
6.2使用
注意: 任何数据库变更操作,数据库和集合能够自动创建
无需权限认证的方式
# 导入模块
from pymongo import MongoClient
# 如果是本地连接host,port参数可以省略
# client = MongoClient()
# 1.连接ubantu的mongodb客户端
client = MongoClient(host="192.168.243.130")
# 支持[]和.语法
# collection = client[数据库名][集合名]
# 2.获取集合对象
collection = client.数据库名.集合名
1. 添加数据
- 添加一条数据: insert_one()
- 添加多条数据: insert_many()
也可以使用 insert() 将要过期
添加一条数据
collection.insert_one({"name": "curry30"})
添加多条数据
collect.insert_many([{"name": "harden"},
{"name": "james"},
{"name": "kobe"}])
2. 查询数据
- 查找一条数据: find_one()
- 查找全部数据: find()
查找一条数据
ret = collect.find_one()
print(ret)
查找全部数据
ret = collect.find()
# find函数默认返回的结果是Cursor对象,是一个可迭代对象
data_list = list(ret)
print(data_list)
注意:
返回所有满足条件的结果
如果条件为空,则返回全部 结果是一个Cursor游标对象,是一个可迭代对象,但是只能够进行一次读取,我们可以强制类型转换成列表
3. 更新数据
语法
collection.update(
{条件},
{'$set':{更新的数据键值对}},
multi=False/True,
upsert=False/True)
-
multi
参数:默认为False,表示更新一条; multi=True则更新多条; multi参数必须和$set一起使用 -
upsert
参数:默认为False; upsert=True则先查询是否存在,存在则更新;不存在就插入 -
$set
表示指定字段进行更新
覆盖式更新
collect.update({"name": "郭靖"}, {"name": "黑马程序员"})
批量更新
# 旧语法
collect.update({"name": "kobe"}, {"$set": {"name": "kobe24"}}, multi=True)
# 新语法,没有multi=True参数
collect.update_many({"name": "kobe24"}, {"$set": {"name": "kobe_24"}})
4. 删除数据
delete_one()删除一条数据
collect.delete_one({"name": "harden"})
delete_many()删除全部数据
collect.delete_many({"name": "james"})
3.其他
pymongo模块其他api:https://pymongo.readthedocs.io/en/stable/
需要权限认证的方式
from pymongo import MongoClient
from urllib.parse import quote_plus
user = 'python' # 账号
password = 'python' # 密码
host = '127.0.0.1' # ip地址
port = 27017 # 端口
# quote_plus函数:对url进行编码
# uri = mongodb://python:aaa@qq.com
uri = "mongodb://%s:%aaa@qq.com%s" % (quote_plus(user),
quote_plus(password),
host)
# 链接客户端
client = MongoClient(uri, port=port)
# 获取集合对象
collection = client.数据库名称.集合名