NoSQL数据库MongoDB和Redis.docx
- 文档编号:16002287
- 上传时间:2023-07-09
- 格式:DOCX
- 页数:41
- 大小:328.88KB
NoSQL数据库MongoDB和Redis.docx
《NoSQL数据库MongoDB和Redis.docx》由会员分享,可在线阅读,更多相关《NoSQL数据库MongoDB和Redis.docx(41页珍藏版)》请在冰点文库上搜索。
NoSQL数据库MongoDB和Redis
NoSQL数据库
—MongoDB和Redis
NoSQL数据库
—MongoDB和Redis
1NoSQL简述
CAP(Consistency,Availabiity,Partitiontolerance)理论告诉我们,一个分布式系统不可能满足一致性,可用性和分区容错性这三个需求,最多只能同时满足两个。
关系型数据库通过把更新操作写到事务型日志里实现了部分耐用性,但带来的是写性能的下降。
MongoDB等NoSQL数据库背后蕴涵的哲学是不同的平台应该使用不同类型的数据库,MongoDB通过降低一些特性来达到性能的提高,这在很多大型站点中是可行的。
因为MongoDB是非原子性的,所以如果如果应用需要事务,还是需要选择MySQL等关系数据库。
NoSQL数据库,顾名思义就是打破了传统关系型数据库的范式约束。
很多NoSQL数据库从数据存储的角度看也不是关系型数据库,而是key-value数据格式的hash数据库。
由于放弃了关系数据库强大的SQL查询语言和事务一致性以及范式约束,NoSQL数据库在很大程度上解决了传统关系型数据库面临的诸多挑战。
在社区中,NoSQL是指“notonlysql”,其特点是非关系型,分布式,开源,可水平扩展,模式自由,支持replication,简单的API,最终一致性(相对于即时一致性,最终一致性允许有一个“不一致性窗口”,但能保证最终的客户都能看到最新的值)。
2MongoDB简介
mongo取自“humongous”(海量的),是开源的文档数据库──nosql数据库的一种。
MongoDB是一种面向集合(collection)的,模式自由的文档(document)数据库。
面向集合是说数据被分成集合的形式,每个集合在数据库中有惟一的名称,集合可以包含不限数目的文档。
除了模式不是预先定义好的,集合与RDBMS中的表概念类似,虽然二者并不是完全对等。
数据库和集合的创建是“lazy”的,即只有在第一个document被插入时集合和数据库才真正创建——这时在磁盘的文件系统里才能看见。
模式自由是说数据库不需要知道存放在集合中的文档的结构,完全可以在同一个集合中存放不同结构的文档,支持嵌入子文档。
文档类似于RDBMS中的记录,以BSON的格式保存。
BSON是BinaryJSON的简称,是对JSON-like文档的二进制编码序列化。
像JSON(JavaScriptObjectNotation)一样,BSON支持在对象和数组内嵌入其它的对象和数组。
有些数据类型在JSON里不能表示,但可以在BSON里表示,如Date类型和BinData(二进制数据),Python原生的类型都可以表示。
与ProtocalBuffers(Google开发的用以处理对索引服务器请求/应答的协议)相比,BSON模式更自由,所以更灵活,但这样也使得每个文档都要保存字段名,所以空间压缩上不如ProtocolBuffers。
BSON第一眼看上去像BLOB,但MongoDB理解BSON的内部机制,所以MongoDB可以深入BSON对象的内部,即使是嵌套的对象,这样MongoDB就可以在顶层和嵌套的BSON对象上建立索引来应对各种查询了。
MongoDB可运行在Linux、Windows和OSX平台,支持32位和64位应用,默认端口为27017。
推荐运行在64位平台,因为MongoDB为了提高性能使用了内存映射文件进行数据管理,而在32位模式运行时支持的最大文件为2GB。
MongoDB查询速度比MySQL要快,因为它cache了尽可能多的数据到RAM中,即使是non-cached数据也非常快。
当前MongoDB官方支持的客户端API语言就多达8种(C|C++|Java|Javascript|Perl|PHP|Python|Ruby),社区开发的客户端API还有Erlang、Go、Haskell......
3术语介绍
数据库、集合、文档
每个MongoDB服务器可以有多个数据库,每个数据库都有可选的安全认证。
数据库包括一个或多个集合,集合以命名空间的形式组织在一起,用“.”隔开(类似于JAVA/Python里面的包),比如集合blog.posts和blog.authors都处于"blog"下,不会与bbs.authors有名称上的冲突。
集合里的数据由多个BSON格式的文档对象组成,document的命名有一些限定,如字段名不能以"$"开头,不能有".",名称"_id"被保留为主键。
如果插入的文档没有提供“_id”字段,数据库会为文档自动生成一个ObjectId对象作为“_id”的值插入到集合中。
字段“_id”的值可以是任意类型,只要能够保证惟一性。
BSONObjectID是一个12字节的值,包括4字节的时间戳,3字节的机器号,2字节的进程id以及3字节的自增计数。
建议用户还是使用有意义的“_id”值。
MongoDb相比于传统的SQL关系型数据库,最大的不同在于它们的模式设计(SchemaDesign)上的差别,正是由于这一层次的差别衍生出其它各方面的不同。
如果将关系数据库简单理解为由数据库、表(table)、记录(record)三个层次概念组成,而在构建一个关系型数据库的时候,工作重点和难点都在数据库表的划分与组织上。
一般而言,为了平衡提高存取效率与减少数据冗余之间的矛盾,设计的数据库表都会尽量满足所谓的第三范式。
相应的,可以认为MongoDb由数据库、集合(collection)、文档对象(Document-oriented、BSON)三个层次组成。
MongoDb里的collection可以理解为关系型数据库里的表,虽然二者并不完全对等。
当然,不要期望collection会满足所谓的第三范式,因为它们根本就不在同一个概念讨论范围之内。
类似于表由多条记录组成,集合也包含多个文档对象,虽然说一般情况下,同一个集合内的文档对象具有相同的格式定义,但这并不是必须的,即MongoDb的数据模式是自由的(schema-free、模式自由、无模式),collection中可以包含具有不同schema的文档记录,支持嵌入子文档。
4MongoDB资源消耗
考虑到性能的原因,mongo做了很多预分配,包括提前在文件系统中为每个数据库分配逐渐增长大小的文件集。
这样可以有效地避免潜在的文件系统碎片,使数据库操作更高效。
一个数据库的文件集从序号0开始分配,0,1...,大小依次是64M,128M,256M,512M,1G,2G,然后就是一直2G的创建下去(32位系统最大到512M)。
所以如果上一个文件是1G,而数据量刚好超过1G,则下一个文件(大小为2G)则可能有超过90%都是空的。
如果想使磁盘利用更有效率,下面是一些解决方法:
1.只建立一个数据库,这样最多只会浪费2G。
2.每个文档使用自建的“_id”值而不要使用默认的ObjectId对象。
3.由于每个document的每个字段名都会存放,所以如果字段名越长,document的数据占用就会越大,因此把字段名缩短会大大降低数据的占用量。
如把“timeAdded”改为“tA”。
Mongo使用内存映射文件来访问数据,在执行插入等操作时,观察mongod进程的内存占用时会发现量很大,当使用内存映射文件时是正常的。
并且映射数据的大小只出现在虚拟内存那一列,常驻内存量才反应出有多少数据cached在内存中。
【按照mongodb官方的说法,mongodb完全由系统内核进行内存管理,会尽可能的占用系统空闲内存,用free可以看到,大部分内存都是作为iocache被占用的,而这部分内存是可以释放出来给应用使用的。
】
5交互式shell
mongo类似于MySQL中的mysql进程,但功能远比mysql强大,它可以使用JavaScript语法的命令从交互式shell中直接操作数据库。
如查看数据库中的内容,使用游标循环查看查询结果,创建索引,更改及删除数据等数据库管理功能。
下面是一个在mongo中使用游标的例子:
>for(varcur=db.posts.find();cur.hasNext();){
...print(tojson(cur.next()));
...}
输出:
{
"_id":
ObjectId("4bb311164a4a1b0d84000000"),
"date":
"WedMar3117:
05:
232010",
"content":
"blablablabla",
"author":
"navygong",
"title":
"thefirstblog"
}
...其它的documents。
6一般功能
6.1插入
客户端把数据序列化为BSON格式传给DB后被存储在磁盘上,在读取时数据库几乎不做什么改动直接把对象返回给客户端,由client完成unserialized。
如:
>doc={'author':
'joe','created':
newDate('2010,6,21'),'title':
'Yetanotherblogpost','text':
'Hereisthetext...','tags':
['example','joe'],'comments':
[{'author':
'jim','comment':
'Idisgree'},{'author':
'navy','comment':
'Goodpost'}],'_id':
'test_id'}
>db.posts.insert(doc)
6.2查询
基本上你能想到的查询种类MongoDB都支持,如等值匹配,<,<=,>,>=,$ne,$in,$mod,$all,$size[1],$exists,$type[2],正则表达式匹配,全文搜索,......。
还有distinct(),sort(),count(),skip()[3],group()[4],......。
这里列表的查询中很多用法都和一般的RDBMS不同。
[1]匹配一个有size个元素的数组。
如db.things.find({a:
{$size:
1}})能够匹配文档{a:
["foo"]}。
[2]根据类型匹配。
db.things.find({a:
{$type:
16}})能够匹配所有a为int类型的文档。
BSON协议中规定了各种类型对应的枚举值。
[3]指定跳过多少个文档后开始返回结果,可以用在分页中。
如:
db.students.find().skip((pageNumber-1)*nPerPage).limit(nPerPage).forEach(function(student){print(student.name+"
");})。
[4]在shardedMongoDB配置环境中应该应该使用map/reduce来代替group()。
6.3删除
可以像查询一样指定条件来删除特定的文档。
6.4索引
可以像在一般的RDBMS中一样使用索引。
提供了建立(一般、惟一、组合)索引、删除索引、重建索引等各种方法。
索引信息保存在集合“system.indexes”中。
6.5map/reduce
MongoDB提供了map/reduce方法来进行数据的批处理及聚集操作。
和Hadoop的使用类似,从集合中接收输入,结果输出到另一个集合。
如果你需要使用group,map/reduce会是个不错的选择。
但MongoDB中的索引和标准查询不是使用map/reduce,而是与MySQL相似。
7模式设计
Mongo式的模式设计
使用Mongo有很多种方式,你本能上可能会像使用关系型数据库一样去使用。
当然这样也可以工作得很好,但却没能发挥出Mongo的真正威力。
Monog是专门设计为富对象模型(richobjectmodel)使用的。
例如:
如果你建立了一个简单的在线商店并且把产品信息存储在关系型数据库中,那你可能会有两个像这样的表:
item
title
price
sku
item_features
sku
feature_name
feature_value
你进行了范式处理因为不同的物品有不同的特征,这样你不用建立一个包含所有特征的表了。
在Mongo中你也可以像上面那样建立两个集合,但像下面这样存储每种物品会更有效。
item:
{
"title":
"price":
"sku":
"features":
{
"opticalzoom":
...
}
}
因为只要查询一个集合就能取得一件物品的所有信息,而这些信息都保存在磁盘上同一个地方,因此大大提高了查询的速度。
如果你想插入或更新一种特征,如db.items.update({sku:
123},{"$set":
{"features.zoom":
"5"}}),也不必在磁盘上移动整个对象,因为Mongo为每个对象在磁盘上预留了空间来适应对象的增长。
8嵌入与引用
以一实例来说,假设需要设计一个小型数据库来存储“学生、地址、科目、成绩”这些信息,那么关系型数据库的设计如图1所示,而key-value型数据库的设计则可能如图2所示。
图1关系型的数据库设计
图2key-value型的数据库设计
对比图1和图2,在关系型的数据库设计里划分出了4个表,而在key-value型的数据库设计里却只有两个集合。
如果说集合与表一一对应的话,那么图2中应该也有4个集合才对,把本应该是集合的address和scores直接合入了集合students中,原因在于在key-value型的数据库里,数据模式是自由的。
以scores来说,在关系型的数据库设计中将其单独成一个表是因为student与score是一对多的关系,如果将score合入student表,那么就必须预留最多可能的字段,这会存在浪费,并且当以后新增一门课程时扩展困难,因此一般都会将score表单独出来。
而对于key-value型的数据库就不同了,其scores字段就是一个BSON,该BSON可以只有一个for_course,也可以有任意多个for_course,其固有的模式自由特性使得它可以将score包含在内而无需另建一个score集合。
对于与student为一对一关系的address表也可以直接合入student,无需担心address的扩展性,当以后需要给address新增一个province字段,直接在数据插入时加上这个值即可。
当然,对于与student成多对多关系course表,为了减少数据冗余,可以将course建立为一个集合,同关系型的数据库设计中类似。
students文档中嵌入了address文档和scores文档,scores文档的“for_course”字段的值是指向courses集合的文档的引用。
如果是关系型数据库,需要把“scores”作为一个单独的表,然后在students表中建立一个指向“scores”的外键。
所以Mongo模式设计中的一个关键问题就是“是值得为这个对象新建一个集合呢,还是把这个对象嵌入到其它的集合中”。
在关系型数据库中为了范式的要求,每个子项都要建一个单独的表,但在Mongo中使用嵌入式对象更有效,所以你应该给出不使用嵌入式对象而单独建一个集合的理由。
为什么说引用要慢些呢,以上面的students集合为例,比如执行:
print(student.scores[0].for_course.name);
如果这是第一次访问scores[0],那些客户端必须执行:
student.scores[0].for_course=db.courses.findOne({_id:
_course_id_to_find_});//伪代码
所以每一次遍历引用都要对数据库进行一次这样的查询,即使所有的数据都在内存中。
再考虑到从客户端到服务器端的种种延迟,这个时间也不会低。
有一些规则可以决定该用嵌入还是引用:
1.第一个类对象,也就是处于顶层的,往往应该有自己的集合。
2.排列项详情对象应该用嵌入。
3.处于被包含关系的应该用嵌入。
4.多对多的关系通常应该用引用。
5.数据量小的集合可以放心地做成一个单独的集合,因为整个集合可以很快地cached。
6.要想获得嵌入式对象的系统级视图会更困难一些。
如上面的“Scores”如果不做成嵌入式对象可以更容易地查询出分数排名前100的学生。
7.如果嵌入的是大对象,需要留意到BSON对象的4M大小限定(后面会讲到)。
8.如果性能是关键就用嵌入。
下面是一些示例:
1.Customer/Order/OrderLine-Item
cutomers和orders应该做成一个集合,line-items应该以数组的形式嵌入在order中。
2.博客系统
posts应该是一个集合;author可以是一个单独的集合,如果只需记录作者的email地址也可以以字段的方式存在于posts中;comments应该做成嵌入的对象。
9GridFS
GridFS是MongoDB中用来存储大文件而定义的一种文件系统。
MongoDB默认是用BSON格式来对数据进行存储和网络传输。
但由于BSON文档对象在MongoDB中最大为4MB,无法存储大的对象。
即使没有大小限制,BSON也无法满足对大数据集的快速范围查询,所以MongoDB引进了GridFS。
9.1GridFS表示的对象信息
1.文件对象(类GridFSFile 的对象)的元数据信息。
结构如下
{
"_id":
"filename":
data_string,//humannameforthefile
"contentType":
data_string,//validmimetypefortheobject
"length":
data_number,//sizeofthefileinbytes
"chunkSize":
data_number,//sizeofeachofthechunks.Defaultis256k
"uploadDate":
data_date,//datewhenobjectfirststored
"aliases":
data_arrayofdata_string,//optionalarrayofaliasstrings
"metadata":
data_object,//anythingtheuserwantstostore
"md5":
data_string//resultofrunning"filemd5"commandonthefile'schunks
}
如下是put进去的一个文件例子:
{
_id:
ObjId(4bbdf6200459d967be9d8e98),
filename:
"/home/hjgong/source_file/wnwb.svg",
length:
7429,
chunkSize:
262144,
uploadDate:
newDate(1270740513127),
md5:
"ccd93f05e5b9912c26e68e9955bbf8b9"
}
2.数据的二进制块以及一些统计信息。
结构如下:
{
"_id":
"files_id":
"n":
data_number,//"chunknumber"-startingwith0
"data":
data_binary(type0x02),//binarydataforchunk
}
因此使用GridFS可以储存富媒体文件,同时存入任意的附加信息,因为这些信息实际上也是一个普通的collection。
以前,如果要存储一个附件,通常的做法是,在主数据库中存放文件的属性同时记录文件的path,当查询某个文件时,需要首先查询数据库,获得该文件的path,然后从存储系统中获得相应的文件。
在使用GridFS时则非常简单,可以直接将这些信息直接存储到文件中。
比如下面的Java代码,将文件file(file可以是图片、音频、视频等文件)储存到db中:
其中该方法的第一个参数的类型还可以是InputStream,byte[],从而实现多个重载的方法。
9.2GridFS管理
MongoDB提供的工具mongofiles可以从命令行操作GridFS。
如:
./mongofiles-hostlocalhost:
1727-unavygong-p111put~/source_file/wnwb.svg
每种语言提供的MongoDB客户端API都提供了一套方法,可以像操作普通文件一样对GridFS文件进行操作,包括read(),write(),tell(),seek()等。
10Replication(复制)
Mongo提供了两种方式的复制:
简单的master-slave配置及replicapair的概念。
如果安全认证被enable,不管哪种replicate方式,都要在master/slave中创建一个能为各个database识别的用户名/密码。
认证步骤如下:
slave先在local.system.users里查找一个名为"repl"的用户,找到后用它去认证master。
如果"repl"用户没有找到,则使用local.system.users中的第一个用户去认证。
local数据库和admin数据库一样,local中的用户可以访问整个dbserver。
10.1master-slave模式
一个server可以同时为master和slave。
一个slave可以有多个master,这种方式并不推荐,因为可能会产生不可预期的结果。
在该模式中,一般是在两个不同的机器上各部署一个MongDB实例,一个为master,另一作为slave。
将MongoDB作为master启动,只需要在命令行输入:
./mongod--master
然后主服务进程将会在数据库中创建一个集合local.oplog.$main,该collection主要记录了事务日志,即需要在slave执行的操作。
而将MongoDB作为slave启动,只需要在命令行输入:
./mongod--slave--source
port不指定时即使用默认端口,masterhostname是master的IP或master机器的FQDN。
其他配置选项:
--autoresync:
自动sync,但在10分钟内最多只会进行一次。
--oplogSize:
指定master上用于存放更改的数据量,如果不指定,在32位机上最少为50M,
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- NoSQL 数据库 MongoDB Redis