龙空技术网

阿里程序员教你如何翻越持久层MongoDB数据库设计这座大山

程序员xysam 516

前言:

如今同学们对“oraclegte”大致比较珍视,我们都想要知道一些“oraclegte”的相关文章。那么小编同时在网络上汇集了一些对于“oraclegte””的相关文章,希望姐妹们能喜欢,姐妹们一起来学习一下吧!

MongoDB是一个基于分布式文件存储的数据库,由C++语言编写,旨在为Web应用提供可扩展的高性能数据存储解决方案。

MongoDB是一个介于关系数据库和非关系数据库之间的数据库。它支持的数据结构非常松散,类似JSON的BSON格式,因此可以存储比较复杂的数据类型。

MongoDB最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。

一 集合与文档

MongoDB是“面向集合”(Collection-Oriented)进行设计的,数据库中的数据被分组存储在不同的数据集合(Collection)中。每个集合在数据库中都有一个唯一的标识名,并且可以包含无限数目的文档(Document)。

集合的概念类似关系数据库(RDBMS)中的表(Table),不同的是它不需要定义任何模式(schema)。向表中写入数据前必须提前定义表结构,而向集合中写入数据前不需要限制集合的结构,这被称为模式自由(schemafree),这意味着对于存储在MongoDB数据库中的文件,不需要知道它的任何结构定义。如果需要的话,完全可以把不同结构的文件存储在同一个数据库里。

写入表中的数据被称为记录行,而写入集合中的数据被称为文档。

存储在集合中的文档,被存储为键/值对的形式。键用于唯一标识一个文档,为字符串类型;而值则可以是各种复杂的文件类型(如整形、日期型),这种存储形式被称为BSON(Binary Serialized Document Format)。

下面详细介绍一下MongoDB中的文档与集合的基本概念。

(1)文档。

文档是MongoDB中数据的基本单位,类似于关系数据库中的行(但是比行复杂)。多个键及其关联的值有序地放在一起就构成了文档。不同的编程语言对文档的表示方法不同,在JavaScript中文档表示为:

{"greeting":"hello,world"}

这个文档只有一个键"greeting",对应的值为"hello, world"。多数情况下,文档比这个更复杂,它包含多个键/值对。例如:

{"greeting":"hello,world","foo": 3}

文档中的键/值对是有序的,下面的文档与上面的文档是两个完全不同的文档。

{"foo": 3,"greeting":"hello,world"}

文档中的值不仅可以是双引号中的字符串,也可以是其他的数据类型,如整型、布尔型、日期型等,还可以是另外一个文档,即文档可以嵌套。文档中的键类型只能是字符串类型。

(2)集合。

集合就是一组文档,类似于关系数据库中的表。集合是无模式的,集合中的文档可以是各式各样的。例如,{"hello,word":"Mike"}和{"foo": 3},

它们的键不同,值的类型也不同,但是它们可以存放在同一个集合中,也就是不同模式的文档可以放在同一个集合中。

既然集合中可以存放任何类型的文档,那么为什么还需要使用多个集合?这是因为所有文档都放在同一个集合中,无论对于开发者还是管理员,都很难对集合进行管理,而且这种情形下,对集合的查询等操作效率很低。所以在实际使用中,往往将文档分类存放在不同的集合中,例如,对于网站的日志记录,可以根据日志的级别进行存储,Info级别日志存放在Info集合中,Debug级别日志存放在Debug集合中,这样既方便了管理,也提高了查询性能。但是需要注意的是,这种对文档进行划分来分别存储并不是MongoDB的强制要求,用户可以灵活选择。

可以使用“.”按照命名空间将集合划分为子集合。例如,对于一个博客系统,可能包括blog.user和blog.article两个子集合,但这样划分只是让组织结构更清晰一些,blog集合和blog.user、blog.article没有任何关系。虽然子集合没有任何特殊的地方,但是使用子集合组织数据结构会更清晰,这也是MongoDB推荐的方法。

二 MongoDB应用场景

MongoDB的设计目标是高性能、可扩展、易部署、易使用,使用它存储数据非常方便。其主要功能特性如下。

