NoSQL经典详解

第一章 关系型数据库

  传统的关系数据库具有不错的性能,高稳定性,久经历史考验,而且使用简单,功能强大,同时也积累了大量的成功案例。常用的数据库软件有Mysql、Oracle、SQL Server等。在互联网领域,MySQL应用广泛,为互联网的发展做出了卓越的贡献。

  

关系型数据库作为应用广泛的通用型数据库,它的突出优势主要有以下几点:   

  • 保持数据的一致性(事务处理)  acid原子性 一致性 隔离性 持久性
  • 由于以标准化为前提,数据更新的开销很小(相同的字段基本上都只有一处)
  • 可以进行JOIN等复杂查询:在Join操作中,分别为Join(Join查询), SelectMany(Select一对多选择)和GroupJoin(分组Join查询)。

要熟练的掌握Join的特性。两个表连接方式主要分为内连接(inner join)和外连接(outer join)。外连接又分为三种,left outer join、right outer join和full outer join。存在很多实际成果和专业技术信息(成熟的技术)‚

在90年代,一个网站的访问量一般都不大,用单个数据库完全可以轻松应付。在那个时候,更多的都是静态网页,动态交互类型的网站不多。

Android Realm 联表查询 nosql联表查询_Android Realm 联表查询

到了2000年以后,网站开始快速发展。论坛、博客、sns、微博逐渐引领web领域的潮流。

Android Realm 联表查询 nosql联表查询_数据库_02

随着访问量的上升,几乎大部分使用MySQL架构的网站在数据库上都开始出现了性能问题,这时候web程序不再仅仅专注在功能上,同时也在追求性能。程序员们开始大量的使用缓存技术来缓解数据库的压力,优化数据库的结构和索引。开始比较流行的是通过文件缓存来缓解数据库压力,但是当访问量继续增大的时候,多台web机器通过文件缓存不能共享,大量的小文件缓存也带了了比较高的IO压力。在这个时候,Memcached就自然的成为一个非常时尚的技术产品。Memcached作为一个独立的分布式的缓存服务器,为多个web服务器提供了一个共享的高性能缓存服务。

Android Realm 联表查询 nosql联表查询_Android Realm 联表查询_03

 

缓存的实质是替数据库挡了一层。频繁被访问的数据可以被放置于缓存当中,以供频繁访问。

Mysql主从读写分离

由于数据库的写入压力增加,Memcached只能缓解数据库的读取压力。读写集中在一个数据库上让数据库不堪重负,大部分网站开始使用主从复制技术来达到读写分离,以提高读写性能和读库的可扩展性。Mysql的master-slave模式被广泛应用。

什么是主从复制?

   多增加几个数据库来存储数据,假设有三台数据库,一主二从,即一台主服务器,两台从服务器,当新增数据至主数据库服务器的时候,那么同时复制此数据进入到从数据库服务器当中。数据复制是为了容灾备份,缓存备份,保证数据的完整性。

整体上来说,复制有3个步骤:   

       (1)    master将改变记录到二进制日志(binary log)中(这些记录叫做二进制日志事件,binary log events);
       (2)    slave将master的binary log events拷贝到它的中继日志(relay log);
        (3)    slave重做中继日志中的事件,将改变反映它自己的数据。

下图描述了复制的过程:

Android Realm 联表查询 nosql联表查询_Android Realm 联表查询_04

 

什么是读写分离?

   增删改是写,查为读。

   分工明确,结合缓存能实现性能的一大提升

 

Android Realm 联表查询 nosql联表查询_数据库_05

Android Realm 联表查询 nosql联表查询_关系型数据库_06

 

其中的M是master 即主DB Server ,S为slaver即从属DB Server,各有分工。

写操作在 M,读操作在S。S的数据是从master复制的。

与此同时,开始流行使用分库分表来缓解写压力和数据增长的扩展问题。这个时候,MySQL推出了还不太稳定的表分区,这也给技术实力一般的公司带来了希望。虽然MySQL推出了MySQL cluster集群,但性能也不能很好的满足互联网的要求,只是在高可靠性上提供了非常大的保证。

1 基本思想之什么是分库分表?

   从字面上简单理解,就是把原本存储于一个库的数据分块存储到多个库上,把原本存储于一个表的数据分块存储到多个表上。

