MongoDB是一个面向文档(document-oriented)的数据库,而不是关系型数据库.不采用关系型主要是为了获得更好的扩展性.与关系型数据库相比,面向文档的数据库不再有行的概念,取而代之的是更为灵活的文档模型.
通过在文档中嵌入文档和数组,面向文档的方法都能够仅使用一条记录来表现复杂的层级关系,这与现代的面向对象语言的开发者对数据的看法一致
另外,不再有预定义模式(predefined schema):文档的键(key)和值(value)不再是固定的类型和大小.由于没有固定的模式,根据需要添加或删除字段变得更容易,通常由于开发者能够进行快速迭代,所以开发进程得以加快.而且实验更容易进行,开发者能尝试大量的数据模型,从中选一个最好的.
易扩展:
应用程序数据集的大小正在以不可思议的程度增长.随着可用带宽的增长和存储器价格的下降,即使是一个小规模的应用程序,需要存储的数据量也可能大的惊人,甚至超出了很多数据库的处理能力.过去非常罕见的T级数据,现在已是司空见惯.
由于需要存储的数据量不断增长,开发者面临一个问题:应该如何扩展数据库,分为纵向扩展和横向扩展,纵向扩展是最省力的做法,但缺点是大型机一般都比较贵,而且当数据量达到机器的物理极限时,花再多的钱也买不到更强的机器了,此时选择横向扩展更为合适,但横向扩展带来的另外一个问题就是需要管理的机器太多.
MongoDB的设计采用横向发展,面向文档的数据模型使他能很容易的在多台服务器之间进行数据分割.MongoDB能够自动处理跨集群的数据和负载,自动重新分配文档,以及将用户的请求路由到正确的机器上.这样,开发者能够集中精力编写应用程序,而不需要考虑如何扩展的问题.如果一个集群需要更大的容量,只需要向集群添加新服务器,MongoDB就会自动将现有的数据向新服务器传送
功能全
MongoDB作为一款通用型数据库,除了能够创建,读取,更新和删除数据之外,还提供了一系列不断扩展的独特功能
1.索引
支持通用二级索引,允许多种快速查询,且提供唯一索引,复合索引,地理空间索引,全文索引
2.聚合
支持聚合管道,用户能通过简单的片段创建复杂的数据,并通过数据库自动优化
3.特殊的集合类型
支持存在时间有限的集合,适用于那些将在某个时刻过期的数据,如会话session.类似的,MongoDB也支持固定大小的集合,用于保存近期数据,如日志
4.文件存储
支持一种非常易用的协议,用于存储大文件和文件元数据,MongoDB并不具备一些在关系型数据库中很普遍的功能,如链接join和复杂的多行事物.省略这些功能是处于架构上的考虑,或者说为了得到更好的扩展性,因为分布式系统中这两个功能难以高效的实现
卓越的性能
MongoDB的一个主要目标是提供卓越的性能,这很大程度上决定了MongoDB的设计,MongoDB把尽可能多的内存用作缓存cache,视图为每次查询自动选择正确的索引.总之各方面的设计都旨在保持它的高性能.虽然MongoDB非常强大并试图保留关系型数据库的很多特性,但他并不追求具备关系型数据库的所有功能.只要有可能,数据库服务器就会将处理逻辑交给客户端.这种精简方式的设计是MongoDB能够实现如此高性能的原因之一
注意事项
1.文档中的键/值对是有序的
2.文档中的值不仅可以是双引号里面的字符串,还可以是其他几种数据类型(甚至是整个嵌入的文档)
3.MongoDB区分类型和大小写
4.MongoDB的文档不可以有重复的键
5.文档中的值可以是多种不同的数据类型,也可以是一个完整的内嵌文档,文档的键是字符串,除了少数例外情况,键可以使用任意UTF-8字符
文档键命名规范:
1.键不能含有\0(空字符).这个字符用来表示键的结尾
2. .和$有特别的含义,只有在特定环境下才能使用
3.以下划线"_"开头的键是保留的
登录:此时没有任何的权限限制,默认是管理员角色
创建账号:
use admin
db.createUser({user:'root',pwd:"123",roles:[{role:"root",db:"admin"}]})
use test
db.createUser({user:"jerry",pwd:"123",roles:[{role:"readwrite,db:"test"},{role:"read",db:"db1"}]})
在mongodb中用不同的数据库来区分权限,要管理那个数据库就在哪个数据库下创建用户即可,创建管理员账户则在admin下创建
db是一个全局变量 表示当前的数据库
db.createUser()是调用一个内部函数用于创建用户
每个账号可以具备多个角色
开启账号认证
默认情况下mongodb不会加载认证信息就像mysql跳过授权表一样,创建完账号用户需要开启用户认证
具体方法:
修改配置文档mongodb.cfg(安装目录bin下)
首先将bind_ip改为0.0.0.0(让其他电脑可以访问,用于远程连接,如果bind_ip是127.0.0.1的话,就只能本地访问)
security:
authorization:enabled
然后重启MongoDB Server服务,启用认证
账号测试:
直接进入程序已经无法查看数据库
show dbs
登录方式一:authenticationDatabase指定数据库
mongo --port 27017 -u "root" -p "123" --authenticationDatabase "admin"
登录方式二:进入mongo后
use admin
db.auth("root","123")
删除账号
db.dropUser("用户名")
修改密码
db.changeUserPassword(用户名,新密码);
库的操作
创建数据库
use 数据库名称
如果有则切换没有则创建,注意如果数据库中没有数据show dbs则不显示
查看数据库
show dbs
删除
db.dropDatabase() 区分大小写
集合的操作
集合是一个存储元素的容器类比mysql中的表
创建集合:
db.user
查看集合:
show collections show tables
同样的数据集合中没有数据则不会显示
数据之间的关系需要应用程序维护,数据库之间没有关系
删除集合
db.user.drop()
基本数据类型
1.在概念上,MongoDB的文档与Javascript的对象类似,因而可以认为他类似于JSON.JSON是一种简单的数据表示方式:其规范仅用一段文字就能描述清楚,且仅包含六种数据类型
2.这样有很多好处:易于理解,易于解析,易于记忆.然而从另一方面说,因为只有null,布尔,数字,字符串,数字和对象这几种数据类型,所以JSON表达能力有一定的局限
3.虽然JSON具备的这些类型已经具有很强的表现力,但绝大多数应用(尤其是在于数据库打交道时)都还需要其他一些重要的类型.例如,JSON没有日期类型,这使得难以处理日期类型,JSON只有一种数字类型,无法区分浮点数和整数,再者JSON无法表示其他一些通用类型,如正则表达式或函数
4.MongoDB在保留了JSON基本键/值对的基础上,添加了其他一些数据类型.在不同的编程员语言下,这些类型的确切表示有些许差异.下面说明了MOngoDB支持的其他通用类型,以及如何正在文档中使用他们
1.null :用于标识空或不存在的字段
d={"x":null}
2.布尔型:true和flase
d={'x':true,'y':false}
3.数值
d={'x':3,'y':3.1234}
4.字符串
d={'x':'123'}
5.日期
d={'x':new date()}
d.x.getHours()
6.正则表达式
d={'pattern':/^sb$/i}
正则写在/ / 内,后面的i代表:
i:忽略大小写 m:多行匹配模式 x:忽略非转义的空白字符 s:单行匹配模式
7.数组
d={'x':[1,'s',1,'v']}
8.内嵌文档
user={'name':'jerry',add:{'coutry':'china'}}
user.add.country
9.对象id:是一个12字节的ID,是文档的唯一标识,不可变
d={'x':ObjectId()}
案例:
db.tb.insert({'a':null,'b'L1.1,'c':true,'d':100,'e':'aaaa','f':new Date(),'g':/^as$/i,'h':[1,2],'j':{"name':'smallJerry'}})
db.tb.find()
db.tb.find().pretty()格式化显示
_id和ObjectId
MongoDB中存储的文档必须有一个"_id"键.这个键可以是任意类型,默认是个ObjectId对象
在一个集合里,每个文档都有一个唯一的'_id',确保集合里每个文档都能被唯一标识.不同集合'_id'的值可以重复,但同一集合内'_id'的值必须唯一
#1.ObjectId
ObjectId是'_id'的默认类型.因为涉及MongoDB的初衷就是用作分布式数据库,所以能在分片环境中生成唯一的标识符非常重要,而常规的做法:在多个服务器上同步自动增加主键即费时又费力,这就是MongoDB采用ObjectsId的原因.
ObjectId采用12字节的存储空间,是一个由24个十六进制数字组成的字符串
0|1|2|3| 4|5|6| 7|8| 910|11
时间戳 机器 PID 计数器
如果快速创建多个ObjectId,会发现每次只有最后几位有变化,另外,中间的几位数字也会变化(要是在创建过程中停顿几秒),这就是ObjectId的创建方式导致的
时间戳单位为秒,与随后5个字节组合起来,提供了秒级的唯一性,这个4个字节隐藏了文档的创建时间,绝大多数驱动程序都会提供一个方法,用于从ObjectId中获取这些信息.
因为使用的是当前时间,很多用户担心要对服务器进行时钟同步,其实没必要,因为时间戳的实际值并不重要,只要他不停增加就好.接下来三个字节是所在主机的唯一标识符,通常是机器主机名的散列值.这样就可以保证不同主机生成不同的ObjectId,不产生冲突.接下来2个字节确保了在同一台机器上并发的多个进程产生的ObjectId是唯一的,前9个字节确保了同一秒钟不同机器并发的多个进程产生的ObjectId是唯一的,最后三个字节是一个自动增加的计数器,确保相同进程的同一秒产生的ObjectId也是不一样的
2.自动生成_id
如果插入文档时,没有'_id'键,系统会自动帮你创建一个.可以由MongoDB服务器来做这件事情.但通常会在客户端由驱动程序完成.这一做法非常好的体现了MOngoDB的哲学:能交给客户端驱动程序来做的事情就不要交给服务器来做.这种理念背后的原因是:即便是像MongoDb这样扩展性非常好的数据库,扩展应用层也要比扩展数据库层容易得多.将工作交给客户端就减轻了数据库扩展的负担
文档操作
1.没有指定_id则默认ObjectId,_id不能重复,且插入后不可变
2.插入单条
user0={"name":"scx","age":18,'hobbies':['music','read','dancing'],
'addr':{'country':'china','city':'xinyang'}}
db.test.insert(user0)
db.test.find()
db.test.save()有则覆盖,无责插入
插入多条使用insertMany([user1,user2,user3,user4])
查询数据
find查找所有匹配数据
findone查找第一个匹配的
MongoDB:{key:value}代表什么等于什么,'$ne','$gt','$lt','$gte','$lte',分别意为不等于,大于,小于,大于等于,小于等于.其中'$ne'能用于所有数据类型
简单的查询语句:实例
db.user.find({'name':'cxx'})
db.user.find({'name':{'$ne':'cxx'}})
db.user.find({'_id':{'$gt':2}})
db.user.find({'_id':{'$gte":2}})
逻辑运算
MongoDB:字典中逗号分隔的多个条件是and关系,'$or'的条件放到[]内,'$not'
db.user.find({'_id'}:{'$gte':2,'$lt':4})
db.user.find({"_id':{"$gte':2},"age":{"$lt":40})
db.user.find({"$or":[{"_id":{"$egt":5}],{'name':"cxx'}]})
db.user.find({'_id':{'$mod':[2,1]}})
db.user.find({'_id':{'$not':{'$mod':[2,1]}}})
成员运算
db.user.find({"age":{'$in':[20,30,40]}})
db.user.find({"age':{"$nin":['alex','yuanho']})
正则匹配
dbuser.find({'name':/^s.*?[s|b]$/i})等同于dbuser.find({'name':/^s.*?(s|b)$/i})
指定字段
db.user.find({"_id":3},{'_id':0,'name':1,'age':1})其中0表示不显示,1表示表示显示,默认为0,且'_id'默认为1
查询数组
db.user.find({'hobbies':'dancing'})
db.user.find({'hobbies':{"$all":['dancing','ea'}})
db.user.find({'hobbies.3':'tea'})
4.查看所有人最后两个喜好
db.user.find({},{'hobbies':{"$slice":-2},'age':0,"_id':0})
db.user.find({},{'hobbies':{"$slice":[1,2]},"age":0,"_id":1,"name":0})
其他
db.user.find().sort({"name":1})
db.user.find().sort({'age':-1,"_id":1})
排序中1代表升序,-1代表降序
分页:limit代表取多少个document,skip代表跳过前多少个document
db.user.find().sort({'age':1}).limit(1).skip(2)
获取数量: db.user.count({'age':{"$gt":10}})
等同于:db.user.find({'age':{"$gt":10}}).count()
{key:null}匹配key的值为null或者没有这个key
db.user.insert({'a':12,'b':1111})
db.user.insert({'a':123})
db.user.insert('b':null)
查找所有:
db.user.find({'b':null})
将查找所有b的值为null或不存在键b的值
修改数据
update()方法用于更新已存在的文档.语法格式如下:
db.collection.update(<query>,<update>,{upsert:<boolean>,multi:<boolean>,wrtieConcern:<document>})
参数说明:query相当于where条件.update:update的对象和一些更新的操作符(如$,$inc相当于set后面的)