(1)面向集合存储,容易存储对象类型的数据。在MongoDB中数据被分组存储在集合中,集合类似RDBMS中的表,一个集合中可以存储无限多的文档。

(2)模式自由,采用无模式结构存储。在MongoDB集合中存储的数据是无模式的文档,采用无模式存储数据是集合区别于RDBMS中的表的一个重要特征。

(3)支持完全索引,可以在任意属性上建立索引,包含内部对象。

MongoDB的索引和RDBMS的索引基本一样,可以在指定属性、内部对象上创建索引以提高查询的速度。除此之外,MongoDB还提供创建基于地理空间的索引的能力。

(4)支持查询。MongoDB支持丰富的查询操作,MongoDB几乎支持SQL中的大部分查询。

(5)强大的聚合工具。MongoDB除了提供丰富的查询功能外,还提供强大的聚合工具,如count、group等,支持使用MapReduce完成复杂的聚合任务。

(6)支持复制和数据恢复。MongoDB支持主从复制机制,可以实现数据备份、故障恢复、读扩展等功能。而基于副本集的复制机制提供了自动故障恢复的功能,确保了集群数据不会丢失。

(7)使用高效的二进制数据存储,包括大型对象(如视频)。使用二进制格式存储,可以保存任何类型的数据对象。

(8)自动处理分片,以支持云计算层次的扩展。MongoDB支持集群自动切分数据,对数据进行分片可以使集群存储更多的数据,实现更大的负载,也能保证存储的负载均衡。

(9)支持Perl、PHP、Java、C#、JavaScript、Ruby、C和C++语言的驱动程序,MongoDB提供了当前所有主流开发语言的数据库驱动包,开发人员使用任何一种主流开发语言都可以轻松编程,实现MongoDB数据库的访问。

(10)文件存储格式为BSON(JSON的一种扩展)。BSON是二进制格式的JSON的简称,BSON支持文档和数组的嵌套。

(11)可以通过网络访问。可以通过网络远程访问MongoDB数据库。MongoDB的主要目标是通过键/值存储方式(提供了高性能和高度伸缩性)和传统的RDBMS系统之间架起一座桥梁,它集两者的优势于一身。

根据官方网站的描述,MongoDB适用于以下场景。

· 网站数据:MongoDB非常适合实时地插入、更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性。

· 缓存:由于性能很高,MongoDB也适合作为信息基础设施的缓存层。在系统重启之后,由MongoDB搭建的持久化缓存层可以避免下层的数据源过载。

· 大尺寸、低价值的数据:使用传统的关系数据库存储一些数据时可能会比较昂贵,在此之前,很多时候程序员往往会选择传统的文件进行存储。

· 高伸缩性的场景:MongoDB非常适合由数十或数百台服务器组成数据库集群,MongoDB的路线图中已经包含对MapReduce引擎的内置支持。

· 用于对象及JSON数据的存储:MongoDB的BSON数据格式非常适合文档化格式的存储及查询。

MongoDB的使用也会有一些限制,例如,它不适合于以下几种应用场景。

· 高度事务性的系统:例如,银行或会计系统。传统的关系数据库目前还是更适用于需要大量原子性复杂事务的应用程序。

· 传统的商业智能应用:针对特定问题的BI数据库会产生高度优化的查询方式。对于此类应用,数据仓库可能是更合适的选择。

· 需要SQL的场景。

三 MongoDB下载与安装

MongoDB目标安装环境为CentOS7,安装步骤如下。

(1)安装包下载。

从MongoDB官网()

下载Linux环境下的安装包MongoDB-linux-x86_64-rhel70-4.2.12.tgz。

(2)使用xftp上传安装包到/usr/local目录下,然后解压。

(3)在MongoDB目录下,创建data和logs文件夹。

(4)后台启动MongoDB服务(默认只允许本机客户端连接)。

(5)客户端连接服务器(如图6-1所示)。

图6-1 MongoDB服务启动与连接

(6)重新启动服务器,允许采用远程IP方式连接服务器。