2 基本思想之为什么要分库分表?

   分库分表主要是因为数据库中的数据量不一定是可控的,在未进行分库分表的情况下,随着时间和业务的发展,库中的表会越来越多,表中的数据量也会越来越大,相应地,数据操作,增删改查的开销也会越来越大;另外,由于无法进行分布式部署,而一台服务器的资源(CPU、磁盘、内存、IO等)是有限的,最终数据库所能承载的数据量、数据处理能力都将遭遇瓶颈。

3 分库分表的实施策略。

分库分表有垂直切分和水平切分两种。

3.1 何谓垂直切分:

   即将表按照功能模块、关系密切程度划分出来,部署到不同的库上。例如,我们会建立定义数据库workDB、商品数据库payDB、用户数据库userDB、日志数据库logDB等,分别用于存储项目数据定义表、商品定义表、用户数据表、日志数据表等。

3.2 何谓水平切分:

   当一个表中的数据量过大时,我们可以把该表的数据按照某种规则,例如userID散列,进行划分,然后存储到多个结构相同的表,和不同的库上。例如,我们的userDB中的用户数据表中,每一个表的数据量都很大,就可以把userDB切分为结构相同的多个userDB:part0DB、part1DB等,再将userDB上的用户数据表userTable,切分为很多userTable:userTable0、userTable1等,然后将这些表按照一定的规则存储到多个userDB上。

Android Realm 联表查询 nosql联表查询_数据_07

 

MySQL的扩展性瓶颈

分库分表的规则把握都是需要经验的,而且分库分表的子库到一定阶段又面临扩展问题。还有就是需求的变更,可能又需要一种新的分库方式。

分库分表缺点:

(1)受业务规则影响,需求变动导致分库分表的维护复杂

(2)系统数据访问层代码需要修改

Master-Slave缺点

(1)Slave实时性的保障,对于实时性很高的场合可能需要做一些处理

(2)高可用性问题,Master就是那个致命点,容易产生单点故障

关系数据库很强大,但是它并不能很好的应付所有的应用场景。所以这个时候NoSQL的应用就显得格外重要。

第二章 为什么要是有NoSQL

  NoSQL概念在2009年被提了出来。NoSQL最常见的解释是“non-relational”,“Not Only SQL”也被很多人接受。(“NoSQL”一词最早于1998年被用于一个轻量级的关系数据库的名字。)关系型数据库应用广泛,能进行事务(acid特性)处理和表连接等复杂查询。相对地,NoSQL数据库只应用在特定领域,基本上不进行复杂的处理,但它恰恰弥补了关系型数据库的不足之处。

Android Realm 联表查询 nosql联表查询_关系型数据库_08

 

  各个数据之间存在关联是关系型数据库得名的主要原因,为了进行join处理,关系型数据库不得不把数据存储在同一个服务器内,这不利于数据的分散,这也是关系型数据库并不擅长大数据量的写入处理的原因。相反NoSQL数据库原本就不支持Join处理,各个数据都是独立设计的,很容易把数据分散在多个服务器上,故减少了每个服务器上的数据量,即使要处理大量数据的写入,也变得更加容易,数据的读入操作也很容易。

例如:谷歌和Facebook每天为他们的用户收集万亿比特的数据。这些数据的存储不需要固定的模式,无需多余的操作就可以横向扩展。

 

CAP,BASE和最终一致性是NoSQL数据库存在的三大基石。

CAP理论

Android Realm 联表查询 nosql联表查询_Android Realm 联表查询_09

 

1. 一致性(Consistency):任何一个读操作总是能读取到之前完成的写操作结果,也就是在分布式环境中,多点的数据是一致的; , 数据一致更新,所有数据变动都是同步的 ,客户能够感知到一系列操作会一下子执行完毕。

即更新操作成功并返回客户端完成后,所有节点在同一时间的数据完全一致。

2. 可用性(Availability):每一个操作总是能够在确定的时间内返回,也就是系统随时都是可用的。 好的响应性能 ,每一个操作必须有意想中的响应并终止。

即服务一直可用,而且是正常响应时间。

3. 分区容忍性(Partition Tolerance): 在出现网络分区(比如断网)的情况下,分离的系统也能正常运行。具有很高的可靠性 ,操作均能够执行完毕,即使个别组件不可用。

