数据库——MongoDB
what's the MongoDB
MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。他支持的数据结构非常松散,是类似 JSON 的 BSON 格式,因此可以存储比较复杂的数据类型。Mongo 最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。MongoDB 具有易用性、易扩展性、功能丰富、性能卓越的优势。
易用性:
MongoDB 是一个面向文档(document-oriented)的数据库,而不是关系型数据库。
不采用关系型主要是为了获得更好得扩展性。当然还有一些其他好处,与关系数据库相比,面向文档的数据库不再有“行”(row)的概念取而代之的是更为灵活的“档”(document)模型。
通过在文档中嵌入文档和数组,面向文档的方法能够仅使用一条记录来表现复杂的层级关系,这与现代的面向对象语言的开发者对数据的看法一致。
另外,不再有预定义模式(predefined schema):文档的键(key)和值(value)不再是固定的类型和大小。由于没有固定的模式,根据需要添加或删除字段变得更容易了。通常由于开发者能够进行快速迭代,所以开发进程得以加快。而且,实验更容易进行。开发者能尝试大量的数据模型,从中选一个最好的。
易扩展性:
应用程序数据集的大小正在以不可思议的速度增长。随着可用带宽的增长和存储器价格的下降,即使是一个小规模的应用程序,需要存储的数据量也可能大的惊人,甚至超出很多数据库的处理能力。过去非常罕见的 T 级数据,现在已经是司空见惯了。
由于需要存储的数据量不断增长,开发者面临一个问题:应该如何扩展数据库,分为纵向扩展和横向扩展,纵向扩展是最省力的做法,但缺点是大型机一般都非常贵,而且
当数据量达到机器的物理极限时,花再多的钱也买不到更强的机器了,此时选择横向扩展更为合适,但横向扩展带来的另外一个问题就是需要管理的机器太多。
MongoDB 的设计采用横向扩展。面向文档的数据模型使它能很容易地在多台服务器之间进行数据分割。MongoDB 能够自动处理跨集群的数据和负载,自动重新分配文档,以及将用户的请求路由到正确的机器上。这样,开发者就能够集中精力编写应用程序,而不需要再去考虑如何扩展的问题。如果一个集群需要更大的容量,只需要向集群添加新服务器 MongoDB 就会自动将现有的数据向新服务器传送。
功能丰富:
MongoDB作为一款通用型数据库,除了能够创建、读取、更新和删除数据之外,还提供了一系列不断扩展的独特功能
- 索引:支持通用二级索引,允许多种快速查询,且提供唯一索引、复合索引、地理空间索引、全文索引
- 聚合:支持聚合管道,用户能通过简单的片段创建复杂的集合,并通过数据库自动优化
- 特殊的集合类型:支持存在时间有限的集合,适用于那些将在某个时刻过期的数据,如会话session。类似地,MongoDB也支持固定大小的集合,用于保存近期数据,如日志
- 文件存储:支持一种非常易用的协议,用于存储大文件和文件元数据。MongoDB并不具备一些在关系型数据库中很普遍的功能,如链接join和复杂的多行事务。省略这些的功能是处于架构上的考虑,或者说为了得到更好的扩展性,因为在分布式系统中这两个功能难以高效地实现
性能卓越:
MongoDB 的一个主要目标是提供卓越的性能,这很大程度上决定了 MongoDB 的设计。MongoDB 把尽可能多的内存用作缓存 cache,视图为每次查询自动选择正确的索引。总之各方面的设计都旨在保持它的高性能虽然 MongoDB 非常强大并试图保留关系型数据库的很多特性,但它并不追求具备关系型数据库的所有功能。只要有可能,数据库服务器就会将处理逻辑交给客户端。这种精简方式的设计是 MongoDB 能够实现如此高性能的原因之一。
安装
好,以上都是废话,根本无需多看。接下来进入正题部分。
- 首先从官网下载 MongoDB 的安装包,选择 Windows 版本,我的安装路径就是C盘 C:\。安装完毕后,将 bin 文件夹所的目录加入环境变量中 C:\MongoDB\bin
- 接下来我们在 MongoDB 的根目录中创建两个文件夹:data 和 log,用于存放数据和日志,在 data 文件夹中再创建一个 db 文件夹
- 制作系统服务
-
#在命令提示符中键入如下内容 #mongod --bind_ip 0.0.0.0 --port 27017 --logpath C:\MongoDB\log\mongod.log --logappend --dbpath C:\MongoDB\data\db --serviceName "MongoDB" --serviceDisplayName "MongoDB" --install #0.0.0.0 表示任何主机都可以连接,默认port是27017,在我们自己建的日志文件夹log下会生成一个mongod.log文件,以追加的形式写入内容,服务端的名字叫MongoDB,无--auth说明允许不登录状态下跳过授权进入
# 启动/关闭服务端: net start MongoDB net stop MongoDB # 启动客户端: mongo
附:本人在启动客户端时遇到了一个问题,报错内容为下图。后来使用管理员启动命令提示符后 net start MongoDB 就没问题了。汗……
账号管理
MongoDB 与 MySQL 不同,MongoDB 中每一个数据库都有自己的账号密码和相应的权限,所以 MongoDB 中建立账号时需先指定在哪个数据库下。
关于数据库的基本命令:
use 数据库名 #选择数据库 db #查看当前数据库 show dbs#查看所有数据库 #MongoDB中若数据库为空时,show dbs命令不显示该数据库 help#这是一个全局命令,可以帮助我们操作
创建账号:注意需指定数据库
use admin#admin下,admin是管理员数据库 db.createUser( { user: "root", pwd: "123", roles: [ { role: "root", db: "admin" } ] } ) #user是用户名,pwd是密码,role是角色,role中的"root"指的是超级管理员 use test db.createUser( { user: "egon", pwd: "123", roles: [ { role: "readWrite", db: "test" }, { role: "read", db: "db1" } ] } ) #创建了一个针对test库有读写权限,对db1库只有读权限的普通用户
对于开发人员而言掌握以上创建账户的命令就够了,毕竟数据库的操作在公司中由 DBA 掌控。更多创建用户的命令见官网https://docs.mongodb.com/master/tutorial/enable-authentication/
创建完账号后我们就要对数据库做一点操作,不能再允许跳过授权的情况下访问了。
mongod --remove # 关闭数据库 mongod --bind_ip 0.0.0.0 --port 27017 --logpath D:\MongoDB\log\mongod.log --logappend --dbpath D:\MongoDB\data\db --serviceName "MongoDB" --serviceDisplayName "MongoDB" --install --auth #重新制作系统服务,需账号密码才可访问
这样之后想要访问数据库就必须先登录了。
登录方式有两种,不过首先记得登陆的时候也要指定数据库,毕竟你的账号只对某些库有权限
#方式一: mongo --port 27017 -u "root" -p "123" --authenticationDatabase "admin" #方式二: mongo#启动客户端 use admin#选择数据库 db.auth("root","123")#输入账号密码
MongoDB基础知识
MongoDB 中没有表的概念,相较于 MySQL 的表而言,在 MongoDB 中以集合的形式存在。MongoDB 无需设计表结构所以也就没有外键字段,直接在数据库中创建集合,然后在集合中插入文档就可以了。文档是 MongoDB 的核心概念。文档就是键值对的一个有序集{'msg':'hello','foo':3},其结构类似于 python 中的有序字典。文档相当于 MySQL 中的记录(就是每一行数据)。MongoDB 没有外键,所以表关系全靠个人命名时给出相应提示,这也使得用 MongoDB 操作数据更加简便。
多个文档==(组成)==>多个集合==(组成)==>数据库
文档
命名规范:
- 键不能含有\0 (空字符)。这个字符用来表示键的结尾。
- . 和 $ 有特别的意义,只有在特定环境下才能使用。
- 以下划线"_"开头的键是保留的(不是严格要求的)。
需要注意的点:
- 文档中的键/值对是有序的。
- 文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)。
- 区分类型和大小写。
- 文档不能有重复的键。
- 文档中的值可以是多种不同的数据类型,也可以是一个完整的内嵌文档。文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符。
集合
命名规范:
- 集合名不能是空字符串""
- 集合名不能含有\0字符(空字符),这个字符表示集合名的结尾
- 集合名不能以"system."开头,这是为系统集合保留的前缀
- 用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里面包含,这是因为某些系统生成的集合中包含该字符。千万不要在名字里出现$。
集合存在于数据库中,通常情况下为了方便管理,不同格式和类型的数据应该插入到不同的集合,但其实集合没有固定的结构,这意味着我们完全可以把不同格式和类型的数据统统插入一个集合中。
组织子集合的方式就是使用“.”,分隔不同命名空间的子集合。
比如一个具有博客功能的应用可能包含两个集合,分别是 blog.posts 和 blog.authors,这是为了使组织结构更清晰,这里的 blog 集合(这个集合甚至不需要存在)跟它的两个子集合没有任何关系。在 MongoDB 中,使用子集合来组织数据非常高效,值得推荐。
数据库
命名规范:
- 不能是空字符串("")
- 不得含有' '(空格)、.、$、/、\和\0 (空字符)
- 应全部小写
- 最多64字节
- 有些数据库是默认创建的,有其特殊的意义:admin、local、config
-
admin: 从身份认证的角度讲,这是“root”数据库,如果将一个用户添加到 admin 数据库,这个用户将自动获得所有数据库的权限。再者,一些特定的服务器端命令也只能从 admin 数据库运行,如列出所有数据库或关闭服务器
-
local: 这个数据库永远都不可以复制,且一台服务器上的所有本地集合都可以存储在这个数据库中
-
config: MongoDB 用于分片设置时,分片信息会存储在 config 数据库中
-
MongoDB基本数据类型
1、在概念上,MongoDB 的文档与 Javascript 的对象相近,因而可以认为它类似于 JSON。JSON 是一种简单的数据表示方式:其规范仅用一段文字就能描述清楚(其官网证明了这点),且仅包含六种数据类型——null、布尔、数字、字符串、数组和对象(MongoDB还包含日期、内嵌文档、对象ID三种数据类型)
2、这样有很多好处:易于理解、易于解析、易于记忆。然而从另一方面说,因为只有 null、布尔、数字、字符串、数组和对象这几种数据类型,所以 JSON 的表达能力有一定的局限。
3、虽然 JSON 具备的这些类型已经具有很强的表现力,但绝大数应用(尤其是在于数据库打交道时)都还需要其他一些重要的类型。例如,JSON 没有日期类型,这使得原本容易日期处理变得烦人。另外,JSON 只有一种数字类型,无法区分浮点数和整数,更别区分 32 位和 64 位了。再者 JSON 无法表示其他一些通用类型,如正则表达式或函数。
4、MongoDB 在保留了 JSON 基本键/值对特性的基础上,添加了其他一些数据类型。在不同的编程语言下,这些类型的确切表示有些许差异。下面说明了 MongoDB 支持的其他通用类型,以及如何正在文档中使用它们
- null:d={'x':null} 用于表示空或不存在的字段
- 布尔型:d={'x':true,'y':false} 注意是 js 的 true 和 false 不是 Python 的 True 和 False
- 数值:d={'x':3,'y':3.1415926} 支持整型和浮点型
- 字符串:d={'x':'Damon'} 与 Python 字符串一致,没啥好说的
- 日期:d={'x':new Date()} 是一个时间对象
- 数组:d={'x':[1,'a','v']} 索引从 0 开始
- user={'name':'egon','addr':{'country':'China','city':'YT'}} 类似于字典套字典,取值形式为 user.addr.country
- 对象id:d={'x':ObjectId()} MongoDB插数据时不指定 _id 的话默认就是用这种形式自增的,好处是绝对唯一。是一个 12 字节的 ID,是文档的唯一标识,不可变
- 正则表达式:d={'pattern':/^egon.*?nb$/i} 正则写在//内,后面的 i 代表:
- i ——忽略大小写
- m ——多行匹配模式
- x ——忽略非转义的空白字符
- s ——单行匹配模式
!!!重点!!!
_id:MongoDB 中存储的文档必须有一个 "_id" 键,其作用与 MySQL 的 id 相同。这个键的值可以是任意类型,不指定时默认是个 ObjectId 对象。在一个集合里,每个文档都有唯一的 “_id”,确保集合里每个文档都能被唯一标识。不同集合 "_id" 的值可以重复,但同一集合内 "_id" 的值必须唯一。
ObjectId:ObjectId是 "_id" 的默认类型。因为设计 MongoDB 的初衷就是用作分布式数据库,所以能够在分片环境中生成唯一的标识符非常重要,而常规的做法:在多个服务器上同步自动增加主键既费时又费力,这就是 MongoDB 采用 ObjectId 的原因。
ObjectId的结构:
""" ObjectId采用12字节的存储空间,是一个由24个十六进制数字组成的字符串 0|1|2|3| 4|5|6| 7|8 9|10|11 时间戳 机器 PID 计数器 如果快速创建多个ObjectId,会发现每次只有最后几位有变化。另外,中间的几位数字也会变化(要是在创建过程中停顿几秒)。 这是ObjectId的创建方式导致的,如上图 时间戳单位为秒,与随后5个字节组合起来,提供了秒级的唯一性。这个4个字节隐藏了文档的创建时间,绝大多数驱动程序都会提供 一个方法,用于从ObjectId中获取这些信息。 因为使用的是当前时间,很多用户担心要对服务器进行时钟同步。其实没必要,因为时间戳的实际值并不重要,只要它总是不停增加就好。 接下来3个字节是所在主机的唯一标识符。通常是机器主机名的散列值。这样就可以保证不同主机生成不同的ObjectId,不产生冲突 接下来连个字节确保了在同一台机器上并发的多个进程产生的ObjectId是唯一的 前9个字节确保了同一秒钟不同机器不同进程产生的ObjectId是唯一的。最后3个字节是一个自动增加的 计数器。确保相同进程的同一秒产生的 ObjectId也是不一样的。 """
增删改查操作
MongoDB 的增删改查操作详情请见作者博客http://www.cnblogs.com/zhuminghui/p/8330429.html
MongoDB 的可视化工具是 Robo 3T,与 MySQL 的 Navicat 用法基本一致,下载链接:https://robomongo.org/
pymongo
与 MySQL 一样,Python 通过解释器也可以直接对 MongoDB 进行操作,当然你得安装了 pymongo 才行
pip3 install pymongo
pymongo 的使用方式与 pymysql 如出一辙,下面列出简要示例代码,详情可咨询官网:http://api.mongodb.com/python/current/tutorial.html
from pymongo import MongoClient #1、链接 client=MongoClient('mongodb://root:123@localhost:27017/') # client = MongoClient('localhost', 27017) #2、use 数据库 db=client['db2'] #等同于:client.db1 #3、查看库下所有的集合 print(db.collection_names(include_system_collections=False)) #4、创建集合 table_user=db['userinfo'] #等同于:db.user #5、插入文档 import datetime user0={ "_id":1, "name":"egon", "birth":datetime.datetime.now(), "age":10, 'hobbies':['music','read','dancing'], 'addr':{ 'country':'China', 'city':'BJ' } } user1={ "_id":2, "name":"alex", "birth":datetime.datetime.now(), "age":10, 'hobbies':['music','read','dancing'], 'addr':{ 'country':'China', 'city':'weifang' } } # res=table_user.insert_many([user0,user1]).inserted_ids # print(res) # print(table_user.count()) #6、查找 # from pprint import pprint#格式化细 # pprint(table_user.find_one()) # for item in table_user.find(): # pprint(item) # print(table_user.find_one({"_id":{"$gte":1},"name":'egon'})) #7、更新 table_user.update({'_id':1},{'name':'EGON'}) #8、传入新的文档替换旧的文档 table_user.save( { "_id":2, "name":'egon_xxx' } )