MongoDB的启动参数如表6-1所示。

表6-1 MongoDB启动参数

表6-1 MongoDB启动参数

四 系统数据库与用户库

MongoDB中多个文档组成集合,多个集合组成数据库。一个MongoDB实例可以承载多个数据库,每个数据库相对独立,都有自己的权限控制。

在磁盘上,不同的数据库存放在不同的文件中。MongoDB中存在的系统数据库有:admin,local,config,用户还可以创建属于自己的数据库。

· admin数据库:一个权限数据库,如果创建用户的时候将该用户添加到admin数据库中,那么该用户就自动继承所有数据库的权限。

· local数据库:这个数据库永远不会被复制,可以用来存储本地单台服务器的任意集合。

· config数据库:当MongoDB使用分片模式时,config数据库在内部使用,用于保存分片的信息。

客户端连接MongoDB数据库后,可以创建属于自己的用户数据库(如图6-2所示)。

图6-2 系统数据库与用户数据库

五 权限管理

角色是身份的象征,同时也是权限的识别。MongoDB中内置了如下多种角色。

· read:允许用户读取指定数据库。

· readWrite:允许用户读写指定数据库。

· dbAdmin:允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问system.profile。

· userAdmin:允许用户向system.users集合写入,可以在指定数据库里创建、删除和管理用户。

· clusterAdmin:只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限。

· readAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读权限。

· readWriteAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的读写权限。

· userAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的userAdmin权限。

· dbAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限。

· root:只在admin数据库中可用。超级账号,超级权限。

作为普通用户,一般会拥有read和readWrite角色。而数据库管理员,会拥 有 dbAdmin 、 dbOwner 、 userAdmin 等 角 色 。 集 群 管 理 员 可 以 有clusterAdmin、clusterManager、clusterMonitor、hostManager等角色。备份管理员可以有backup、restore等角色。

创建用户时,需要给用户赋权,就是指定用户有何种角色。当然,可以允许一个用户同时拥有多种角色。注意:用户属于库,给某个数据库创建用户时,必须要先打开该数据库。

示例:在admin数据库下,创建系统管理员(如图6-3所示)。

图6-3 创建系统管理员

示例:在mydb库下,创建普通用户(如图6-4所示)。

图6-4 创建普通用户

示例:用户登录与权限控制,操作步骤如下。

(1)重新启动MongoDB开启用户权限校验模式:

(2)客户端远程访问MongoDB。

(3)开启授权模式后,打开admin数据库,只有使用管理员权限的用户才能查询数据库和用户信息(如图6-5所示)。

(4)打开mydb数据库,只有授权的普通用户才能操作(如图6-6所示)。注意:同一个客户端只能有一个用户登录,所以管理员与普通用户应该使用两个客户端。

图6-5 管理员登录

图6-6 普通用户登录

六 文档的CRUD操作

MongoDB是完全模式自由的,即类型完全不同的文档可以存放在同一个集合中。但是在实际开发中,同一个集合中的数据尽量存放相同类型的文档数据,这样更便于数据检索和数据维护。文档的CRUD(增删改查)操作示例如下。

(1)新建user集合,插入用户数据

(2)查询新建user集合与集合中的数据。

(3)使用循环向集合中插入数据。

(4)删除集合中复合条件的文档。

(5)修改文档。

(6)条件查询。

查询条件中在遇到特殊符号时,需要使用转义符,$lt表示<,$lte表示<=,$gt表示>,$gte表示>=,$ne表示!=;$in表示范围内,$nin表示不在范围内。

(7)删除集合。

七 内嵌文档

在关系数据库中,表与表之间存在着一对一、一对多、多对一、多对多等关系。通过表之间的关系,可以进行多表联合的SQL查询(外键关联)检索数据。

在MongoDB中集合模式是完全自由的,不存在集合与集合之间的关系。当出现如下场景时,如何存储数据呢?

一个用户有多个邮寄地址、一个用户有多个电话、一个用户有多种角色信息、一个用户有一个购物车、一个用户对应一个员工等。