即分布式系统在遇到某节点或网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务

 

 注意:可用性与分区容忍性在一些情况下很容易混淆。举个例子,假设系统中有若干个节点宕机了,系统仍然能正常运行,那么应该说是系统的可用性高还是分区容忍性高呢?个人的理解是,这种应该理解为系统的分区容忍性高。因为若干个节点宕机,可以理解为这几个节点与其它正常的节点失去联系了,也就是出现了网络分区,按照定义,这属于分区容忍性的范畴。那么可用性是什么?个人的理解可用性更多强调的是,系统对于读写操作的反应快慢,反应越快,可用性越高。

CAP原理的意思是,一个分布式系统不能同时满足一致性,可用性和分区容忍性这三个需求,最多只能同时满足两个。所以大多数NoSQL系统都会根据自己的设计理念来进行相应的选择,但由于许多NoSQL数据库都以水平扩展著称,所以在CAP的选择上面,都倾向于坚持分区容忍性,而放弃一致性或者可用性,它们的做法主要是消减关系型和事务相关的功能。

 

拓展:通过CAP理论,我们知道无法同时满足一致性、可用性和分区容错性这三个特性,那要舍弃哪个呢?

对于多数大型互联网应用的场景,主机众多、部署分散,而且现在的集群规模越来越大,所以节点故障、网络故障是常态,而且要保证服务可用性达到N个9,即保证P和A,舍弃C(退而求其次保证最终一致性)。虽然某些地方会影响客户体验,但没达到造成用户流程的严重程度。

对于涉及到钱财这样不能有一丝让步的场景,C必须保证。网络发生故障宁可停止服务,这是保证CA,舍弃P。貌似这几年国内银行业发生了不下10起事故,但影响面不大,报到也不多,广大群众知道的少。还有一种是保证CP,舍弃A。例如网络故障事只读不写。

孰优孰略,没有定论,只能根据场景定夺,适合的才是最好的。

 

BASE理论

BASE理论是对CAP理论的延伸,核心思想是即使无法做到强一致性(Strong Consistency,CAP的一致性就是强一致性),但应用可以采用适合的方式达到最终一致性(Eventual Consitency)。

 

关系数据库的ACID模型拥有 高一致性 + 可用性 很难进行分区:

 

Android Realm 联表查询 nosql联表查询_数据库_10

(1) 原子性(Atomicity)

事务的原子性指的是,事务中包含的程序作为数据库的逻辑工作单位,它所做的对数据修改操作要么全部执行,要么完全不执行。这种特性称为原子性。

例如银行取款事务分为2个步骤(1)存折减款(2)提取现金。不可能存折减款,却没有提取现金。2个步骤必须同时完成或者都不完成。

(2)一致性(Consistency)    

事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。这种特性称为事务的一致性。假如数据库的状态满足所有的完整性约束,就说该数据库是一致的。

例如完整性约束a+b=10,一个事务改变了a,那么b也应随之改变。

(3)隔离性(亦称独立性Isolation)

隔离性指并发的事务是相互隔离的。即一个事务内部的操作及正在操作的数据必须封锁起来,不被其它企图进行修改的事务看到。假如并发交叉执行的事务没有任何控制,操纵相同的共享对象的多个并发事务的执行可能引起异常情况。

(4)持久性(Durability)

持久性意味着当系统或介质发生故障时,确保已提交事务的更新不能丢失。即一旦一个事务提交,DBMS保证它对数据库中数据的改变应该是永久性的,即对已提交事务的更新能恢复。持久性通过数据库备份和恢复来保证。

 

BASE模型反ACID模型,完全不同ACID模型,牺牲高一致性,获得可用性:

Android Realm 联表查询 nosql联表查询_数据库_11

Basically Available基本可用。

基本可用是指分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用。

电商大促时,为了应对访问量激增,部分用户可能会被引导到降级页面,服务层也可能只提供降级服务。这就是损失部分可用性的体现。

经历过12306抢票的人应该经常会遇到这个问题:在抢票高峰的时候,明明票还有,但是查询出来的列表却是为空的(如果没票列表也应该会呈现);等高峰过后再查询,列表又恢复正常。个人猜测应该是查询过程中出现了问题,要么超时,要么网络问题导致查询失败采用的服务降级处理。所以,最终呈现给用户的并不是内部系统出错之类的提示,而是一个空的列表。

Soft state软状态 状态可以有一段时间不同步,异步。

软状态是指允许系统存在中间状态,而该中间状态不会影响系统整体可用性。分布式存储中一般一份数据至少会有三个副本,允许不同节点间副本同步的延时就是软状态的体现。mysql replication的异步复制也是一种体现。

