综合实习报告文献综述类1221149Word文件下载.docx
《综合实习报告文献综述类1221149Word文件下载.docx》由会员分享,可在线阅读,更多相关《综合实习报告文献综述类1221149Word文件下载.docx(12页珍藏版)》请在冰豆网上搜索。
目前国内的大型互联网应用中,大量的采用了这样的数据切分方案,Taobao,Alibaba,Tencent,它们大都实现了自己的分布式数据访问层(DDAL)。
以实现方式和实现的层次来划分,大概分为两个层次(Java应用为例):
JDBC层的封装,ORM框架层的实现。
就JDBC层的直接封装而言,现在国内发展较好的一个项目是被称作"变形虫"(Amoeba)的项目,由阿里集团的研究院开发,现在仍然处于测试阶段(beta版),其运行效率和生产时效性有待考究。
就ORM框架层的实现而言,比如Taobao的基于ibatis和Spring的的分布式数据访问层,已有多年的应用,运行效率和生产实效性得到了开发人员和用户的肯定。
1.3研究的基本轮廓
人类认知问题的过程总是这样的:
how怎么做,why为什么what什么。
以这样的方式阐述了数据库切分的一些概念和意义以及对一些常规的切分规则做了概要的介绍。
本课题所讨论的分布数据层并不仅仅如此,它是一个完整的数据层解决方案,它到底是什么样的呢?
接下来的文字,我将详细阐述本研究课题的完整思想和实现方式。
分布式数据方案提供功能如下:
(1)提供分库规则和路由规则(RouteRule简称RR),将三种切分规则直接内嵌入本系统;
(2)引入集群(Group)的概念,保证数据的高可用性;
(3)引入负载均衡策略(LoadBalancePolicy简称LB);
(4)引入集群节点可用性探测机制,对单点机器的可用性进行定时的侦测,以保证LB策略的正确实施,以确保系统的高度稳定性;
(5)引入读/写分离,提高数据的查询速度;
仅仅是分库分表的数据层设计也是不够完善的,当某个节点上的DB服务器出现了宕机的情况的时候,会是什么样的呢?
是的,我们采用了数据库切分方案,也就是说有N太机器组成了一个完整的DB,如果有一台机器宕机的话,也仅仅是一个DB的N分之一的数据不能访问而已,这是我们能接受的,起码比切分之前的情况好很多了,总不至于整个DB都不能访问。
一般的应用中,这样的机器故障导致的数据无法访问是可以接受的,假设我们的系统是一个高并发的电子商务网站呢?
单节点机器宕机带来的经济损失是非常严重的。
也就是说,现在我们这样的方案还是存在问题的,容错性能是经不起考验的。
当然了,问题总是有解决方案的。
我们引入集群的概念,在此我称之为Group,也就是每一个分库的节点我们引入多台机器,每台机器保存的数据是一样的,一般情况下这多台机器分摊负载,当出现宕机情况,负载均衡器将分配负载给这台宕机的机器。
这样一来,就解决了容错性的问题。
所以我们引入了集群的概念,并将其内嵌入我们的框架中,成为框架的一部分。
图2-1集群和读写分离的模型
如图2-1所示,整个数据层有Group1,Group2,Group3三个集群组成,这三个集群就是数据水平切分的结果,当然这三个集群也就组成了一个包含完整数据的DB。
每一个Group包括1个Master(当然Master也可以是多个)和N个Slave,这些Master和Slave的数据是一致的。
比如Group1中的一个slave发生了宕机现象,那么还有两个slave是可以用的,这样的模型总是不会造成某部分数据不能访问的问题,除非整个Group里的机器全部宕掉,但是考虑到这样的事情发生的概率非常小(除非是断电了,否则不易发生吧)。
在这个确定的DB内进行数据操作。
这是没有引入集群的情况,当时引入集群会是什么样子的呢?
看图即可得知,我们的路由器上规则和策略其实只能路由到具体的Group,也就是只能路由到一个虚拟的Group,这个Group并不是某个特定的物理服务器。
接下来需要做的工作就是找到具体的物理的DB服务器,以进行具体的数据操作。
基于这个环节的需求,我们引入了负载均衡器的概念(LB)。
负载均衡器的职责就是定位到一台具体的DB服务器。
具体的规则如下:
负载均衡器会分析当前sql的读写特性,如果是写操作或者是要求实时性很强的操作的话,直接将查询负载分到Master,如果是读操作则通过负载均衡策略分配一个Slave。
我们的负载均衡器的主要研究方向也就是负载分发策略,通常情况下负载均衡包括随机负载均衡和加权负载均衡。
随机负载均衡很好理解,就是从N个Slave中随机选取一个Slave。
这样的随机负载均衡是不考虑机器性能的,它默认为每台机器的性能是一样的。
假如真实的情况是这样的,这样做也是无可厚非的。
假如实际情况并非如此呢?
每个Slave的机器物理性能和配置不一样的情况,再使用随机的不考虑性能的负载均衡,是非常不科学的,这样一来会给机器性能差的机器带来不必要的高负载,甚至带来宕机的危险,同时高性能的数据库服务器也不能充分发挥其物理性能。
基于此考虑从,我们引入了加权负载均衡,也就是在我们的系统内部通过一定的接口,可以给每台DB服务器分配一个权值,然后再运行时LB根据权值在集群中的比重,分配一定比例的负载给该DB服务器。
当然这样的概念的引入,无疑增大了系统的复杂性和可维护性。
有得必有失,我们也没有办法逃过的。
数据层根据区分字段Route到具体的DB在没有引入集群以前,我们的一次查询的过程大致如下:
请求数据层,并传递必要的分库区分字段(通常情况下是user_id)
有了分库,有了集群,有了负载均衡器,是不是就万事大吉了呢?
事情远没有我们想象的那么简单。
虽然有了这些东西,基本上能保证我们的数据层可以承受很大的压力,但是这样的设计并不能完全规避数据库宕机的危害。
假如Group1中的slave2宕机了,那么系统的LB并不能得知,这样的话其实是很危险的,因为LB不知道,它还会以为slave2为可用状态,所以还是会给slave2分配负载。
这样一来,问题就出来了,客户端很自然的就会发生数据操作失败的错误或者异常。
这样是非常不友好的!
怎样解决这样的问题呢?
我们引入集群节点的可用性探测机制,或者是可用性的数据推送机制。
这两种机制有什么不同呢?
首先说探测机制吧,顾名思义,探测即使,就是我的数据层客户端,不定时对集群中各个数据库进行可用性的尝试,实现原理就是尝试性链接,或者数据库端口的尝试性访问,都可以做到,当然也可以用JDBC尝试性链接,利用Java的Exception机制进行可用性的判断,具体的会在后面的文字中提到。
那数据推送机制又是什么呢?
其实这个就要放在现实的应用场景中来讨论这个问题了,一般情况下应用的DB数据库宕机的话我相信DBA肯定是知道的,这个时候DBA手动的将数据库的当前状态通过程序的方式推送到客户端,也就是分布式数据层的应用端,这个时候在更新一个本地的DB状态的列表。
并告知LB,这个数据库节点不能使用,请不要给它分配负载。
一个是主动的监听机制,一个是被动的被告知的机制。
两者各有所长。
但是都可以达到同样的效果。
这样一来刚才假设的问题就不会发生了,即使就是发生了,那么发生的概率也会降到最低。
上面的文字中提到的Master和Slave,我们并没有做太多深入的讲解。
如图一所示,一个Group由1个Master和N个Slave组成。
为什么这么做呢?
其中Master负责写操作的负载,也就是说一切写的操作都在Master上进行,而读的操作则分摊到Slave上进行。
这样一来的可以大大提高读取的效率。
在一般的互联网应用中,经过一些数据调查得出结论,读/写的比例大概在10:
1左右,也就是说大量的数据操作是集中在读的操作,这也就是为什么我们会有多个Slave的原因。
但是为什么要分离读和写呢?
熟悉DB的研发人员都知道,写操作涉及到锁的问题,不管是行锁还是表锁还是块锁,都是比较降低系统执行效率的事情。
我们这样的分离是把写操作集中在一个节点上,而读操作其其他的N个节点上进行,从另一个方面有效的提高了读的效率,保证了系统的高可用性。
读写分离也会引入新的问题,比如我的Master上的数据怎样和集群中其他的Slave机器保持数据的同步和一致呢?
这个是我们不需要过多的关注的问题,MySql的Proxy机制可以帮助我们做到这点,由于Proxy机制与本课题相关性不是太强,在这里不做详细介绍。
2开发技术及理论基础
2.1数据库分割技术
数据库分割技术就是研究如何把数据物理地组织到存贮设备上。
对于关系型数据库来说共有六种分割技术:
水平分割、分组水平分割,单一垂直分割,物理垂直分割,分组垂直分割,混合分割。
水平分割是把一个关系细分成若干元组组成的组,不考虑元组之间的相互关系,每一组包含一些完整的元组。
由于这种分割方法比较简单,所以通常被作为一种常规的存贮结构。
分组水平分割技术是把一个关系依据元组之间的相互联系分成若干组。
通常在一起使用的元组放在同一组中。
这种技术多用于分布式数据库系统中。
单一垂直分割着重单一属性,而不考虑属性之间的相互联系,它只是把一个关系垂直地细分成若干组,每一组只包含一种类型的属性。
在物理垂直分割技术中,每个元组被分成大小固定的物理组,组的大小与属性长度无关,有些属性可以调整成几组。
分组垂直分割是把一个关系的属性分割成相互有关系的组,通常需要一起使用的属性物理地存贮在一起。
混合分割技术就是综合利用垂直分割和水平分割技术。
2.2不同分割技术的存贮方式
上面提出了六种分割方式,下面举例说明不同的分割技术的存贮情况。
设有一个关系有10个元组,每个元组有3个属性即姓名(15字节),电话(10字节),地址(50字节),物理页大小为750字节。
用水平分割技术,每页存放10个元组,需10个物理页来存贮这个关系。
元组l~元组10在第一页,元组1~元组20放在第二页,以此类推。
用分组水平分割技术,至少需要10页,但元组的顺序不同于水平分割,为表明元组之间的相互关系,一般需要10页以上的空间。
单一垂直分割需要2页存姓名,2页存电话,7页存地址,总共需11页,第四页和第十一页有碎片存在。
假设一组占50字节,物理分割需要14页来存贮这个关系,这种技术不仅在页的末尾产生水平碎片,而且为了保证所有组大小相同,也存在纵向碎片,地址属性被分成了两组。
用分组垂直分割技术,假设姓名和电话存在同一组中,则需4页存贮姓名和电话,每页存30个人的信息,需7页存地址,每页存15个人的地址.存贮这10个元组需1页,在第四页和第十一页有碎片存在。
如果首先进行分组水平分割,然后再分组垂直分割,则混合分割需要20页(假如仍然采用上面所说的技术).由于篇幅所限,图表略。
这一简单例子表明:
数据库的分割技术直接影响存贮数据所需的物理空间,这又反过来影响数据库处理过程中的I/O操作次数。
2.3数据库分割技术对数据库系统的影响
2.3.1重新组织的开销
数据库的重新组织就是改变数据库的结构。
这里主要的开销就是:
为了消除已有的数据库用新的格式重装数据库所需的I/O请求。
另外,对于分组水平分割,分组垂直分割及混合分割来说,重新分组也要增加一些开销。
那么这些重新分组究竟需要多少消耗,重新组织的效果是什么呢?
前面已经提到,分组水平分割需要的页数最多,所以对于数据库来说要消除和重装也需要大量的I/O。
另外这种技术对于元组的重新分组代价也是相当昂贵的因为所有的元组都将重新检查。
对于单一垂直分割来说,不存在重新分组的间题,所以也就没有重新分组的开销。
实验表明,重新组织数据库所需代价方面各种分割技术的排列顺序如下:
(序号越小表明开销越少)
(1)单一垂直分割
(2)水平分割(3)物理垂直分割(4)分组垂直分割(5)混合分割(6)分组水平分割
2.3.2删除属性所需的代价
删除属性所需的代价就是从一个关系框架中删除一个属性所需要的消耗。
除了单一垂直分割外,其它的分割技术都需重新组织数据库。
对于水平和分组水平分割来说,要删除一个属性,整个数据库都要重新组织。
对于分组垂直分割和物理垂直分割及混合分割来说,要删除某一属性,要重新组织某个属性组。
由于将要重新组织的关系的容量直接决定重新组织所需的开销,所以有必要检查这个数据库中有多少需要重新组织的。
单一垂直分割技术仅需空间来存放将要被重新组织的属性(其实这些属性并未真正重新组织)。
分组垂直分割、物理垂直分割、混合分割只是包含要删除的属性的组必须重新组织。
而其它技术将要使整个关系重新组织。
在删除一个属性所需代价方面的排列顺序是:
(序号小表明代价小)
(1)单一垂直分割<
(4)混合分割<
(2)物理垂直分割<
(5)水平分割<
(3)分组垂直分割<
(6)分组水平分割
2.3.3属性插人所需代价
属性插入就是把一个属性插入到关系的框架中,或把一个属性的值存入关系中。
插入属性的代价基本上是一样的。
但也有些区别,如果擂入一个属性要引起其它属性的移动,则开销就会大些。
假设在三个分组技术中,每个属性只在一个组中,则插入一个属性所需代价的排列顺序:
(序号小表明代价小)
2.3.4存取属性的代价
存取属性需要经过三步:
确定所需存取的属性所在的物理页;
找出该属性值在元组中的物理位置;
从确定的物理位置中存取该属性值。
对于三种垂直分割来说,只需检查所有物理页的一部分,即可确定该属性的位置,所以开销较少。
其中单一垂直分割存取属性所需代价最小。
水平和分组水平分割所需代价较大。
其余居中。
排列顺序:
2.3.5重新组建元组的开销
元组重新组建所需代价与从一个关系中选择这个元组的代价相同。
选择一个元组给出它的标识符需要两个步骤:
确定这个元组的物理地址;
存取这个元组的所有属性值。
六种分割技术在这方面的区别主要是:
为了找到一个元组的所有属性值所需访问的物理页数。
显然水平分割和分组水平分割,每个元组都在一个物理页上,但分组水平分割比水平分割需要更多的I/O操作,所以在这方面水平分割排在第一位,分组水平分割排在第二位。
由于单一垂直分割一页只存贮一个属性,所以元组重建的开销最大。
其余三种居中。
2.3.6插入元组的开销
把一个元组插入到一个关系中有三个主要步骤:
检查现有的物理页的空间是否够插入一个元组;
如果没有足够的空间则要分配新的物理页,并修改目录表;
把这个元组的值存入现有的页或新增的贡中。
第一、二步就是确定新元组插在何处。
为了方便比较,假设对于六种技术来说,自由空间的管理方法相同。
对于两种水平分割技术来说,前二步一次完成,而单一垂直分割,每个属性需要进行一次,其它三个分组技术,每组要进行一次。
具体次数取决于这个关系的分组数,来际上插入一个元组的开销取决于存贮一个元组所需的物理页数。
六种技术在这方面的排列顺序是:
(6)单一垂直分割<
(1)水平分割<
(3)物理垂直分割<
(4)分组垂直分割<
(5)混合分割<
(2)分组水平分割
2.3.7修改元组的开销
修改元组主要有两步确定要修改的元组中各属性的物理位置修改各属性的值。
修改元组的开销文要取决于要修改的属性个数,如果只修改一个属性,贝吐与对属性的操作相同,单一垂直分割技术开销最少;
但要修改的属性多时,则与插入元组的操作相同,水平分割开销最少。
由于无法确定要修改的属性多少,所以在这方面采用与上相同的排列顺序。
2.4什么是数据切分
"Shard"这个词英文的意思是"碎片",而作为数据库相关的技术用语,似乎最早见于大型多人在线角色扮演游戏中。
"Sharding"姑且称之为"分片"。
Sharding不是一门新技术,而是一个相对简朴的软件理念。
众所周知,MySQL5之后才有了数据表分区功能,那么在此之前,很多MySQL的潜在用户都对MySQL的扩展性有所顾虑,而是否具备分区功能就成了衡量一个数据库可扩展性与否的一个关键指标(当然不是唯一指标)。
数据库扩展性是一个永恒的话题,MySQL的推广者经常会被问到:
是如何在单一数据库上处理应用数据捉襟见肘而需要进行分区化之类的处理。
答案是:
Sharding。
Sharding不是一个某个特定数据库软件附属的功能,而是在具体技术细节之上的抽象处理,是水平扩展(ScaleOut,亦或横向扩展、向外扩展)的解决方案,其主要目的是为突破单节点数据库服务器的I/O能力限制,解决数据库扩展性问题。
通过一系列的切分规则将数据水平分布到不同的DB或table中,在通过相应的DB路由或者table路由规则找到需要查询的具体的DB或者table,以进行Query操作。
这里所说的"sharding"通常是指"水平切分",这也是本文讨论的重点。
具体将有什么样的切分方式呢和路由方式呢?
行文至此,读者难免有所疑问,接下来举个简单的例子:
我们针对一个Blog应用中的日志来说明,比如日志文章(article)表有如下字段:
面对这样的一个表,我们怎样切分呢?
怎样将这样的数据分布到不同的数据库中的表中去呢?
其实分析blog的应用,我们不难得出这样的结论:
blog的应用中,用户分为两种:
浏览者和blog的主人。
浏览者浏览某个blog,实际上是在一个特定的用户的blog下进行浏览的,而blog的主人管理自己的blog,也同样是在特定的用户blog下进行操作的(在自己的空间下)。
所谓的特定的用户,用数据库的字段表示就是"user_id"。
就是这个"user_id",它就是我们需要的分库的依据和规则的基础。
我们可以这样做,将user_id为1~10000的所有的文章信息放入DB1中的article表中,将user_id为10001~20000的所有文章信息放入DB2中的article表中,以此类推,一直到DBn。
这样一来,文章数据就很自然的被分到了各个数据库中,达到了数据切分的目的。
接下来要解决的问题就是怎样找到具体的数据库呢?
其实问题也是简单明显的,既然分库的时候我们用到了区分字段user_id,那么很自然,数据库路由的过程当然还是少不了user_id的。
考虑一下我们刚才呈现的blog应用,不管是访问别人的blog还是管理自己的blog,总之我都要知道这个blog的用户是谁吧,也就是我们知道了这个blog的user_id,就利用这个user_id,利用分库时候的规则,反过来定位具体的数据库,比如user_id是234,利用该才的规则,就应该定位到DB1,假如user_id是12343,利用该才的规则,就应该定位到DB2。
以此类推,利用分库的规则,反向的路由到具体的DB,这个过程我们称之为"DB路由"。
当然考虑到数据切分的DB设计必然是非常规,不正统的DB设计。
那么什么样的DB设计是正统的DB设计呢?
我们平常规规矩矩用的基本都是。
平常我们会自觉的按照范式来设计我们的数据库,负载高点可能考虑使用相关的Replication机制来提高读写的吞吐和性能,这可能已经可以满足很多需求,但这套机制自身的缺陷还是比较显而易见的(下文会提及)。
上面提到的"自觉的按照范式设计"。
考虑到数据切分的DB设计,将违背这个通常的规矩和约束,为了切分,我们不得不在数据库的表中出现冗余字段,用作区分字段或者叫做分库的标记字段,比如上面的article的例子中的user_id这样的字段(当然,刚才的例子并没有很好的体现出user_id的冗余性,因为user_id这个字段即使就是不分库,也是要出现的,算是我们捡了便宜吧)。
当然冗余字段的出现并不只是在分库的场景下才出现的,在很多大型应用中,冗余也是必须的,这个涉及到高效DB的设计,本文不再赘述。
2.5为什么要数据切分
上面对什么是数据切分做了个概要的描述和解释,可能会疑问,为什么需要数据切分呢?
像Oracle这样成熟稳定的数据库,足以支撑海量数据的存储与查询了?
为什么还需要数据切片呢?
的确,Oracle的DB确实很成熟很稳定,但是高昂的使用费用和高端的硬件支撑不是每一个公司能支付的起的。
试想一下一年几千万的使用费用和动辄上千万元的小型机作为硬件支撑,这是一般公司能支付的起的吗?
即使就是能支付的起,假如有更好的方案,有更廉价且水平扩展性能更好的方案,我们很有必要选择。
但是,事情总是不尽人意。
平常我们会自觉的按照范式来设计我们的数据库,负载高点可能考虑使用相关的Replication机制来提高读写的吞吐和性能,这可能已经可以满足很多需求,但这套机制自身的缺陷还是比较显而易见的。
首先它的有效很依赖于读操作的比例,Master往往会成为瓶颈所在,写操作需要顺序排队来执行,过载的话Master首先扛不住,Slaves的数据同步的延迟也可能比较大,而且会大大耗费CPU的计算能力,因为write操作在Master上执行以后还是需要在每台slave机器上都跑一次。
这时候Sharding可能会成为鸡肋了。
Replication搞不定,那么为什么Sharding可以工作呢?
道理很简单,因为它可以很好的扩展。
我们知道每台机器无论配置多么好它都有自身的物理上限,所以当我们应用已经能触及或远远超出单台机器的某个上限的时候,我们惟有寻找别的机器的帮助或者继续升级的我们的硬件,但常见的方案还是横向扩展,通过添加更多的机器来共同承担压力。
我们还得考虑当我们的业务逻辑不断增长,我们的机器能不能通过线性增长就能满足需求?
Sharding可以轻松的将计算,存储,I/O并行分发到多台机器上,这样可以充分利用多台机器各种处理能力,同时可以避免单点失败,提供系统的可用性,进行很好的错误隔离。
综合以上因素,数据切分是很有必要的,且我们在此讨论的数据切分也是将MySql作为背景的。
基于成本的考虑,很多公司也选择了Free且Open的MySql。
对MySql有所了解的开发人员可能会知道,MySQL5之后才有了数据表分区功能,那么在此之前,很多MySQL的潜在用户都对MySQL的扩展性有所顾虑,而是否具备分区功能就成了衡量一个数据库可扩展性与否的一个关键指标(当然不是唯一指标)。
数据