MongoDB的解决方案是推荐使用内嵌文档来体现文档与文档之间的一对一和一对多的关系。

创建内嵌文档操作示例如下。

内嵌文档查询示例如下。

内嵌文档内容修改示例如下。

向内嵌文档中插入新数据的示例如下。

订单与订单明细内嵌文档操作步骤如下。

(1)创建两条新订单(订单明细为内嵌文档)。

(2)订单检索。

(3)添加订单明细(如图6-7所示)。

图6-7 添加订单明细

(4)删除一条订单明细。

八 索引

关系数据库如MySQL、Oracle,当记录数超过几万条时就需要考虑使用索引来提高检索速度。从数据结构上划分,MySQL支持B+树或哈希索引;从逻辑上划分,MySQL目前主要有以下几种索引类型:普通索引;唯一索引;主键索引;联合索引;全文索引。

从MySQL 5.5以后,InnoDB是默认引擎。在MySQL innoDB引擎中,主键索引又被称为聚簇索引(叶子数据区中含有所有真实的数据)。

Oracle中还有位图索引,它特定用于该列只有几个枚举值的情况,比如性别字段、省份字段、订单状态等字段都可以建立位图索引。

MongoDB支持多种类型的索引,如普通索引、联合索引、唯一索引、全文索引等,每种类型的索引有不同的使用场合。

创建普通索引示例(如图6-8所示)如下。

注意:集合中的文档都会有一个名为_id的默认键,该键会被自动创建索引。

删除索引示例如下。

创建唯一索引示例(如图6-9所示)如下。

创建联合索引示例(如图6-10所示)如下。

图6-8 集合中的普通索引

图6-9 唯一索引

图6-10 联合索引

创建全文索引示例如下。

注意:MongoDB对于中文的分词搜索支持得不好,请谨慎使用。

(1)新增3个用户,用户名中间有空格。

(2)创建全文索引。

(3)使用全文索引进行分词查询(如图6-11所示)。

图6-11 全文索引进行分词查询

九 查询分析

在MongoDB进行条件查询时,调用explain()方法,即可进行查询分析,如执行时间、是否使用了索引、使用的是何种索引等信息都可以显示。

示例1(查询计划):使用uname条件进行检索分析,可以看到"stage" :"IXSCAN",即使用了索引扫描。

示例2(执行状态分析):在示例1的基础上,增加executionStats参数,会增加执行状态信息。

下面是对查询分析中的几个关键参数的解释。

(1)explain最为直观的返回值是executionTimeMillis,指的是这条语句的执行时间,这个值希望越小越好。

(2)nReturned为返回的条目、totalKeysExamined为索引扫描条目、totalDocsExamined为文档扫描条目,文档扫描数量当然也是越小越好,查询的理想的状态是:nReturned=totalKeysExamined=totalDocsExamined。

(3)stage状态分析:查询中应尽量使用索引,如FETCH+IDHACK、FETCH+IXSCAN 、 LIMIT+ ( FETCH +IXSCAN ) 、 PROJECTION+IXSCAN 、SHARDING_FITER+IXSCAN、COUNT_SCAN。尽量不要出现COLLSCAN,全表扫描严重影响查询性能,尤其是在MongoDB中数据量十分庞大时,合理使用索引进行查询,性能会有非常显著的提升。

COLLSCAN:全表扫描;IXSCAN:索引扫描;FETCH:根据索引去检索指定document;SHARD_MERGE:将各个分片返回数据进行merge;SORT:表明在内存中进行了排序;LIMIT:使用limit限制返回数;SKIP:使用skip进行跳过;IDHACK:针对_id进行查询;SHARDING_FILTER:通过MongoDB对分片数据进行查询;COUNT:利用db.coll.explain().count()之类进行count运算;

COUNTSCAN:count不使用Index进行count时的stage返回;COUNT_SCAN:count使用了Index进行count时的stage返回;SUBPLA:未使用索引的$or查询的stage返回;TEXT:使用全文索引进行查询时的stage返回;PROJECTION:限定返回字段时的stage返回。

十 案例:新浪微博MongoDB实战