对关系型数据库来说,插入一条数据后立刻查询,是肯定可以读出来这条数据的,但是对于很多Web应用而言,并不要求这么高的实时性,比方说我发一条微博之后,过几秒乃至十几秒后,别人才提示有新微博,这是完全可以的。

Eventually consistent最终一致,最终数据是一致的就可以了,而不是时时高一致。

最终一致性是指系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。弱一致性和强一致性相反,最终一致性是弱一致性的一种特殊情况。

 

NoSQL的优势:

易扩展

NoSQL数据库种类繁多,但是一个共同的特点都是去掉关系数据库的关系型特性。数据之间无关系,这样就非常容易扩展。也无形之间,在架构的层面上带来了可扩展的能力。

大数据量,高性能

NoSQL数据库都具有非常高的读写性能,尤其在大数据量下,同样表现优秀。这得益于它的无关系性,数据库的结构简单。一般MySQL使用Query Cache,每次表的更新Cache就失效,是一种大粒度的Cache(大粒度锁:一个url获得了锁,其他的url都不能获得),在针对web2.0的交互频繁的应用,Cache性能不高。而NoSQL的Cache是记录级的,是一种细粒度的Cache,所以NoSQL在这个层面上来说就要性能高很多了。

灵活的数据模型

NoSQL无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式。而在关系数据库里,增删字段是一件非常麻烦的事情。如果是非常大数据量的表,增加字段简直就是一个噩梦。这点在大数据量的web2.0时代尤其明显。

高可用

NoSQL在不太影响性能的情况,就可以方便的实现高可用的架构。

 

第三章 NoSQL数据库

 

列存储

Hbase

Cassandra

Hypertable

顾名思义,是按列存储数据的。最大的特点是方便存储结构化和半结构化数据,方便做数据压缩,对针对某一列或者某几列的查询有非常大的IO优势。

文档存储

MongoDB

CouchDB

文档存储一般用类似json的格式存储,存储的内容是文档型的。这样也就有机会对某些字段建立索引,实现关系数据库的某些功能。

key-value存储

Tokyo Cabinet / Tyrant

Berkeley DB

MemcacheDB

Redis

可以通过key快速查询到其value。一般来说,存储不管value的格式,照单全收。(Redis包含了其他功能)

图存储

Neo4J

FlockDB

InfoGrid

图形关系的最佳存储。使用传统关系数据库来解决的话性能低下,而且设计使用不方便。

对象存储

db4o

Versant

通过类似面向对象语言的语法操作数据库,通过对象的方式存取数据。

xml数据库

Berkeley DB XML

BaseX

高效的存储XML数据,并支持XML的内部查询语法,比如XQuery,Xpath。

 

面向列的数据库

   Cassandra、HBase、HyperTable属于这种类型。

   普通的关系型数据库都是以行为单位来存储数据的,擅长以行为单位的读入处理,比如特定条件数据的获取。因此,关系型数据库也被称为面向行的数据库。相反,面向列的数据库是以列为单位来存储数据的,擅长以列为单位读入数据。

面向列的数据库具有高扩展性,即使数据增加也不会降低相应的处理速度(特别是写入速度),所以它主要应用于需要处理大量数据的情况。另外,把它作为批处理程序的存储器来对大量数据进行更新也是非常有用的。但由于面向列的数据库跟现行数据库存储的思维方式有很大不同,故应用起来十分困难。

 

面向文档的数据库

   MongoDB、CouchDB属于这种类型,它们属于NoSQL数据库,但与键值存储相异。

   (1)不定义表结构

     即使不定义表结构,也可以像定义了表结构一样使用,还省去了变更表结构的麻烦。

   (2)可以使用复杂的查询条件 

     跟键值存储不同的是,面向文档的数据库可以通过复杂的查询条件来获取数据,虽然不具备事务处理和Join这些关系型数据库所具有的处理能力,但除此以外的其他处理基本上都能实现。

键值存储

 

键值存储

它的数据是以键值的形式存储的,虽然它的速度非常快,但基本上只能通过键的完全一致查询获取数据,根据数据的保存方式可以分为临时性、永久性和两者兼具 三种。

(1)临时性

  所谓临时性就是数据有可能丢失,memcached把所有数据都保存在内存中,这样保存和读取的速度非常快,但是当memcached停止时,数据就不存在了。由于数据保存在内存中,所以无法操作超出内存容量的数据,旧数据会丢失。总结来说:

  。在内存中保存数据

  。可以进行非常快速的保存和读取处理

  。数据有可能丢失

 (2)永久性

   所谓永久性就是数据不会丢失,这里的键值存储是把数据保存在硬盘上,与临时性比起来,由于必然要发生对硬盘的IO操作,所以性能上还是有差距的,但数据不会丢失是它最大的优势。总结来说:

  。在硬盘上保存数据