微博系统(如新浪微博),以用户发布博文为核心,然后会产生评论、点赞、关注、收藏、转发等相关操作(如图6-12所示)。

1 微博项目分析

新浪微博用户数量庞大,如果采用传统的开发模式,可以用关系数据库存储微博数据,微博系统表设计如图6-13所示。但是,由于关系数据库的Connection连接数量非常有限,而且CRUD(增删改查)操作受事务影响,因此当用户数量庞大时,性能会非常低。

图6-12 新浪微博

图6-13 微博系统表设计

如果采用MongoDB替代MySQL或Oracle,很多问题会迎刃而解。首先,MongoDB的数据库连接数单机可以达到几十万,远远超过MySQL的连接数;其次,MongoDB没有事务控制,数据库读写性能大幅提升;再有,当用户数量庞大时,MongoDB集群非常容易扩展,使用MongoDB集群的数据承载量远远大于MySQL数据库集群。

2 Java连接MongoDB

MongoDB的驱动下载,参见github地址

(如图6-14所示)。

图6-14 MongoDB驱动下载

使用Maven下载MongoDB驱动,按如下进行配置(如图6-15所示)。

图6-15 MongoDB的驱动依赖

Java客户端使用驱动,获取MongoDB数据库连接的方式如下。

获取MongoDB数据库对象:

获取MongoDB集合:

多个key/value组成的字符串,称为BSON对象:

获取文档对象:

3 微博项目代码实现

1.系统初始化脚本

微博系统的用户与博文数据作为初始化脚本写入,Java代码实现暂不考虑。

2.MongoDB访问封装

使用工具类MongoFactory管理MongoDB数据库的打开与关闭操作。

3.添加关注

“我的关注”是指登录用户对感兴趣的博主进行关注,关注人与被关注人都必须是系统的注册用户(如图6-13所示表设计),操作步骤如下。

(1)采用逻辑类UserBiz中addConcern()方法添加关注。

(2)持久层类UserDao中实现向MongoDB中添加关注信息。

(3)UI层添加关注。

4.我关注的人员列表

读取当前登录用户“我的关注”中的人员的信息列表,操作步骤如下。

(1)逻辑层UserBiz中getMyConcern()方法。

(2)持久层UserDao中getMyConcern()方法。

(3)UI层调用逻辑方法,显示“我的关注”的人员列表。

5.取消关注

从“我的关注”人员列表中移除某个人员,即为取消关注,操作步骤如下。

(1)逻辑层UserBiz中的removeConcern()方法。

(2)持久层UserDao中的removeConcern()方法。

(3)UI层调用逻辑方法,实现取消关注的功能。

6.添加博文收藏

登录用户在浏览博文时,可以对于感兴趣的博文进行收藏操作。从“我的收藏”中可以提取用户收藏的所有博文列表。

如图6-13所示的表设计,收藏博文表有两个外键,即用户名和博文id必须存在,才能成功进行博文收藏。收藏博文表是两个一对多的体现,即一个用户可以收藏多篇博文;一篇博文可以被多个用户同时收藏。

在MongoDB的用户集合中写入内嵌文档,“我的收藏”作为用户集合的内嵌文档进行存储(如图6-16所示)。

图6-16 内嵌文档存储“我的收藏”

(1)逻辑层UserBiz中collectWeibo()方法。博文收藏成功给博文的收藏数量加1。

(2)持久层UserDao中collectWeibo()方法。向用户集合的内嵌文档写入数据,如果找不到用户名文档,则写入失败。

(3)持久层WeiboDao中incCollectNum()方法。修改博文的收藏数量加1。

(4)UI层调用逻辑类,实现博文收藏。

7.取消博文收藏

(1)逻辑层UserBiz的collectWeiboCancel()方法。

(2)持久层UserDao中的collectWeiboCancel()方法。修改内嵌文档,移除指定条件的一条收藏数据。

(3)持久层WeiboDao中给博文收藏数量减1。

(4)UI层调用逻辑对象,实现取消收藏(如图6-17所示)。

图6-17 取消收藏

标签: #oraclegte