。可以进行非常快速的保存和读取处理(但无法与memcached相比)

  。数据不会丢失

(3) 两者兼备

  Redis属于这种类型。Redis有些特殊,临时性和永久性兼具。Redis首先把数据保存在内存中,在满足特定条件(默认是 15分钟一次以上,5分钟内10个以上,1分钟内10000个以上的键发生变更)的时候将数据写入到硬盘中,这样既确保了内存中数据的处理速度,又可以通过写入硬盘来保证数据的永久性,这种类型的数据库特别适合处理数组类型的数据。总结来说:

  。同时在内存和硬盘上保存数据

  。可以进行非常快速的保存和读取处理

  。保存在硬盘上的数据不会消失(可以恢复)

  。适合于处理数组类型的数据

 NoSQL数据库面临的挑战

1.成熟度

RDBMS系统由来已久。NoSQL拥护者们会说RDBMS的高龄是其衰退的标志,不过对于大多数CIO来说,RDBMS的成熟让人放心。对于大多数情况来说,RDBMS系统是稳定且功能丰富的。相比较而言,大多数NoSQL数据库则还有很多特性有待实现。

2.支持

企业需要的是安心,如果关键系统出现了故障,他们可以获得即时的支持。所有RDBMS厂商都在不遗余力地提供良好的企业支持。与之相反,大多数NoSQL系统都是开源项目,虽然每种数据库都有那么几家公司提供支持,不过这些公司大多都是小的初创公司,没有全球支持资源,也没有Oracle、微软或是IBM那种令人放心的公信力。

3.分析与商业智能

NoSQL数据库在Web 2.0应用时代开始出现。因此,大多数特性都是面向这些应用的需要的。然而,应用中的数据对于业务来说是有价值的,这种价值远远超出了Web应用那种CRUD。企业数据库中的业务信息可以帮助改进效率并提升竞争力,商业智能对于大中型企业来说是个非常关键的IT问题。

4.管理

大多数NoSQL系统都是开源项目,虽然每种数据库都有那么几家公司提供支持,不过这些公司大多都是小的初创公司,没有全球支持资源,也没有Oracle、微软或是IBM那种令人放心的公信力。

5.专业

全球有很多开发者,每个业务部门都会有熟悉RDBMS概念与编程的人。相反,几乎每个NoSQL开发者都处于学习模式。这种状况会随着时间的流逝而发生改观。但现在,找到一个有经验的RDBMS程序员或是管理员要比NoSQL专家容易多了。 

 

NoSQL还是关系型数据库

Android Realm 联表查询 nosql联表查询_关系型数据库_12

 

 NoSQL应用场景

♣ 频繁的写入操作,相对较少的读取统计信息的操作(比如一个web访问计数器)应该使用基于内存的key/value存储系统,比如Redis,或者是具备本地更新特性的文档存储系统,如MongoDB。

♣ 海量数据(比如数据仓库中需要分析的数据)适合与存储在一个schmaless,分布式的文件存储系统中,如Hadoop。

♣ 存储二进制文件(比如mp3或者pdf文档)并且能够直接为用户的浏览器提供下载功能,可以使用Amazon S3。

♣ 临时性的数据(比如网站的session,缓存HTML页面信息,等等)适合存储在Memcache。

 

们可以在一个网站中使用下面四款数据产品来提供服务:

Android Realm 联表查询 nosql联表查询_Android Realm 联表查询_13

MySQL用于存储敏感的数据,比如用户的资料,交易的信息等等。

MongoDB用于存储大量的,相对不敏感的数据,比如博客文章的内容,文章访问次数等等。

Amazon S3用于存储用户上传的文档,图片,音乐等等数据。

Memcached用于存储临时性的信息,比如缓存HTML页面等。

 

总结

NoSQL数据库的出现,弥补了关系数据(比如MySQL)在某些方面的不足,在某些方面能极大的节省开发成本和维护成本。

关系数据(比如MySQL)和NoSQL都有各自的特点和使用的应用场景,两者的紧密结合将会给web2.0的数据库发展带来新的思路。让关系数据库关注在关系上,NoSQL关注在存储上。