资料贴

资料贴

@Robin_TY 老师,能推荐一些强化学习的资料吗,谢谢

skf009 回复了问题 1 人关注 1 个回复 1402 次浏览 2018-12-09 16:47 来自相关话题

近200篇机器学习&深度学习资料分享

治肥虫btz 回复了问题 102 人关注 16 个回复 66189 次浏览 2017-11-07 18:53 来自相关话题

在哪儿下载视频

fish 回复了问题 2 人关注 1 个回复 1472 次浏览 2017-02-18 22:38 来自相关话题

中国人民银行:2015年中国区域金融运行报告

回复

傲风寒 发起了问题 2 人关注 0 个回复 1939 次浏览 2016-07-12 12:12 来自相关话题

80 多个 Linux 系统管理员必备的监控工具

fish 回复了问题 2 人关注 1 个回复 1688 次浏览 2016-05-24 11:20 来自相关话题

【Scala·语法·笔记】1、为主构造器参数形成的字段,设置 protected;2、类中定义同名字段却不报错的坑

曹志翔 发表了文章 0 个评论 1840 次浏览 2015-10-30 00:13 来自相关话题

结论: 1,通过 protected[this],可以把主构造器参数形成的字段的访问级别,设置成受保护类型 class Person(protected[this] var age : Int) { }   2, ...查看全部
结论:
1,通过 protected[this],可以把主构造器参数形成的字段的访问级别,设置成受保护类型
class Person(protected[this] var age : Int) { }
 
2,特定条件下,类定义中存在同名字段,却不会报错。(隐隐感觉会在将来的某个时候弄个Bug出来搞死自己,千万要避免字段同名)
条件:a,主构造器参数,没有 val 或 var;b,类中,定义了同名、非私有字段
class Person(age : Int) { //age 前加 val 或 var。就会报错
protected[this] var age : Int = _ //不写访问级别,或 protected[this],不报错;pribate[this] 报错
}

class Student extends Person(7) {
def schoolAge {
println(if (age > 6) "School Age" else "Preschool")
}
}

object Person extends App {
val s = new Student
s.schoolAge
s.age //无法在类外访问,但可以在子类中访问
}

 
分析过程:
 
ps.    刚抱怨完学习效率不高,又忍不住来扣这么个细节的地方。(耳边响起了玉海老师的嘱咐“学习的内容很多,要抓重点啊!)
不过,在学习这个知识点时,有些意外收获。还是发出来跟大家交流一下。
 
       事情是从陈超老师讲“主构造器参数自动形成类的字段”开始的。其中,有个知识点是“加 val 或 var,则对应的字段为 public;不加,则为 private”,那如果需要这个字段为 protected 该怎么声明呢?情景如下:
class Person(age : Int){
}

class Student extends Person(20){
println(age) // 报错:Not Found。因为 age 是 Person 类的私有类型
}

object Person extends App {
val s = new Student
}

       那么,如何在子类中访问父类“不带 val 或 var 的主构造器参数形成的字段”呢?当然,可以把该字段从主构造器的参数列表中去掉,挪到类中声明。但,只样一来,还需要再定义一个方法,用于设置该字段的值。能否依然“在主构造器的参数列表中”,又能是 protected?
 
       我做了以下尝试:
1,直接在类中定义同名字段。
       但显然 1 个类里,有 2 个同名字段,是不正常的。可万万没想到“没 有 报 错”。而且,类里定义的同名 protected 字段,可以在子类中访问,但就是主构造器参数的值,没有传给该字段。这么做就没有意义了(还得声明个方法设置 age 的值)。
class Person(age : Int){
protected[this] var age : Int = 10
}

class Student extends Person(20){
println(age) // 10 //意外的是,居然没有报错
}

object Person extends App {
val s = new Student
}

2,直接在主构造器参数上设置访问级别,可以有效使用
class Person(protected[this] var age : Int) { //一定要写 protected[this]。如果只写 protected,依然可以在类声明之外访问,却 “没 有 提 示”
}

class Student extends Person(7) {
def schoolAge {
println(if (age > 6) "School Age" else "Preschool")
}
}

object Person extends App {
val s = new Student
s.schoolAge
s.age //无法在类外访问,但可以在子类中访问
}

 

GOOGLE分布式数据库技术演进研究 --从Bigtable、Dremel到Spanner

唐半张 发表了文章 0 个评论 1649 次浏览 2015-10-10 09:57 来自相关话题

GOOGLE分布式数据库技术演进研究           1 引言     ...查看全部
GOOGLE分布式数据库技术演进研究 
        

1 引言 
  
     在传统RDBM系统中,对于事务处理必须保证为一个完整的逻辑处理过程,具备ACID四个特性,A Atonomy事务处理的原子性,要么成功,要么失败 ,C Consistency一致性,数据库必须保持原有约束的关系,数据之间必须符合数据完整性,I Isolation事务处理必须要彼此隔离,由RDBM保证能够并发处理事务,而不需要用户显示的干预,D Durability数据能够被持久化下来,不会出现事务涉及数据丢失的情况。此4个特性是RDBM系统的基础要素和必须遵循的原则。 
  
     事物总是发展的,人类社会信息总量在不断增大,对于RDBM系统管理的数据容量已经从GB级别演进到TB级别,又从TB级别演进到PB级别,数据量不断增大,原有的RDBM已经力不从心。如何解决该问题? 
  
     软件设计很多思想都来自于建筑领域,先回到建筑领域,房屋建造有2要素,建筑高度、基础构造,建筑高度决定基础构造,而基础构造又会影响到建筑高度,建设一栋三层小房,专混结构就可以了,建设十一层房子,就需要上框架结构,摩天大楼,需要采用钢和框架的混合结构方式来支撑。软件设计也有相同的道理,也存在2个要素,一个容量要素,软件能够管理多少数据规模,一个是软件构造要素,软件运行的物理硬件、操作系统、采用何种架构来组织软件各功能模块。二者对应关系,“建筑高度”对应于“容量”,“基础构造”对应于“软件构造”,但是,二者有所不同,由于建筑物需要考虑成本、实用性、安全性和各种限制,建筑高度一定是有限的,总有一个极限,而软件则有所不同,一个大容量系统数据量已经从GB级增加到了TB级,从TB级到PB级,跃升了6个数量级,软件容量呈现指数级的增加,使得软件设计面临比建筑领域更大的挑战。 
  
     如何解决数据容量持续增加带来的挑战,第一 提升系统的计算能力,可以并行对应数据进行分区域或者分块计算,然后对应计算结果进行汇集处理,第二 提升数据的读取速度,单存储节点的读取速度必定存在限制,需要指出多存储节点的DB系统,分布式数据库DDBS系统诞生了,分布式数据库系统可以支持多个存储节点,从GOOGLE的BIGTBALE数据库原理相对应的HBASE数据库,就可以支持多个存储节点,存储节点的数量可以根据要求进行扩展,无理论上限。但是分布式数据库采用对存储节点构造后,带来了一个新的问题,这个问题就是CAP理论的魔咒,CAP为英文Consistent、Availablity、Partition Tolerance的缩写,一致性,可用性、分区容忍性,通常认为CAP理论是只能满足二个要求,不可兼得(这种限制,其实已经被GOOGLE的SPanner系统打破)。常见的DDBS系统可以保证AP,但是无法保证C,一致性,比如HBASE数据库,就无法保证多个存储区域的外部事务的一致性,如果数据跨了多个存储节点,数据可能存在冲突的可能,不一致的可能,无法做到数据表外的事务支持。ACID特性是一个数据库完美解决的要求,但DDBS要满足ACID特性中的原子性和各存储节点数据的外部一致性存在很大的困难,如果对二种不同的特征描述进行关联,AC---Consistent事务的原子性和一致性对应于DDBS一致性属性要求,ID---Availablity,事务的隔离性和持久化对应于DDBS可用性属性要求,而DDBS的Partition Tolerance会带来满足AC或者ID属性的困难,因此很多DDBS会进行特性取舍。 
  
     因此,如果让DDBS系统,使之完全满足ACID的特性,必须要解决数据Partition后带来的困难,数据分成多个存储节点,如何在满足事务隔离和持久化特性的基础上,保证这些不同存储节点上的数据一致性可用性,让DDBS呈现出完全的ACID特性,打破CAP的魔咒,最常用的方式是不同数据节点的同步和全局锁控制,如果采用这种控制机制,必然会带来系统网络同步开销增加,系统的Availability能力下降,而且会出现全局锁瓶颈点,影响到系统的扩展性,出现扩展后的瓶颈点,因此这条解决方案很难走通。那我们是否能够从不一致和不确定基础上,构造出一个可靠和确定的DDBS系统,GOOGLE的SPanner数据库设计思想为解决该问题带来的新的希望,以一种全新的视角和思路来解决该问题,在可以期待的将来,开源社区也能够出现与SPanner方式对应的产品出现,使得DDBS系统应用范围更加宽广。 
  
     综上所述,从CAP特性理论看,面对传统的权威理论,技术上要敢于去挑战,勇于分析,可以另辟蹊径解决技术问题,解决这些问题最大困难还是改善软件开发人员的认识,一旦认识成为定势,势必陷入到死胡同中,同时必须具备深厚的技术功底和高超编程技巧,从开源社区现在还缺乏类似于Spanner技术看,充分说明了这个技术要从思想走到实现,存在非常多的技术设计难点,要搞定这些设计难点绝无易事,但值得庆幸的,已经有人做到了,而且还在不断的完善它。 
  
     GOOGLE的分布式数据库系统从BIGTABLE的正式推出后,先后对外发布了Bigtable、Dremel、Spanner等不同的分布式数据库产品,有的是引入新的设计实现,有的是针对原有的技术进行改进和优化,用于满足GOOGLE不同的应用场景,支持日益增加的数据量管理要求。 
  
     GOOGLE分布式数据库技术,从个人理解看,可以分为三个阶段,第一阶段以Bigtable产品为代表,实现了数据的分布式存储、行数据的事务性管理和较好的扩展性,从存储WEB页面而生,创造性提出了KEY-VALUE这种MAP数据结构,并广泛应用到GOOGLE的各种应用中,与GOOGLE的MapReduce GFS技术搭配,构成了GOOGLE分布式云计算的三架马车,对应开源社区推出HBASE产品,也在近年得到了广泛应用。 
  
     第二个阶段以Dremel产品为代表,Dremel产品采用了与Bigtable不同的数据结构,立足实时对于海量数据进行分析,据说在秒级可以完成PB级别的数据分析和处理,可以做是分布式数据库实时处理的杰作,其实时处理能力达到令人惊艳的速度。 
  
     第三阶段以Spanner数据库技术为代表,Spanner数据库在可以做到多数据表事务一致性管理,利用原子时钟(TrueTime)和Paxos协议解决了分布式数据库多表事务一致性管理的难题,打破的CAP不可三者兼得的理论神话,使得分布式数据库技术得到了革命性的进步。 
  
     严格来讲Dremel与Bigtable和Spanner解决的问题有所不同,Dremel侧重于对应海量数据的实时处理,而Bigtable和Spanner更侧重于传统的关系型数据库支持功能对齐和替换,并不是简单产品替换关系。从GOOGLE分布式数据库技术发展历程看,这些技术得以成功推出,有创造性的新锐视角和解决方案,更有其坚持在廉价PC服务器上面构筑海量数据处理系统的理想和情怀,更有起高超的技术实力和团队合作,这些因素的结合,使得技术难关被不断的突破,分布式数据库产品得以大成,这些产品的确值得技术人员去深入学习和体会。 
  
     为了更好的对比和分析GOOGLE的分布式数据库技术,本文从Bigtable、Dremel、Spanner数据模型、系统架构、数据查询原理、应用场景和关键技术进行深入分析,最后对于其特点进行对比,从而使得读者对应GOOGLE的分布式数据库技术有一个初步的认识。 
  
  
2 BIGTABLE开山壁祖 
  
2.1Bigtable的数据模型 
  
2.1.1Bigtable的Key-Value数据结构 
  
     Bigtable采用Key-Value数据结构,Key由行关键字、列关键字、时间戳组合而成,Value为对应数据内容,行关键字和列关键字都是字符串数据类型,时间戳是一个64位的长整数,精确到毫秒的时间戳,这三个属性在一个数据库中全局唯一,由Key和Value构成的KV数据结构称为一个数据项,考虑到分布式数据库的多副本的特性,数据项会按照时间戳进行排序,并对于过期的数据项进行过期回收,其数据结构如图2-1所示。 
  

 
  
图2-1 Bigtable KV结构示意图 
  
2.1.2Bigtable的数据模型层次 
  
     Bigtable数据模型由下而上,主要部件可以初分为四个层面,最底层为SSTABLE,存放在GFS分布式文件系统中,为数据存储MAP结构体,第二层TABLET结构体,一个表可以由一个或者多个Tablet构成,由一系列的SSTABLE构成,第三层TABLET服务器,管理一组TABLET数据体,最上层Bigtable数据库,由多个TABLET服务器和一个Master服务器、客户端访问连接支持软件构成,最终形成了一个分布式数据库,对外提供数据库服务,层次关系如图2-2所示。 
  

 
  
图2-2 Bigtable各级数据模型视图 
  
     数据项基于一种叫做SSTABLE的数据格式,SSTABLE是一种持久化、排序的、不可更改的MAP数据结构,每一个SSTABLE由一系列数据块构成,在每一个SSTABLE的最后存储着块索引,SSTABLE使用这些索引来定位数据块,在每一个SSTABLE被打开时,块索引会自动加载到内存中。SSTABLE提供了二种数据访问方式,第一种方式,使用二分法查找内存中的块索引,根据对应的块索引把对应的数据块读取到内存中,此种方式只会发起一次磁盘寻址,第二种方式是把整个SSTABLE都整体加载到内存中。SSTABLE基于GOOGLE GFS全局文件系统基础上,实现对应文件系统层面的负载均衡和IO能力分担, 
  
     从特点看,BIGTABLE不直接支持对应数据的修改操作,通过时间戳方式来间接支持数据修改。数据读取方式,提供了二种不同的机制,二分法的块索引定位加载,类似于ORACLE提供的索引访问方式;SSTABLE的全部加载,类似于ORACLE提供的全表扫描机制,从技术角度看,不管分布式数据库技术本身如何发展,对小粒度数据精确加载和整体数据加载的场景,从这点看,所有的数据库存储结构应该都是殊途同归。 
  
     一个TABLET由TABLET Log存储TABLET的提交数据的Redo记录数据,MEMTABLE是内存缓存,存储最近访问的记录数据,数据持久化到一组SSTABLE文件中。 
  
     最上层Bigtable数据库服务层,对外提供Bigtable的数据库服务功能,为Bigtable的最顶层结构。 
  
2.2Bigtable的系统架构 
  
     Bigtable由客户端连接库、Master服务器、TABLET服务器组构成,客户端链接库提供数据库客户端访问功能,提供服务端访问,比如对于数据寻址的缓存信息; Master服务器负责TABLET服务器组的管理,根据负载情况,可以动态的增加和删除TABLET服务器,维护TABLET服务器组;TABLET服务器管理该服务器上面的TABLET集合,完成TABLET数据读取和写入操作,当TABLET太大时,对TABLET进行拆分,在一个Bigtable中只能有一个Master服务器,通过Chubby保证Master服务器的唯一性;Chubby服务提供了TABLET根信息服务、系统Master管理和TABLET服务出错的善后处理任务,保存Bigtable数据库Schema信息。Bigtable整个系统架构如图2-3所示。 
  

 
  
图2-3 Bigtable系统架构图 
  
2.3Bigtable的数据查询 
  
2.3.1Bigtable的数据定位 
  
     Bigtable数据查询,首先是对于数据所在tablet进行定位,Bigtable的位置定位,分为三个步骤。 
  
     第一步,客户端程序在缓存中查找tablet位置是否在缓存中存在,如果在缓存中存在就直接读取,如果不存在,通过Chubby服务器查询tablet的根节点,取到Bigtable根节点tablet信息; 
  
     第二步,根据Bigtable根节点的tablet信息,找到数据对应METADATA的数据表,该数据表中存储着所有的用户数据tablet的位置信息; 
  
     第三步,根据METADATA的数据表存储的用户数据tablet信息,找到数据对应tablet信息,根据该位置信息,读取到tablet数据到客户端。 
  
2.3.2Bigtable的数据读取 
  
     Bigtable数据读取时以Tablet为单位,必须读取到构成该Tablet所有涉及到SSTABLE,发起读取操作时,首先要对操作完整性和权限做检查,检查通过后,首先在Tablet服务器所在的缓存里面查找,Bigtable提供二级缓存缓存,一种是以Key-Value形式的一级数据缓存,如果在这种级别中的缓存中无法找到,访问二级数据缓存,二级数据是SSTABLE的BLOCK数据缓存,对于热点局部性数据来讲,这种BLOCK环境命中率很高,对于系统性能改善更加有效。 
  
     在数据缓存中如果没有找到对应的读取数据,启动数据定位的三个步骤,完成对于TABLET的位置信息读取,TABLET信息读取转换为对应SSTABLE数据,根据SSTABLE数据是否进行了压缩,对于涉及到该TABLET的SSTABLE进行解压操作,完成读取后返回到客户端。 
  
2.3.3Bigtable的数据写入 
  
     Bigtable数据写入时,首先是坚持该操作数据格式是否正确,并判断该操作发起者是否有该操作的权限,权限判断是通过存储在Chubby服务器上面的权限控制表来判断。判断操作发起者具备该操作的权限后,会发起具体写入数据动作,该动作是一个事务操作,操作必须保证对于Tablet中数据写入成功,否则不会写入TABLET,如果写入成功后,会把提交该操作修改到Tablet对应的Redo日志中,同时该写入内容会插入到MEMTABLE中。 
  
2.4应用场景和关键技术 
  
2.4.1应用场景 
  
     Bigtable从数据存储特点看,属于行式数据库存储模型,虽然Bigtable具备把列分配到不同的SSTABLE,形成不同的列簇的情况。由于Bigtable采用按照行存储模型,因此对于数据表中的一行可以实现事务性操作,实现数据单表上的事务控制,虽然Bigtable提供了批量数据的访问接口,但是还不支持跨行的事务操作。 
  
     而Bigtable采用的KV结构,可以按照Key中的行关键字和列关键字,对于海量数据进行列维度和行维度的切片管理,分别到同步到TABLET服务器上面。在一个BIGTABLE系统中,TABLET服务器的数量可以根据负载要求,做动态的调整,对于TABLET服务器数量无上限,这样就可以支持对于数据库负载的水平扩展,根据GOOGLE提供的数据,单TABLET服务器在BLOCK为64KB时,KEY-VALUE中的VALUE为1K的长度是,可以支持1200次请求,一个TABLET服务器可以处理75M数据读取,当TABLET服务器数量增加后,其读取数据的能力可以提升,当并不会严格的遵循线性关系,可以从GOOGLE在BIGTABLE中提供的测试数据看出,TABLET服务器增加和整体系统吞吐能力提升的关系,参见图2-4。 
  
     可以看出对于TABLET的扫描和在TABLET中内存中的随机读,提升效率最为明显,这是由于在TABLET服务器增加过程中,此类操作都是在内存中进行,因此服务器越多,支持吞吐量就会更大。顺序读、随机写、顺序写提升比率低于前两种操作,顺序读由于读取SSTABLE到TABLET服务器时,一个BLOCK被读取时,SSTABLE中相邻接的BOLCK会被加载到BLOCK缓存中,后续会在缓存中被读取到该BLOCK,不在需要在从SSTABLE中读取。随机写和序列写在采用了批量提交的方式,通过数据流的方式来进行处理,此种操作方式随TABLET的增加,提升效果还是比较明显。谁TABLET服务器提升效果最低的为随机读,原因是随机读取时,为了访问KEY-VALUE值中,VALUE为1K的数据时,会同步把整个SSTABLE中一个BLOCK都读取出来,当随机读取请求数量增加时,整个网络带宽会急剧上升,导致每个TABLET服务器吞吐能力下降,整个系统能够处理的随机请求数量变少。 
  

 
  
图2-4 tablet服务器与系统IO吞吐量关系图 
  
2.4.2关键技术 
  
     从Bigtable应用场景看,BIGTABLE系统设计需要解决下面的核心技术 
  
     技术难题一 系统鲁棒性 
  
     对于分布式环境,对于网络受损、内存数据损坏、时间误差,机器硬件损坏这这种常见的问题出现后,系统的容错处理能力,要求Bigtable系统具有较强的鲁棒性和容错性。 
  
     技术难题二 系统容错和负载与效率的平衡 
  
     TABLET副本数量增加会增加系统容错能力,但是会增加系统管理代价和同步成本,效率就会相应的下降;系统负载和效率增加,有限制了数据副本数量、副本数量下降会导致系统容错性减低,GOOGLE具体处理算法,这些在GOOGLE对外公布的论文中没有进行详细的描述,在开源产品HBASE遇到问题看,这些都是一些技术难点。 
  
     技术难题三 不同查询特点的应对 
  
     由于系统不同查询特点,数据特点,对于BIGTABLE中的各种关键配置参数的配置方式,比如SSTABLE中BLOCK大小的选择、缓存算法设计细节。 
  
     上面的这些技术难题相信已经被GOOGLE很好的解决,不过这些解决经验和具体技术细节GOOGLE并没有进行公开。 
  
2.4.3BIGTABLE后续发展 
  
     Bigtable做为GOOGLE公布的第一代分布式数据库技术,结合下层的GFS文件系统,上结合MAP-REDUCE框架完成数据的分片计算,构成了GOOGLE的分布式计算体系。而BIGTABLE目前只有在一个系统只支持一个Master服务器,同时对于多表事务性无法支持,这些都是Bigtable后续要解决的技术问题。对于不同特点数据库查询请求,不同特点存放数据, BIGTABLE的关键参数应该如何配置,是否有一种完美的配置参数,可以完全满足各种不同特点的查询场景,从目前来看,还不能做到的,还必须根据数据特点,对BIGTABLE的参数做相应定制,包括一些BIGTABLE要使用的GFS文件配置参数、网络配置参数,这些都成为使用Bigtable数据库过程中一些较为复杂问题,整个Bigtable数据库使用技术门槛仍然比较高。 
  
3  Dremel 
  
3.1背景 
  
     大规模交互性数据分析处理在整个行业中应用越来越广泛,对于交互型分析对于数据处理的响应时间要求比较高,而原有Bigtable数据库设计上并没有考虑对于交互式场景要求,对于大大规模交互数据分析处理响应性不够,因此Dremel就应运而生,Dremel解决大规模交互数据分析的实时性问题,可以做到秒级的数据响应,GOOGLE在测试中宣称,可以在3秒钟的时间处理1PB数据。 
  
     在大规模交互数据分析中,会有这样一种场景,需要参加数据分析的原始数据量非常大,但是最终结果集数据量会很小,往往是一个分析结果或者是汇总型的数据,这种场景就是大型交互时数据分析的典型场景。从GOOGLE分布式数据库产品的战略定位看,Dremel和Bigtable的定位有所不同,Dremel更适合对于交互式场景,而Bigtable通常会跟MapReduce配置,做为大数据处理搭配处理,当然Dremel同样可以与MAPReduce结合使用。因此Dremel并不是取代Bigtable的一种分布式数据库,而是一种补充,从技术演进角度看,由于Dremel数据库公开时间晚于Bigtable,因此做为Google第二代分布式数据库代表之一。 
  
3.2Dremel的数据模型 
  
3.2.1Dremel嵌套列数据模型 
  
     Dremel采用是嵌套列数据模型,该数据模型把嵌套数据拆分为列结构加以存储,在查询时把数据重建为嵌套数据,原有列存储数据库通常属于关系型数据库,在嵌套类型的数据处理还未采用这种结构,GOOGLE创造性的把嵌套数据处理为列数据库,并且技术指标还能大幅提升,满足大型数据的交互式查询要求,不得不说这个GOOGLE的一个新创造,但为什么是这样的列嵌套结构,而不是其他数据结构,这点GOOGLE并没有进行介绍说明,因此这一点理解上面有一定困难,不过在后续介绍中,会发现这种结构在数据查询处理时的优势和特点。 
  
     一提起数学模型,很多程序员就会晕,不过仔细看看,稍微有一点数据知识的人,应该就能够看明白,Dremel的数学模型如下: 
  
     π = dom |  
  
     π是数据库中的一条记录类型,Ai是数据库记录的一个字段,*为一个重复字段,?为可选字段,这个数据模型说明该一条记录是由多个字段构成,字段类型由可选字段、必选字段、重复字段组成,可选字段可以出现,也可以不出现;必选字段必须会出现,重复字段则会出现多次。在GOOGLE公开的资料中,在图3-1给出嵌套记录样例和数据模式定义,Document是一个网页类型的数据模式,该网页有整形的DocId和可选的Link分组属性,Link分组属性包含了多个Backword和Forward可 
  

 
  
图3-1 嵌套式记录样例和数据模式定义 
  
     重复的整型属性构成列表,一个网页包括了多个可以重复的名字分组,每个名字分组,可以包括多个语言分组和可选的URL地址,语言分组包括必选的CODE字段,可选的Country属性。整个Document嵌套层次关系如图3-2: 
  

 
  
图3-2嵌套层次关系图 
  
3.2.2Dremel的重复深度和定义深度 
  
     在Dremel中如何对于嵌套式数据结构,在列存储后进行重建,依赖于二个重要参数,一个是Repetition Level重复深度,一个是Definition Level定义深度.重复深度和定义深度仅针对重复字段来计算层次,用于描述这个值什么重复字段的此值重复了,以此确定重复的位置;定义深度描述多个嵌套层次的路径上,有多少个字段是有值的,通过这二个参数可以完成嵌套数据的列式化存储。 
  
3.2.3Dremel的存储和重构 
  
     Dremel存储是采用列式模型,按照列进行嵌套式数据的存储,读取数据时,根据定义在列式模型中的数据,按照顺序,把列式存储数据还原到原有的嵌套式数据结构。 
  
3.3Dremel的系统架构和查询 
  
     Dremel采用的是多层次服务树架构,最上层Dremel的根服务器,根服务器接收所有的查询请求,读取数据库相关的元数据,并把相关请求下发到下一级查询服务器查询。中间层查询服务器负责根服务器请求的派发和叶子服务器的查询结果的处理。叶子服务器与具体存储层直接通讯,完成存储系统上相关数据的读取和查询动作,整个系统架构图如下图所示。 
  

 
  
图3-3 Dremel多层次查询树架构 
  
     Dremel查询语句基于SQL语法,并根据自身列存储模型进行了定制,构成在Dremel的存储结构上面高效的执行,对于Dremel查询树结构,会对于根查询服务器收到查询请求进行层层拆分,最终传递到叶节点的查询服务器,叶节点查询服务器获取的数据结果后,进行过滤和汇总这样的计算,然后在上传到上级查询服务器,层层汇总结果后,最终返回结果数据。 
  
     Dremel支持多客户端并发查询,通常情况下查询请求会被同时运行,查询派发器,会根据系统的负载情况和查询优先级进行一定的查询调度操作,并提供容错性,对于查询节点响应缓慢和访问数据不达的情况进行调整。 
  
3.4应用场景和关键技术 
  
3.4.1应用场景 
  
     Dremel定位为交互系统大型数据处理的数据库系统,适合数据读取数据量大,但是返回数据量小的场景,对于返回数据量大的场景,采用Dremel就不太合适。 
  
3.4.2关键技术 
  
     Dremel采用的嵌套式列存储结构+多层次查询查询树,大型数据处理快速响应的关键,列存储结构可以只对查询关心的列数据读取,多层次查询树结构对于数据查询的拆分和聚合的方式有效的匹配,这是Dremel在该场景速度快于Bigtable的关键技术; 
  
     Dremel号称可以在秒级进行1PB级的数据

memcached

唐半张 发表了文章 0 个评论 2066 次浏览 2015-10-10 09:56 来自相关话题

一、Memcached概念 Memcached是NoSQL产品之一,是一个临时性键值存储NoSQL数据库,过去被大量使用在 ...查看全部
一、Memcached概念
Memcached是NoSQL产品之一,是一个临时性键值存储NoSQL数据库,过去被大量使用在互联网网站中,作为应用和数据库之间的缓存层,大大提高查询和访问速度。
Memcached有以下特点:
1、全内存运转:数据从来不保存在硬盘中,机器一重启,数据就全部没有了,所有又称临时性数据库;
2、哈希方式存储:
3、简单文本协议进行数据通信:不需要特定二进制代码,只需要用telnet连通memcached的监听端口,打入简单浅显的代码就能操作;
4、只操作字符型数据:无论往memcached放入什么,都是以字节的方式来处理。还原成数组、哈希表、字符串和数值等都交给应用层来解释。应用读写memcached的数据时,进行序列化和反序列化,把其解释成应用所能理解的数据类型;
5、集群也由应用进行控制,采用一致性散列(哈希)算法。二、安装Memcached
1、在linux上搭建yum环境
2、使用yum命令进行安装Memcached的rpm包
[root@nn init.d]# yum install memcached

3、启动Memcached,
首先要cd到相应目录
[root@nn ~]# cd /etc/rc.d/init.d/
运行memcached安装脚本
[root@nn init.d]# ./memcached start

4、查看Memcached是否启动
[root@nn init.d]# pstree

表示Memcached进程被启动了,下面开了5个线程
或者使用[root@nn init.d]# ps aux命令

memcached -d -p 11211 -u memcached -m 64 -c 1024 -P /var/run/memcached/memcached.pid
-d表示程序要后台化运行,-p指定端口,-u表示用memcached这个身份来运行,后面的都是memcached的控制参数
5、连接Memcached
[root@nn init.d]# telnet localhost 11211

三、Memcached常用命令
命令格式:


参数说明如下:

set/add/replace

查找关键字

客户机使用它存储关于键值对的额外信息,用于指定是否压缩,0不压缩,1压缩

该数据的存活时间,0表示永远

存储字节数

存储的数据块(可直接理解为key-value结构中的value)
1、增加:set、add、cas
2、获取:get、gets、
3、追加:append、prepend
4、删除:delete
5、清除所有:flush_all
6、加减:incr、decr
7、退出:quit
三、用java连接Memcached
目前java提供了三种API供我们实现与Memcached的连接和存取
1、memcached client for java
较早推出的memcached JAVA客户端API,应用广泛,运行比较稳定。
2、pymemcached
A simple, asynchronous, single-threaded memcached client written in java. 支持异步,单线程的memcached客户端,用到了java1.5版本的concurrent和nio,存取速度会高于前者,但是稳定性不好,测试中常报timeOut等相关异常。
3、xmemcached
XMemcached同样是基于java nio的客户端,java nio相比于传统阻塞io模型来说,有效率高(特别在高并发下)和资源耗费相对较少的优点。传统阻塞IO为了提高效率,需要创建一定数量的连接形成连接池,而nio仅需要一个连接即可(当然,nio也是可以做池化处理),相对来说减少了线程创建和切换的开销,这一点在高并发下特别明显。因此XMemcached与Spymemcached在性能都非常优秀,在某些方面(存储的数据比较小的情况下)Xmemcached比Spymemcached的表现更为优秀,具体可以看这个Java Memcached Clients Benchmark。
本文章使用memcached client for java为例
在使用java连接远程的PC机的Memcached时,记得保证两台机都开启telnet服务,并且本机能telnet通远程机,远程机必须关闭防火墙。
实例代码1:(java连接Memcached并实现数据的存取)
import com.danga.MemCached.MemCachedClient;
import com.danga.MemCached.SockIOPool;
public class memcachedTest {
public static void main(String[] args) {
//初始化SockIOPool,管理Memcached的连接池
String[] servers = {"192.183.3.230:11211"};
SockIOPool pool = SockIOPool.getInstance();
pool.setServers(servers);
pool.setFailover(true);
pool.setInitConn(10);
pool.setMinConn(5);
pool.setMaxConn(250);
pool.setMaintSleep(30);
pool.setNagle(false);
pool.setSocketTO(3000);
pool.setAliveCheck(true);
pool.initialize();
//建立MemcachedClient实例
MemCachedClient memCachedClient = new MemCachedClient();
for(int i = 0;i < 100000;i++){
//将对象加入到memcached缓存
boolean success = memCachedClient.set(""+i, "hello!");
}
for(int i = 0;i < 100000;i++){
//从memcached缓存中按key值取对象
String result = (String)memCachedClient.get(""+i);
System.out.println(String.format("get(%d):%s", i,result+i));
}
}
}
四、测试Memcached性能
为性能对比测试准备数据
1、插入数据到oracle
/**
* 插入测试数据到oracle数据库
* @param count插入记录数
* @return
*/
public static boolean insertIntoOracle(int count){
try {
con = dbConn("feng","feng");
if(con == null){
System.out.println("连接失败");
System.exit(0);
}
System.out.println("truncate table memcached_test......");
sql = "truncate table memcached_test";
pstmt = con.prepareStatement(sql);
rs = pstmt.executeQuery();
System.out.println("truncate table memcached_test finish.");
System.out.println("insert "+count+" values");
sql = "insert into memcached_test (memcachedId,memcachedvalues) values (?,?)";
pstmt = con.prepareStatement(sql);
for(int i = 1;i <= count;i++){
pstmt.setInt(1, i);
pstmt.setString(2, "Memcached is a good thing.I like it very much !-----------"+i);
pstmt.executeUpdate();
}
System.out.println("insert "+count+" values finish.");
rs.close();
pstmt.close();
con.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return true;
}
public static Connection dbConn(String name,String pass) throws ClassNotFoundException, SQLException{
Connection conn = null;
Class.forName("oracle.jdbc.driver.OracleDriver");
conn = DriverManager.getConnection("jdbc:oracle:thin:@192.183.3.230:1522:myorcl",name,pass);
return conn;
}
2、插入数据到Memcached
/**
* 插入测试数据到Memcached
* @param count插入记录数
* @return
*/
public static boolean insertIntoMemcached(int count){
//初始化SockIOPool,管理Memcached的连接池
String[] servers = {"192.183.3.230:11211"};
SockIOPool pool = SockIOPool.getInstance();
pool.setServers(servers);
pool.setFailover(true);
pool.setInitConn(10);
pool.setMinConn(5);
pool.setMaxConn(250);
pool.setMaintSleep(30);
pool.setNagle(false);
pool.setSocketTO(3000);
pool.setAliveCheck(true);
pool.initialize();
//建立MemcachedClient实例
MemCachedClient memCachedClient = new MemCachedClient();
System.out.println("insert "+count+" values into memcached......");
for(int i = 1;i < count;i++){
//将对象加入到memcached缓存
boolean success = memCachedClient.set("testData"+i, insertStr+i);
}
System.out.println("insert "+count+" values into memcached finish.");
return true;
}
Main函数调用这两个方法后,会将count条记录,值为insertData,插入到Oracle数据和set进Memcached中。
 
1、比较同时插入100000条数据的时间

从运行结果可以看出,插入10万条数据到Memcached比插10万条数据入Oracle所用时间有一个质的减少。
2、比较查询时间
以下是连接oracle数据并查找10000条数据的方法
/**
* oracle数据库查找
* @param count记录数
* @return
* @throws ParseException
*/
public static long searchOracle(int count) throws ParseException{
long useTime = 0;
try {
con = dbConn("feng","feng");
if(con == null){
System.out.println("连接失败");
System.exit(0);
}
StringBuffer sql =new StringBuffer("select memcachedid,memcachedvalues from memcached_test where memcachedid = ?");
pstmt = con.prepareStatement(sql.toString());
String memcachedvalues = "";
System.out.println("search table memcached_test......");
String beginTime = d.format(new Date());
for(int i = 1;i <= count;i++){
if(i == 0){
pstmt.setInt(1, i);
rs = pstmt.executeQuery();
while(rs.next()){
memcachedvalues = rs.getString(2);
}
}
}
System.out.println("search table memcached_test finish.");
String endTime = d.format(new Date());
useTime = d.parse(endTime).getTime() - d.parse(beginTime).getTime();
long ss = (useTime/1000)%60;//秒
long MM = useTime/60000;//分
System.out.println("Oracle中查找10000条记录的开始时间:"+beginTime);
System.out.println("Oracle中查找10000条记录的结束时间:"+endTime);
System.out.println("Oracle中查找10000条记录的所用时间: "+MM+"分"+ss+"秒");
rs.close();
pstmt.close();
con.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return useTime;
}
以下是连接Memcached并查找10000条数据的方法
/**
* Memcached查找
* @param count
* @return
* @throws ParseException
*/
public static long searchMemcached(int count) throws ParseException{
//初始化SockIOPool,管理Memcached的连接池
String[] servers = {"192.183.3.230:11211"};
SockIOPool pool = SockIOPool.getInstance();
pool.setServers(servers);
pool.setFailover(true);
pool.setInitConn(10);
pool.setMinConn(5);
pool.setMaxConn(250);
pool.setMaintSleep(30);
pool.setNagle(false);
pool.setSocketTO(3000);
pool.setAliveCheck(true);
pool.initialize();
//建立MemcachedClient实例
MemCachedClient memCachedClient = new MemCachedClient();
System.out.println("search 10000 data in Memcached......");
String memcachedvalues = "";
String beginTime = d.format(new Date());
for(int i = 1;i <= count;i++){
//从memcached缓存中按key值取对象
if(i == 0){
memcachedvalues = (String)memCachedClient.get("testData"+i);
}
}
System.out.println("search 10000 data in Memcached finish.");
String endTime = d.format(new Date());
long useTime = d.parse(endTime).getTime() - d.parse(beginTime).getTime();
long ss = (useTime/1000)%60;//秒
long MM = useTime/60000;//分
System.out.println("从Memcached查找10000条记录的开始时间:"+beginTime);
System.out.println("从Memcached查找10000条记录的结束时间:"+endTime);
System.out.println("从Memcached查找10000条记录的所用时间: "+MM+"分"+ss+"秒");
return useTime;
}
运行结果如下:

从运行结果可以看出,同时查找10000条数据,Memcached所用时间比1Oracle所用时间减少了29秒。四、启动多个节点的Memcached
由于实验器材有限,现在在同一台pc机中启动多个Memcached,只要设定端口不一样,这些Memcached之间互相不会干扰。
启动命令如下:
[root@nn init.d]# memcached -d -p 11212 -u memcached -m 64 -c 1024
[root@nn init.d]# memcached -d -p 11213 -u memcached -m 64 -c 1024
其中-d表示在后台运行,-p表示端口号,-u表示用户
启动之后用pstree查看
[root@nn init.d]# pstree

 
3*[memcached───5*[{memcached}]]表示有3组Memcached的进程。
或者用ps aux命令
[root@nn init.d]# ps aux

往多节点的Memcached中插入数据的java代码
/**
* 往节点Memcached插入数据
* @param count
*/
public static void testManyNode(int count){
//初始化SockIOPool,管理Memcached的连接池
String[] servers = {"192.183.3.230:11211","192.183.3.230:11212","192.183.3.230:11213"};
SockIOPool pool = SockIOPool.getInstance();
pool.setServers(servers);
pool.setFailover(true);
pool.setInitConn(10);
pool.setMinConn(5);
pool.setMaxConn(250);
pool.setMaintSleep(30);
pool.setNagle(false);
pool.setSocketTO(3000);
pool.setAliveCheck(true);
pool.initialize();
//建立MemcachedClient实例
MemCachedClient memCachedClient = new MemCachedClient();
String beginTime = d.format(new Date());
for(int i = 1;i <= count;i++){
//将对象加入到memcached缓存
boolean success = memCachedClient.set("node"+i, insertStr+i);
}
}
Memcached的查询结果:

从结果中可以看出,数据分布到Memcached的不同节点上。五、高可用方案repcached
假如Memcached中有一个节点失效了,这个节点所管辖的数据都没有,我们必须重新去数据库中获取数据放入新的节点中,这样会引发数据库性能的波动。这里就需要我们做一个高可用的Memcached,使得Memcached中的每一个节点都有另外一个节点与之一一对应,这两个一一对应的节点中的数据是一模一样的。这样当其中一个节点失效了,另外一个节点就能马上接管缓存的工作,这样就不需要重新从数据库中获取数据库。
下面我们使用repcached来实现Memcached的高可用
1、下载repcached
[root@nn ~]# wget http://downloads.sourceforge.net/repcached/memcached-1.2.8-repcached-2.2.tar.gz

2、杀死本机上的所于喎�"http://www.it165.net/pro/pkqt/" target="_blank" class="keylink">QTWVtY2FjaGVkPC9wPgo8cD5bcm9vdEBubiB+XSMga2lsbGFsbCBtZW1jYWNoZWQ8L3A+CjxwPjOhor3i0bnPwtTYtcRyZXBjYWNoZWQ8L3A+CjxwPltyb290QG5uIH5dIyB0YXIgenh2ZiBtZW1jYWNoZWQtMS4yLjgtcmVwY2FjaGVkLTIuMi50YXIuZ3o8L3A+CjxwPiA8aW1nIHNyYz0="http://www.it165.net/uploadfile/files/2014/0804/20140804180145607.jpg" alt="\">
4、进入所解压的目录
[root@nn ~]# cd memcached-1.2.8-repcached-2.2

5、安装依赖包,运行编译repcached所需要的
[root@nn memcached-1.2.8-repcached-2.2]# yum install libevent-devel
6、开始安装repcached
[root@nn memcached-1.2.8-repcached-2.2]# ./config --enable-replication --program-transform-name=s/memcached/repcached/


安装好之后就会产生一个Makefile文件

7、可以使用Makefile来编译
[root@nn memcached-1.2.8-repcached-2.2]# make
[root@nn memcached-1.2.8-repcached-2.2]# make install

可以看到主穴ky"http://www.it165.net/qq/" target="_blank" class="keylink">qqwstewtcSzzNDy09ByZXBjYWNoZWS6zXJlcGNhY2hlZC1kZWJ1ZzwvcD4KPHA+OKGixvS2r01lbWNhY2hlZLXEuN+/ydPDvK/IuqOo16LS4rK7xNzTw3Jvb3TTw7unwLTG9Lavo6k8L3A+CjxwPltvcmFjbGVAbm4gfl0kIC91c3IvbG9jYWwvYmluL3JlcGNhY2hlZCAtcCAxMTIxMSAtdiAtZDwvcD4KPHA+W29yYWNsZUBubiB+XSQgL3Vzci9sb2NhbC9iaW4vcmVwY2FjaGVkIC1wIDExMjEyIC14IGxvY2FsaG9zdCAtdiAtZDwvcD4KPHA+IDxpbWcgc3JjPQ=="http://www.it165.net/uploadfile/files/2014/0804/20140804180146613.jpg" alt="\">
其中-x表示穴ky"http://www.it165.net/qq/" target="_blank" class="keylink">qq84Mz9uN+/ydPDu/rG96OsyOe5+8rHxuTL+7bLv9rSqtC0yc+hsDq2y7/ausWhsaOsyOe5+8rHxKzIz7bLv9qjqDExMjExo6mjrL7NsrvQ6NKq0LShozwvcD4KPHA+OaGisuLK1KO6tNMxMTIxMbbLv9qy5cjryv2+3aOstb0xMTIxMrbLv9rIpbLp1dKju7TTMTEyMTK2y7/asuXI68r9vt2jrLW9MTEyMTG2y7/ayKWy6dXSPC9wPgo8cD4gPGltZyBzcmM9"http://www.it165.net/uploadfile/files/2014/0804/20140804180147614.jpg" alt="\">
从测试结果可以看出,这个高可用复制是双向的。无论在哪一个节点插入数据,都可以在另外一个节点中查到。六、Memcached的一致性
如果有两个不同终端连接同一Memcached服务,对同一key值进行修改,结果会如何呢?
下面来做实验
1、A终端连接Memcached并set入key为counter的一个值1

用gets命令看出,比用get命令多最后一个数字,这个数字表示这个key的版本号
2、B终端连接Memcached并set入key为counter的一个值2

3、用set命令去改会改变一致性,这里改用cas命令

我们用gets查看当前的版本号是3

用cas最后一个参数表示版本号,如果版本号不一样,不能修改,会有EXISTS提示,表示修改失败;如果版本号一致,就能修改成功。
Memcached的缺点
1、纯内存操作的数据库,关机或者关闭Memcached进程后数据全部丢失;
2、保存字节数,数据类型贫乏,其他数据类型都要通过应用程序来解释,这样应用端要做的事情会很多,服务器端要做的事情就很好很好了;
3、兼容性差,不同编程语言之间不能相互调用;
4、LRU算法导致数据不可控的丢失;
5、一致性处理简单;
6、应用场景有限,难以被看成是完整的数据库产品,仅仅用来做数据库和应用之间的缓存层。

大数据异构环境数据同步工具DataX 与Sqoop 之比较

唐半张 发表了文章 0 个评论 2443 次浏览 2015-10-07 09:33 来自相关话题

从接触DataX起就有一个疑问,它和Sqoop到底有什么区别,昨天部署好了DataX和Sqoop,就可以对两者进行更深入的了解了。 两者从原理上看有点相似,都是解决异构环境的数据交换问题,都支持oracle,mysql,hdfs,hive的互相交换 ...查看全部
从接触DataX起就有一个疑问,它和Sqoop到底有什么区别,昨天部署好了DataX和Sqoop,就可以对两者进行更深入的了解了。
两者从原理上看有点相似,都是解决异构环境的数据交换问题,都支持oracle,mysql,hdfs,hive的互相交换,对于不同数据库的支持都是插件式的,对于新增的数据源类型,只要新开发一个插件就好了,
但是只细看两者的架构图,很快就会发现明显的不同
DataX架构图
 
 
Job: 一道数据同步作业Splitter: 作业切分模块,将一个大任务与分解成多个可以并发的小任务. Sub-job: 数据同步作业切分后的小任务 Reader(Loader): 数据读入模块,负责运行切分后的小任务,将数据从源头装载入DataX Storage: Reader和Writer通过Storage交换数据 Writer(Dumper): 数据写出模块,负责将数据从DataX导入至目的数据地
Sqoop架构图

 
 
DataX 直接在运行DataX的机器上进行数据的抽取及加载。
而Sqoop充分里面了map-reduce的计算框架。Sqoop根据输入条件,生成一个map-reduce的作业,在Hadoop的框架中运行。

从理论上讲,用map-reduce框架同时在多个节点上进行import应该会比从单节点上运行多个并行导入效率高。而实际的测试中也是如此,测试一个Oracle to hdfs的作业,DataX上只能看到运行DataX上的机器的数据库连接,而Sqoop运行时,4台task-tracker全部产生一个数据库连接。调起的Sqoop作业的机器也会产生一个数据库连接,应为需要读取数据表的一些元数据信息,数据量等,做分区。

Sqoop现在作为Apache的顶级项目,如果要我从DataX和Sqoop中间选择的话,我想我还是会选择Sqoop。而且Sqoop还有很多第三方的插件。早上使用了Quest开发的OraOop插件,确实像quest说的一样,速度有着大幅的提升,Quest在数据库方面的经验,确实比旁人深厚。


Transfer highly clustered data more than five times faster than with Sqoop alone
Avoid scalability issues that can occur with Sqoop when data has no primary key or is not stored in primary key order
Reduce CPU by up to 80 percent and IO time by up to 95 percent
Prevent disruption to concurrently running Oracle workload
Get free use of Data Transporter for Hive, a Java command-line utility that allows you to execute a Hive query and insert the results into an Oracle table


在我的测试环境上,一台只有700m内存的,IO低下的oracle数据库,百兆的网络,使用Quest的Sqoop插件在4个并行度的情况下,导出到HDFS速度有5MB/s ,这已经让我很满意了。相比使用原生Sqoop的2.8MB/s快了将近一倍,sqoop又比DataX的760KB/s快了两倍。

另外一点Sqoop采用命令行的方式调用,比如容易与我们的现有的调度监控方案相结合,DataX采用xml 配置文件的方式,在开发运维上还是有点不方便。

近200篇机器学习&深度学习资料分享

治肥虫btz 回复了问题 102 人关注 16 个回复 66189 次浏览 2017-11-07 18:53 来自相关话题

@Robin_TY 老师,能推荐一些强化学习的资料吗,谢谢

回复

skf009 回复了问题 1 人关注 1 个回复 1402 次浏览 2018-12-09 16:47 来自相关话题

近200篇机器学习&深度学习资料分享

回复

治肥虫btz 回复了问题 102 人关注 16 个回复 66189 次浏览 2017-11-07 18:53 来自相关话题

在哪儿下载视频

回复

fish 回复了问题 2 人关注 1 个回复 1472 次浏览 2017-02-18 22:38 来自相关话题

中国人民银行:2015年中国区域金融运行报告

回复

傲风寒 发起了问题 2 人关注 0 个回复 1939 次浏览 2016-07-12 12:12 来自相关话题

80 多个 Linux 系统管理员必备的监控工具

回复

fish 回复了问题 2 人关注 1 个回复 1688 次浏览 2016-05-24 11:20 来自相关话题

【Scala·语法·笔记】1、为主构造器参数形成的字段,设置 protected;2、类中定义同名字段却不报错的坑

曹志翔 发表了文章 0 个评论 1840 次浏览 2015-10-30 00:13 来自相关话题

结论: 1,通过 protected[this],可以把主构造器参数形成的字段的访问级别,设置成受保护类型 class Person(protected[this] var age : Int) { }   2, ...查看全部
结论:
1,通过 protected[this],可以把主构造器参数形成的字段的访问级别,设置成受保护类型
class Person(protected[this] var age : Int) { }
 
2,特定条件下,类定义中存在同名字段,却不会报错。(隐隐感觉会在将来的某个时候弄个Bug出来搞死自己,千万要避免字段同名)
条件:a,主构造器参数,没有 val 或 var;b,类中,定义了同名、非私有字段
class Person(age : Int) { //age 前加 val 或 var。就会报错
protected[this] var age : Int = _ //不写访问级别,或 protected[this],不报错;pribate[this] 报错
}

class Student extends Person(7) {
def schoolAge {
println(if (age > 6) "School Age" else "Preschool")
}
}

object Person extends App {
val s = new Student
s.schoolAge
s.age //无法在类外访问,但可以在子类中访问
}

 
分析过程:
 
ps.    刚抱怨完学习效率不高,又忍不住来扣这么个细节的地方。(耳边响起了玉海老师的嘱咐“学习的内容很多,要抓重点啊!)
不过,在学习这个知识点时,有些意外收获。还是发出来跟大家交流一下。
 
       事情是从陈超老师讲“主构造器参数自动形成类的字段”开始的。其中,有个知识点是“加 val 或 var,则对应的字段为 public;不加,则为 private”,那如果需要这个字段为 protected 该怎么声明呢?情景如下:
class Person(age : Int){
}

class Student extends Person(20){
println(age) // 报错:Not Found。因为 age 是 Person 类的私有类型
}

object Person extends App {
val s = new Student
}

       那么,如何在子类中访问父类“不带 val 或 var 的主构造器参数形成的字段”呢?当然,可以把该字段从主构造器的参数列表中去掉,挪到类中声明。但,只样一来,还需要再定义一个方法,用于设置该字段的值。能否依然“在主构造器的参数列表中”,又能是 protected?
 
       我做了以下尝试:
1,直接在类中定义同名字段。
       但显然 1 个类里,有 2 个同名字段,是不正常的。可万万没想到“没 有 报 错”。而且,类里定义的同名 protected 字段,可以在子类中访问,但就是主构造器参数的值,没有传给该字段。这么做就没有意义了(还得声明个方法设置 age 的值)。
class Person(age : Int){
protected[this] var age : Int = 10
}

class Student extends Person(20){
println(age) // 10 //意外的是,居然没有报错
}

object Person extends App {
val s = new Student
}

2,直接在主构造器参数上设置访问级别,可以有效使用
class Person(protected[this] var age : Int) { //一定要写 protected[this]。如果只写 protected,依然可以在类声明之外访问,却 “没 有 提 示”
}

class Student extends Person(7) {
def schoolAge {
println(if (age > 6) "School Age" else "Preschool")
}
}

object Person extends App {
val s = new Student
s.schoolAge
s.age //无法在类外访问,但可以在子类中访问
}

 

GOOGLE分布式数据库技术演进研究 --从Bigtable、Dremel到Spanner

唐半张 发表了文章 0 个评论 1649 次浏览 2015-10-10 09:57 来自相关话题

GOOGLE分布式数据库技术演进研究           1 引言     ...查看全部
GOOGLE分布式数据库技术演进研究 
        

1 引言 
  
     在传统RDBM系统中,对于事务处理必须保证为一个完整的逻辑处理过程,具备ACID四个特性,A Atonomy事务处理的原子性,要么成功,要么失败 ,C Consistency一致性,数据库必须保持原有约束的关系,数据之间必须符合数据完整性,I Isolation事务处理必须要彼此隔离,由RDBM保证能够并发处理事务,而不需要用户显示的干预,D Durability数据能够被持久化下来,不会出现事务涉及数据丢失的情况。此4个特性是RDBM系统的基础要素和必须遵循的原则。 
  
     事物总是发展的,人类社会信息总量在不断增大,对于RDBM系统管理的数据容量已经从GB级别演进到TB级别,又从TB级别演进到PB级别,数据量不断增大,原有的RDBM已经力不从心。如何解决该问题? 
  
     软件设计很多思想都来自于建筑领域,先回到建筑领域,房屋建造有2要素,建筑高度、基础构造,建筑高度决定基础构造,而基础构造又会影响到建筑高度,建设一栋三层小房,专混结构就可以了,建设十一层房子,就需要上框架结构,摩天大楼,需要采用钢和框架的混合结构方式来支撑。软件设计也有相同的道理,也存在2个要素,一个容量要素,软件能够管理多少数据规模,一个是软件构造要素,软件运行的物理硬件、操作系统、采用何种架构来组织软件各功能模块。二者对应关系,“建筑高度”对应于“容量”,“基础构造”对应于“软件构造”,但是,二者有所不同,由于建筑物需要考虑成本、实用性、安全性和各种限制,建筑高度一定是有限的,总有一个极限,而软件则有所不同,一个大容量系统数据量已经从GB级增加到了TB级,从TB级到PB级,跃升了6个数量级,软件容量呈现指数级的增加,使得软件设计面临比建筑领域更大的挑战。 
  
     如何解决数据容量持续增加带来的挑战,第一 提升系统的计算能力,可以并行对应数据进行分区域或者分块计算,然后对应计算结果进行汇集处理,第二 提升数据的读取速度,单存储节点的读取速度必定存在限制,需要指出多存储节点的DB系统,分布式数据库DDBS系统诞生了,分布式数据库系统可以支持多个存储节点,从GOOGLE的BIGTBALE数据库原理相对应的HBASE数据库,就可以支持多个存储节点,存储节点的数量可以根据要求进行扩展,无理论上限。但是分布式数据库采用对存储节点构造后,带来了一个新的问题,这个问题就是CAP理论的魔咒,CAP为英文Consistent、Availablity、Partition Tolerance的缩写,一致性,可用性、分区容忍性,通常认为CAP理论是只能满足二个要求,不可兼得(这种限制,其实已经被GOOGLE的SPanner系统打破)。常见的DDBS系统可以保证AP,但是无法保证C,一致性,比如HBASE数据库,就无法保证多个存储区域的外部事务的一致性,如果数据跨了多个存储节点,数据可能存在冲突的可能,不一致的可能,无法做到数据表外的事务支持。ACID特性是一个数据库完美解决的要求,但DDBS要满足ACID特性中的原子性和各存储节点数据的外部一致性存在很大的困难,如果对二种不同的特征描述进行关联,AC---Consistent事务的原子性和一致性对应于DDBS一致性属性要求,ID---Availablity,事务的隔离性和持久化对应于DDBS可用性属性要求,而DDBS的Partition Tolerance会带来满足AC或者ID属性的困难,因此很多DDBS会进行特性取舍。 
  
     因此,如果让DDBS系统,使之完全满足ACID的特性,必须要解决数据Partition后带来的困难,数据分成多个存储节点,如何在满足事务隔离和持久化特性的基础上,保证这些不同存储节点上的数据一致性可用性,让DDBS呈现出完全的ACID特性,打破CAP的魔咒,最常用的方式是不同数据节点的同步和全局锁控制,如果采用这种控制机制,必然会带来系统网络同步开销增加,系统的Availability能力下降,而且会出现全局锁瓶颈点,影响到系统的扩展性,出现扩展后的瓶颈点,因此这条解决方案很难走通。那我们是否能够从不一致和不确定基础上,构造出一个可靠和确定的DDBS系统,GOOGLE的SPanner数据库设计思想为解决该问题带来的新的希望,以一种全新的视角和思路来解决该问题,在可以期待的将来,开源社区也能够出现与SPanner方式对应的产品出现,使得DDBS系统应用范围更加宽广。 
  
     综上所述,从CAP特性理论看,面对传统的权威理论,技术上要敢于去挑战,勇于分析,可以另辟蹊径解决技术问题,解决这些问题最大困难还是改善软件开发人员的认识,一旦认识成为定势,势必陷入到死胡同中,同时必须具备深厚的技术功底和高超编程技巧,从开源社区现在还缺乏类似于Spanner技术看,充分说明了这个技术要从思想走到实现,存在非常多的技术设计难点,要搞定这些设计难点绝无易事,但值得庆幸的,已经有人做到了,而且还在不断的完善它。 
  
     GOOGLE的分布式数据库系统从BIGTABLE的正式推出后,先后对外发布了Bigtable、Dremel、Spanner等不同的分布式数据库产品,有的是引入新的设计实现,有的是针对原有的技术进行改进和优化,用于满足GOOGLE不同的应用场景,支持日益增加的数据量管理要求。 
  
     GOOGLE分布式数据库技术,从个人理解看,可以分为三个阶段,第一阶段以Bigtable产品为代表,实现了数据的分布式存储、行数据的事务性管理和较好的扩展性,从存储WEB页面而生,创造性提出了KEY-VALUE这种MAP数据结构,并广泛应用到GOOGLE的各种应用中,与GOOGLE的MapReduce GFS技术搭配,构成了GOOGLE分布式云计算的三架马车,对应开源社区推出HBASE产品,也在近年得到了广泛应用。 
  
     第二个阶段以Dremel产品为代表,Dremel产品采用了与Bigtable不同的数据结构,立足实时对于海量数据进行分析,据说在秒级可以完成PB级别的数据分析和处理,可以做是分布式数据库实时处理的杰作,其实时处理能力达到令人惊艳的速度。 
  
     第三阶段以Spanner数据库技术为代表,Spanner数据库在可以做到多数据表事务一致性管理,利用原子时钟(TrueTime)和Paxos协议解决了分布式数据库多表事务一致性管理的难题,打破的CAP不可三者兼得的理论神话,使得分布式数据库技术得到了革命性的进步。 
  
     严格来讲Dremel与Bigtable和Spanner解决的问题有所不同,Dremel侧重于对应海量数据的实时处理,而Bigtable和Spanner更侧重于传统的关系型数据库支持功能对齐和替换,并不是简单产品替换关系。从GOOGLE分布式数据库技术发展历程看,这些技术得以成功推出,有创造性的新锐视角和解决方案,更有其坚持在廉价PC服务器上面构筑海量数据处理系统的理想和情怀,更有起高超的技术实力和团队合作,这些因素的结合,使得技术难关被不断的突破,分布式数据库产品得以大成,这些产品的确值得技术人员去深入学习和体会。 
  
     为了更好的对比和分析GOOGLE的分布式数据库技术,本文从Bigtable、Dremel、Spanner数据模型、系统架构、数据查询原理、应用场景和关键技术进行深入分析,最后对于其特点进行对比,从而使得读者对应GOOGLE的分布式数据库技术有一个初步的认识。 
  
  
2 BIGTABLE开山壁祖 
  
2.1Bigtable的数据模型 
  
2.1.1Bigtable的Key-Value数据结构 
  
     Bigtable采用Key-Value数据结构,Key由行关键字、列关键字、时间戳组合而成,Value为对应数据内容,行关键字和列关键字都是字符串数据类型,时间戳是一个64位的长整数,精确到毫秒的时间戳,这三个属性在一个数据库中全局唯一,由Key和Value构成的KV数据结构称为一个数据项,考虑到分布式数据库的多副本的特性,数据项会按照时间戳进行排序,并对于过期的数据项进行过期回收,其数据结构如图2-1所示。 
  

 
  
图2-1 Bigtable KV结构示意图 
  
2.1.2Bigtable的数据模型层次 
  
     Bigtable数据模型由下而上,主要部件可以初分为四个层面,最底层为SSTABLE,存放在GFS分布式文件系统中,为数据存储MAP结构体,第二层TABLET结构体,一个表可以由一个或者多个Tablet构成,由一系列的SSTABLE构成,第三层TABLET服务器,管理一组TABLET数据体,最上层Bigtable数据库,由多个TABLET服务器和一个Master服务器、客户端访问连接支持软件构成,最终形成了一个分布式数据库,对外提供数据库服务,层次关系如图2-2所示。 
  

 
  
图2-2 Bigtable各级数据模型视图 
  
     数据项基于一种叫做SSTABLE的数据格式,SSTABLE是一种持久化、排序的、不可更改的MAP数据结构,每一个SSTABLE由一系列数据块构成,在每一个SSTABLE的最后存储着块索引,SSTABLE使用这些索引来定位数据块,在每一个SSTABLE被打开时,块索引会自动加载到内存中。SSTABLE提供了二种数据访问方式,第一种方式,使用二分法查找内存中的块索引,根据对应的块索引把对应的数据块读取到内存中,此种方式只会发起一次磁盘寻址,第二种方式是把整个SSTABLE都整体加载到内存中。SSTABLE基于GOOGLE GFS全局文件系统基础上,实现对应文件系统层面的负载均衡和IO能力分担, 
  
     从特点看,BIGTABLE不直接支持对应数据的修改操作,通过时间戳方式来间接支持数据修改。数据读取方式,提供了二种不同的机制,二分法的块索引定位加载,类似于ORACLE提供的索引访问方式;SSTABLE的全部加载,类似于ORACLE提供的全表扫描机制,从技术角度看,不管分布式数据库技术本身如何发展,对小粒度数据精确加载和整体数据加载的场景,从这点看,所有的数据库存储结构应该都是殊途同归。 
  
     一个TABLET由TABLET Log存储TABLET的提交数据的Redo记录数据,MEMTABLE是内存缓存,存储最近访问的记录数据,数据持久化到一组SSTABLE文件中。 
  
     最上层Bigtable数据库服务层,对外提供Bigtable的数据库服务功能,为Bigtable的最顶层结构。 
  
2.2Bigtable的系统架构 
  
     Bigtable由客户端连接库、Master服务器、TABLET服务器组构成,客户端链接库提供数据库客户端访问功能,提供服务端访问,比如对于数据寻址的缓存信息; Master服务器负责TABLET服务器组的管理,根据负载情况,可以动态的增加和删除TABLET服务器,维护TABLET服务器组;TABLET服务器管理该服务器上面的TABLET集合,完成TABLET数据读取和写入操作,当TABLET太大时,对TABLET进行拆分,在一个Bigtable中只能有一个Master服务器,通过Chubby保证Master服务器的唯一性;Chubby服务提供了TABLET根信息服务、系统Master管理和TABLET服务出错的善后处理任务,保存Bigtable数据库Schema信息。Bigtable整个系统架构如图2-3所示。 
  

 
  
图2-3 Bigtable系统架构图 
  
2.3Bigtable的数据查询 
  
2.3.1Bigtable的数据定位 
  
     Bigtable数据查询,首先是对于数据所在tablet进行定位,Bigtable的位置定位,分为三个步骤。 
  
     第一步,客户端程序在缓存中查找tablet位置是否在缓存中存在,如果在缓存中存在就直接读取,如果不存在,通过Chubby服务器查询tablet的根节点,取到Bigtable根节点tablet信息; 
  
     第二步,根据Bigtable根节点的tablet信息,找到数据对应METADATA的数据表,该数据表中存储着所有的用户数据tablet的位置信息; 
  
     第三步,根据METADATA的数据表存储的用户数据tablet信息,找到数据对应tablet信息,根据该位置信息,读取到tablet数据到客户端。 
  
2.3.2Bigtable的数据读取 
  
     Bigtable数据读取时以Tablet为单位,必须读取到构成该Tablet所有涉及到SSTABLE,发起读取操作时,首先要对操作完整性和权限做检查,检查通过后,首先在Tablet服务器所在的缓存里面查找,Bigtable提供二级缓存缓存,一种是以Key-Value形式的一级数据缓存,如果在这种级别中的缓存中无法找到,访问二级数据缓存,二级数据是SSTABLE的BLOCK数据缓存,对于热点局部性数据来讲,这种BLOCK环境命中率很高,对于系统性能改善更加有效。 
  
     在数据缓存中如果没有找到对应的读取数据,启动数据定位的三个步骤,完成对于TABLET的位置信息读取,TABLET信息读取转换为对应SSTABLE数据,根据SSTABLE数据是否进行了压缩,对于涉及到该TABLET的SSTABLE进行解压操作,完成读取后返回到客户端。 
  
2.3.3Bigtable的数据写入 
  
     Bigtable数据写入时,首先是坚持该操作数据格式是否正确,并判断该操作发起者是否有该操作的权限,权限判断是通过存储在Chubby服务器上面的权限控制表来判断。判断操作发起者具备该操作的权限后,会发起具体写入数据动作,该动作是一个事务操作,操作必须保证对于Tablet中数据写入成功,否则不会写入TABLET,如果写入成功后,会把提交该操作修改到Tablet对应的Redo日志中,同时该写入内容会插入到MEMTABLE中。 
  
2.4应用场景和关键技术 
  
2.4.1应用场景 
  
     Bigtable从数据存储特点看,属于行式数据库存储模型,虽然Bigtable具备把列分配到不同的SSTABLE,形成不同的列簇的情况。由于Bigtable采用按照行存储模型,因此对于数据表中的一行可以实现事务性操作,实现数据单表上的事务控制,虽然Bigtable提供了批量数据的访问接口,但是还不支持跨行的事务操作。 
  
     而Bigtable采用的KV结构,可以按照Key中的行关键字和列关键字,对于海量数据进行列维度和行维度的切片管理,分别到同步到TABLET服务器上面。在一个BIGTABLE系统中,TABLET服务器的数量可以根据负载要求,做动态的调整,对于TABLET服务器数量无上限,这样就可以支持对于数据库负载的水平扩展,根据GOOGLE提供的数据,单TABLET服务器在BLOCK为64KB时,KEY-VALUE中的VALUE为1K的长度是,可以支持1200次请求,一个TABLET服务器可以处理75M数据读取,当TABLET服务器数量增加后,其读取数据的能力可以提升,当并不会严格的遵循线性关系,可以从GOOGLE在BIGTABLE中提供的测试数据看出,TABLET服务器增加和整体系统吞吐能力提升的关系,参见图2-4。 
  
     可以看出对于TABLET的扫描和在TABLET中内存中的随机读,提升效率最为明显,这是由于在TABLET服务器增加过程中,此类操作都是在内存中进行,因此服务器越多,支持吞吐量就会更大。顺序读、随机写、顺序写提升比率低于前两种操作,顺序读由于读取SSTABLE到TABLET服务器时,一个BLOCK被读取时,SSTABLE中相邻接的BOLCK会被加载到BLOCK缓存中,后续会在缓存中被读取到该BLOCK,不在需要在从SSTABLE中读取。随机写和序列写在采用了批量提交的方式,通过数据流的方式来进行处理,此种操作方式随TABLET的增加,提升效果还是比较明显。谁TABLET服务器提升效果最低的为随机读,原因是随机读取时,为了访问KEY-VALUE值中,VALUE为1K的数据时,会同步把整个SSTABLE中一个BLOCK都读取出来,当随机读取请求数量增加时,整个网络带宽会急剧上升,导致每个TABLET服务器吞吐能力下降,整个系统能够处理的随机请求数量变少。 
  

 
  
图2-4 tablet服务器与系统IO吞吐量关系图 
  
2.4.2关键技术 
  
     从Bigtable应用场景看,BIGTABLE系统设计需要解决下面的核心技术 
  
     技术难题一 系统鲁棒性 
  
     对于分布式环境,对于网络受损、内存数据损坏、时间误差,机器硬件损坏这这种常见的问题出现后,系统的容错处理能力,要求Bigtable系统具有较强的鲁棒性和容错性。 
  
     技术难题二 系统容错和负载与效率的平衡 
  
     TABLET副本数量增加会增加系统容错能力,但是会增加系统管理代价和同步成本,效率就会相应的下降;系统负载和效率增加,有限制了数据副本数量、副本数量下降会导致系统容错性减低,GOOGLE具体处理算法,这些在GOOGLE对外公布的论文中没有进行详细的描述,在开源产品HBASE遇到问题看,这些都是一些技术难点。 
  
     技术难题三 不同查询特点的应对 
  
     由于系统不同查询特点,数据特点,对于BIGTABLE中的各种关键配置参数的配置方式,比如SSTABLE中BLOCK大小的选择、缓存算法设计细节。 
  
     上面的这些技术难题相信已经被GOOGLE很好的解决,不过这些解决经验和具体技术细节GOOGLE并没有进行公开。 
  
2.4.3BIGTABLE后续发展 
  
     Bigtable做为GOOGLE公布的第一代分布式数据库技术,结合下层的GFS文件系统,上结合MAP-REDUCE框架完成数据的分片计算,构成了GOOGLE的分布式计算体系。而BIGTABLE目前只有在一个系统只支持一个Master服务器,同时对于多表事务性无法支持,这些都是Bigtable后续要解决的技术问题。对于不同特点数据库查询请求,不同特点存放数据, BIGTABLE的关键参数应该如何配置,是否有一种完美的配置参数,可以完全满足各种不同特点的查询场景,从目前来看,还不能做到的,还必须根据数据特点,对BIGTABLE的参数做相应定制,包括一些BIGTABLE要使用的GFS文件配置参数、网络配置参数,这些都成为使用Bigtable数据库过程中一些较为复杂问题,整个Bigtable数据库使用技术门槛仍然比较高。 
  
3  Dremel 
  
3.1背景 
  
     大规模交互性数据分析处理在整个行业中应用越来越广泛,对于交互型分析对于数据处理的响应时间要求比较高,而原有Bigtable数据库设计上并没有考虑对于交互式场景要求,对于大大规模交互数据分析处理响应性不够,因此Dremel就应运而生,Dremel解决大规模交互数据分析的实时性问题,可以做到秒级的数据响应,GOOGLE在测试中宣称,可以在3秒钟的时间处理1PB数据。 
  
     在大规模交互数据分析中,会有这样一种场景,需要参加数据分析的原始数据量非常大,但是最终结果集数据量会很小,往往是一个分析结果或者是汇总型的数据,这种场景就是大型交互时数据分析的典型场景。从GOOGLE分布式数据库产品的战略定位看,Dremel和Bigtable的定位有所不同,Dremel更适合对于交互式场景,而Bigtable通常会跟MapReduce配置,做为大数据处理搭配处理,当然Dremel同样可以与MAPReduce结合使用。因此Dremel并不是取代Bigtable的一种分布式数据库,而是一种补充,从技术演进角度看,由于Dremel数据库公开时间晚于Bigtable,因此做为Google第二代分布式数据库代表之一。 
  
3.2Dremel的数据模型 
  
3.2.1Dremel嵌套列数据模型 
  
     Dremel采用是嵌套列数据模型,该数据模型把嵌套数据拆分为列结构加以存储,在查询时把数据重建为嵌套数据,原有列存储数据库通常属于关系型数据库,在嵌套类型的数据处理还未采用这种结构,GOOGLE创造性的把嵌套数据处理为列数据库,并且技术指标还能大幅提升,满足大型数据的交互式查询要求,不得不说这个GOOGLE的一个新创造,但为什么是这样的列嵌套结构,而不是其他数据结构,这点GOOGLE并没有进行介绍说明,因此这一点理解上面有一定困难,不过在后续介绍中,会发现这种结构在数据查询处理时的优势和特点。 
  
     一提起数学模型,很多程序员就会晕,不过仔细看看,稍微有一点数据知识的人,应该就能够看明白,Dremel的数学模型如下: 
  
     π = dom |  
  
     π是数据库中的一条记录类型,Ai是数据库记录的一个字段,*为一个重复字段,?为可选字段,这个数据模型说明该一条记录是由多个字段构成,字段类型由可选字段、必选字段、重复字段组成,可选字段可以出现,也可以不出现;必选字段必须会出现,重复字段则会出现多次。在GOOGLE公开的资料中,在图3-1给出嵌套记录样例和数据模式定义,Document是一个网页类型的数据模式,该网页有整形的DocId和可选的Link分组属性,Link分组属性包含了多个Backword和Forward可 
  

 
  
图3-1 嵌套式记录样例和数据模式定义 
  
     重复的整型属性构成列表,一个网页包括了多个可以重复的名字分组,每个名字分组,可以包括多个语言分组和可选的URL地址,语言分组包括必选的CODE字段,可选的Country属性。整个Document嵌套层次关系如图3-2: 
  

 
  
图3-2嵌套层次关系图 
  
3.2.2Dremel的重复深度和定义深度 
  
     在Dremel中如何对于嵌套式数据结构,在列存储后进行重建,依赖于二个重要参数,一个是Repetition Level重复深度,一个是Definition Level定义深度.重复深度和定义深度仅针对重复字段来计算层次,用于描述这个值什么重复字段的此值重复了,以此确定重复的位置;定义深度描述多个嵌套层次的路径上,有多少个字段是有值的,通过这二个参数可以完成嵌套数据的列式化存储。 
  
3.2.3Dremel的存储和重构 
  
     Dremel存储是采用列式模型,按照列进行嵌套式数据的存储,读取数据时,根据定义在列式模型中的数据,按照顺序,把列式存储数据还原到原有的嵌套式数据结构。 
  
3.3Dremel的系统架构和查询 
  
     Dremel采用的是多层次服务树架构,最上层Dremel的根服务器,根服务器接收所有的查询请求,读取数据库相关的元数据,并把相关请求下发到下一级查询服务器查询。中间层查询服务器负责根服务器请求的派发和叶子服务器的查询结果的处理。叶子服务器与具体存储层直接通讯,完成存储系统上相关数据的读取和查询动作,整个系统架构图如下图所示。 
  

 
  
图3-3 Dremel多层次查询树架构 
  
     Dremel查询语句基于SQL语法,并根据自身列存储模型进行了定制,构成在Dremel的存储结构上面高效的执行,对于Dremel查询树结构,会对于根查询服务器收到查询请求进行层层拆分,最终传递到叶节点的查询服务器,叶节点查询服务器获取的数据结果后,进行过滤和汇总这样的计算,然后在上传到上级查询服务器,层层汇总结果后,最终返回结果数据。 
  
     Dremel支持多客户端并发查询,通常情况下查询请求会被同时运行,查询派发器,会根据系统的负载情况和查询优先级进行一定的查询调度操作,并提供容错性,对于查询节点响应缓慢和访问数据不达的情况进行调整。 
  
3.4应用场景和关键技术 
  
3.4.1应用场景 
  
     Dremel定位为交互系统大型数据处理的数据库系统,适合数据读取数据量大,但是返回数据量小的场景,对于返回数据量大的场景,采用Dremel就不太合适。 
  
3.4.2关键技术 
  
     Dremel采用的嵌套式列存储结构+多层次查询查询树,大型数据处理快速响应的关键,列存储结构可以只对查询关心的列数据读取,多层次查询树结构对于数据查询的拆分和聚合的方式有效的匹配,这是Dremel在该场景速度快于Bigtable的关键技术; 
  
     Dremel号称可以在秒级进行1PB级的数据

memcached

唐半张 发表了文章 0 个评论 2066 次浏览 2015-10-10 09:56 来自相关话题

一、Memcached概念 Memcached是NoSQL产品之一,是一个临时性键值存储NoSQL数据库,过去被大量使用在 ...查看全部
一、Memcached概念
Memcached是NoSQL产品之一,是一个临时性键值存储NoSQL数据库,过去被大量使用在互联网网站中,作为应用和数据库之间的缓存层,大大提高查询和访问速度。
Memcached有以下特点:
1、全内存运转:数据从来不保存在硬盘中,机器一重启,数据就全部没有了,所有又称临时性数据库;
2、哈希方式存储:
3、简单文本协议进行数据通信:不需要特定二进制代码,只需要用telnet连通memcached的监听端口,打入简单浅显的代码就能操作;
4、只操作字符型数据:无论往memcached放入什么,都是以字节的方式来处理。还原成数组、哈希表、字符串和数值等都交给应用层来解释。应用读写memcached的数据时,进行序列化和反序列化,把其解释成应用所能理解的数据类型;
5、集群也由应用进行控制,采用一致性散列(哈希)算法。二、安装Memcached
1、在linux上搭建yum环境
2、使用yum命令进行安装Memcached的rpm包
[root@nn init.d]# yum install memcached

3、启动Memcached,
首先要cd到相应目录
[root@nn ~]# cd /etc/rc.d/init.d/
运行memcached安装脚本
[root@nn init.d]# ./memcached start

4、查看Memcached是否启动
[root@nn init.d]# pstree

表示Memcached进程被启动了,下面开了5个线程
或者使用[root@nn init.d]# ps aux命令

memcached -d -p 11211 -u memcached -m 64 -c 1024 -P /var/run/memcached/memcached.pid
-d表示程序要后台化运行,-p指定端口,-u表示用memcached这个身份来运行,后面的都是memcached的控制参数
5、连接Memcached
[root@nn init.d]# telnet localhost 11211

三、Memcached常用命令
命令格式:


参数说明如下:

set/add/replace

查找关键字

客户机使用它存储关于键值对的额外信息,用于指定是否压缩,0不压缩,1压缩

该数据的存活时间,0表示永远

存储字节数

存储的数据块(可直接理解为key-value结构中的value)
1、增加:set、add、cas
2、获取:get、gets、
3、追加:append、prepend
4、删除:delete
5、清除所有:flush_all
6、加减:incr、decr
7、退出:quit
三、用java连接Memcached
目前java提供了三种API供我们实现与Memcached的连接和存取
1、memcached client for java
较早推出的memcached JAVA客户端API,应用广泛,运行比较稳定。
2、pymemcached
A simple, asynchronous, single-threaded memcached client written in java. 支持异步,单线程的memcached客户端,用到了java1.5版本的concurrent和nio,存取速度会高于前者,但是稳定性不好,测试中常报timeOut等相关异常。
3、xmemcached
XMemcached同样是基于java nio的客户端,java nio相比于传统阻塞io模型来说,有效率高(特别在高并发下)和资源耗费相对较少的优点。传统阻塞IO为了提高效率,需要创建一定数量的连接形成连接池,而nio仅需要一个连接即可(当然,nio也是可以做池化处理),相对来说减少了线程创建和切换的开销,这一点在高并发下特别明显。因此XMemcached与Spymemcached在性能都非常优秀,在某些方面(存储的数据比较小的情况下)Xmemcached比Spymemcached的表现更为优秀,具体可以看这个Java Memcached Clients Benchmark。
本文章使用memcached client for java为例
在使用java连接远程的PC机的Memcached时,记得保证两台机都开启telnet服务,并且本机能telnet通远程机,远程机必须关闭防火墙。
实例代码1:(java连接Memcached并实现数据的存取)
import com.danga.MemCached.MemCachedClient;
import com.danga.MemCached.SockIOPool;
public class memcachedTest {
public static void main(String[] args) {
//初始化SockIOPool,管理Memcached的连接池
String[] servers = {"192.183.3.230:11211"};
SockIOPool pool = SockIOPool.getInstance();
pool.setServers(servers);
pool.setFailover(true);
pool.setInitConn(10);
pool.setMinConn(5);
pool.setMaxConn(250);
pool.setMaintSleep(30);
pool.setNagle(false);
pool.setSocketTO(3000);
pool.setAliveCheck(true);
pool.initialize();
//建立MemcachedClient实例
MemCachedClient memCachedClient = new MemCachedClient();
for(int i = 0;i < 100000;i++){
//将对象加入到memcached缓存
boolean success = memCachedClient.set(""+i, "hello!");
}
for(int i = 0;i < 100000;i++){
//从memcached缓存中按key值取对象
String result = (String)memCachedClient.get(""+i);
System.out.println(String.format("get(%d):%s", i,result+i));
}
}
}
四、测试Memcached性能
为性能对比测试准备数据
1、插入数据到oracle
/**
* 插入测试数据到oracle数据库
* @param count插入记录数
* @return
*/
public static boolean insertIntoOracle(int count){
try {
con = dbConn("feng","feng");
if(con == null){
System.out.println("连接失败");
System.exit(0);
}
System.out.println("truncate table memcached_test......");
sql = "truncate table memcached_test";
pstmt = con.prepareStatement(sql);
rs = pstmt.executeQuery();
System.out.println("truncate table memcached_test finish.");
System.out.println("insert "+count+" values");
sql = "insert into memcached_test (memcachedId,memcachedvalues) values (?,?)";
pstmt = con.prepareStatement(sql);
for(int i = 1;i <= count;i++){
pstmt.setInt(1, i);
pstmt.setString(2, "Memcached is a good thing.I like it very much !-----------"+i);
pstmt.executeUpdate();
}
System.out.println("insert "+count+" values finish.");
rs.close();
pstmt.close();
con.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return true;
}
public static Connection dbConn(String name,String pass) throws ClassNotFoundException, SQLException{
Connection conn = null;
Class.forName("oracle.jdbc.driver.OracleDriver");
conn = DriverManager.getConnection("jdbc:oracle:thin:@192.183.3.230:1522:myorcl",name,pass);
return conn;
}
2、插入数据到Memcached
/**
* 插入测试数据到Memcached
* @param count插入记录数
* @return
*/
public static boolean insertIntoMemcached(int count){
//初始化SockIOPool,管理Memcached的连接池
String[] servers = {"192.183.3.230:11211"};
SockIOPool pool = SockIOPool.getInstance();
pool.setServers(servers);
pool.setFailover(true);
pool.setInitConn(10);
pool.setMinConn(5);
pool.setMaxConn(250);
pool.setMaintSleep(30);
pool.setNagle(false);
pool.setSocketTO(3000);
pool.setAliveCheck(true);
pool.initialize();
//建立MemcachedClient实例
MemCachedClient memCachedClient = new MemCachedClient();
System.out.println("insert "+count+" values into memcached......");
for(int i = 1;i < count;i++){
//将对象加入到memcached缓存
boolean success = memCachedClient.set("testData"+i, insertStr+i);
}
System.out.println("insert "+count+" values into memcached finish.");
return true;
}
Main函数调用这两个方法后,会将count条记录,值为insertData,插入到Oracle数据和set进Memcached中。
 
1、比较同时插入100000条数据的时间

从运行结果可以看出,插入10万条数据到Memcached比插10万条数据入Oracle所用时间有一个质的减少。
2、比较查询时间
以下是连接oracle数据并查找10000条数据的方法
/**
* oracle数据库查找
* @param count记录数
* @return
* @throws ParseException
*/
public static long searchOracle(int count) throws ParseException{
long useTime = 0;
try {
con = dbConn("feng","feng");
if(con == null){
System.out.println("连接失败");
System.exit(0);
}
StringBuffer sql =new StringBuffer("select memcachedid,memcachedvalues from memcached_test where memcachedid = ?");
pstmt = con.prepareStatement(sql.toString());
String memcachedvalues = "";
System.out.println("search table memcached_test......");
String beginTime = d.format(new Date());
for(int i = 1;i <= count;i++){
if(i == 0){
pstmt.setInt(1, i);
rs = pstmt.executeQuery();
while(rs.next()){
memcachedvalues = rs.getString(2);
}
}
}
System.out.println("search table memcached_test finish.");
String endTime = d.format(new Date());
useTime = d.parse(endTime).getTime() - d.parse(beginTime).getTime();
long ss = (useTime/1000)%60;//秒
long MM = useTime/60000;//分
System.out.println("Oracle中查找10000条记录的开始时间:"+beginTime);
System.out.println("Oracle中查找10000条记录的结束时间:"+endTime);
System.out.println("Oracle中查找10000条记录的所用时间: "+MM+"分"+ss+"秒");
rs.close();
pstmt.close();
con.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return useTime;
}
以下是连接Memcached并查找10000条数据的方法
/**
* Memcached查找
* @param count
* @return
* @throws ParseException
*/
public static long searchMemcached(int count) throws ParseException{
//初始化SockIOPool,管理Memcached的连接池
String[] servers = {"192.183.3.230:11211"};
SockIOPool pool = SockIOPool.getInstance();
pool.setServers(servers);
pool.setFailover(true);
pool.setInitConn(10);
pool.setMinConn(5);
pool.setMaxConn(250);
pool.setMaintSleep(30);
pool.setNagle(false);
pool.setSocketTO(3000);
pool.setAliveCheck(true);
pool.initialize();
//建立MemcachedClient实例
MemCachedClient memCachedClient = new MemCachedClient();
System.out.println("search 10000 data in Memcached......");
String memcachedvalues = "";
String beginTime = d.format(new Date());
for(int i = 1;i <= count;i++){
//从memcached缓存中按key值取对象
if(i == 0){
memcachedvalues = (String)memCachedClient.get("testData"+i);
}
}
System.out.println("search 10000 data in Memcached finish.");
String endTime = d.format(new Date());
long useTime = d.parse(endTime).getTime() - d.parse(beginTime).getTime();
long ss = (useTime/1000)%60;//秒
long MM = useTime/60000;//分
System.out.println("从Memcached查找10000条记录的开始时间:"+beginTime);
System.out.println("从Memcached查找10000条记录的结束时间:"+endTime);
System.out.println("从Memcached查找10000条记录的所用时间: "+MM+"分"+ss+"秒");
return useTime;
}
运行结果如下:

从运行结果可以看出,同时查找10000条数据,Memcached所用时间比1Oracle所用时间减少了29秒。四、启动多个节点的Memcached
由于实验器材有限,现在在同一台pc机中启动多个Memcached,只要设定端口不一样,这些Memcached之间互相不会干扰。
启动命令如下:
[root@nn init.d]# memcached -d -p 11212 -u memcached -m 64 -c 1024
[root@nn init.d]# memcached -d -p 11213 -u memcached -m 64 -c 1024
其中-d表示在后台运行,-p表示端口号,-u表示用户
启动之后用pstree查看
[root@nn init.d]# pstree

 
3*[memcached───5*[{memcached}]]表示有3组Memcached的进程。
或者用ps aux命令
[root@nn init.d]# ps aux

往多节点的Memcached中插入数据的java代码
/**
* 往节点Memcached插入数据
* @param count
*/
public static void testManyNode(int count){
//初始化SockIOPool,管理Memcached的连接池
String[] servers = {"192.183.3.230:11211","192.183.3.230:11212","192.183.3.230:11213"};
SockIOPool pool = SockIOPool.getInstance();
pool.setServers(servers);
pool.setFailover(true);
pool.setInitConn(10);
pool.setMinConn(5);
pool.setMaxConn(250);
pool.setMaintSleep(30);
pool.setNagle(false);
pool.setSocketTO(3000);
pool.setAliveCheck(true);
pool.initialize();
//建立MemcachedClient实例
MemCachedClient memCachedClient = new MemCachedClient();
String beginTime = d.format(new Date());
for(int i = 1;i <= count;i++){
//将对象加入到memcached缓存
boolean success = memCachedClient.set("node"+i, insertStr+i);
}
}
Memcached的查询结果:

从结果中可以看出,数据分布到Memcached的不同节点上。五、高可用方案repcached
假如Memcached中有一个节点失效了,这个节点所管辖的数据都没有,我们必须重新去数据库中获取数据放入新的节点中,这样会引发数据库性能的波动。这里就需要我们做一个高可用的Memcached,使得Memcached中的每一个节点都有另外一个节点与之一一对应,这两个一一对应的节点中的数据是一模一样的。这样当其中一个节点失效了,另外一个节点就能马上接管缓存的工作,这样就不需要重新从数据库中获取数据库。
下面我们使用repcached来实现Memcached的高可用
1、下载repcached
[root@nn ~]# wget http://downloads.sourceforge.net/repcached/memcached-1.2.8-repcached-2.2.tar.gz

2、杀死本机上的所于喎�"http://www.it165.net/pro/pkqt/" target="_blank" class="keylink">QTWVtY2FjaGVkPC9wPgo8cD5bcm9vdEBubiB+XSMga2lsbGFsbCBtZW1jYWNoZWQ8L3A+CjxwPjOhor3i0bnPwtTYtcRyZXBjYWNoZWQ8L3A+CjxwPltyb290QG5uIH5dIyB0YXIgenh2ZiBtZW1jYWNoZWQtMS4yLjgtcmVwY2FjaGVkLTIuMi50YXIuZ3o8L3A+CjxwPiA8aW1nIHNyYz0="http://www.it165.net/uploadfile/files/2014/0804/20140804180145607.jpg" alt="\">
4、进入所解压的目录
[root@nn ~]# cd memcached-1.2.8-repcached-2.2

5、安装依赖包,运行编译repcached所需要的
[root@nn memcached-1.2.8-repcached-2.2]# yum install libevent-devel
6、开始安装repcached
[root@nn memcached-1.2.8-repcached-2.2]# ./config --enable-replication --program-transform-name=s/memcached/repcached/


安装好之后就会产生一个Makefile文件

7、可以使用Makefile来编译
[root@nn memcached-1.2.8-repcached-2.2]# make
[root@nn memcached-1.2.8-repcached-2.2]# make install

可以看到主穴ky"http://www.it165.net/qq/" target="_blank" class="keylink">qqwstewtcSzzNDy09ByZXBjYWNoZWS6zXJlcGNhY2hlZC1kZWJ1ZzwvcD4KPHA+OKGixvS2r01lbWNhY2hlZLXEuN+/ydPDvK/IuqOo16LS4rK7xNzTw3Jvb3TTw7unwLTG9Lavo6k8L3A+CjxwPltvcmFjbGVAbm4gfl0kIC91c3IvbG9jYWwvYmluL3JlcGNhY2hlZCAtcCAxMTIxMSAtdiAtZDwvcD4KPHA+W29yYWNsZUBubiB+XSQgL3Vzci9sb2NhbC9iaW4vcmVwY2FjaGVkIC1wIDExMjEyIC14IGxvY2FsaG9zdCAtdiAtZDwvcD4KPHA+IDxpbWcgc3JjPQ=="http://www.it165.net/uploadfile/files/2014/0804/20140804180146613.jpg" alt="\">
其中-x表示穴ky"http://www.it165.net/qq/" target="_blank" class="keylink">qq84Mz9uN+/ydPDu/rG96OsyOe5+8rHxuTL+7bLv9rSqtC0yc+hsDq2y7/ausWhsaOsyOe5+8rHxKzIz7bLv9qjqDExMjExo6mjrL7NsrvQ6NKq0LShozwvcD4KPHA+OaGisuLK1KO6tNMxMTIxMbbLv9qy5cjryv2+3aOstb0xMTIxMrbLv9rIpbLp1dKju7TTMTEyMTK2y7/asuXI68r9vt2jrLW9MTEyMTG2y7/ayKWy6dXSPC9wPgo8cD4gPGltZyBzcmM9"http://www.it165.net/uploadfile/files/2014/0804/20140804180147614.jpg" alt="\">
从测试结果可以看出,这个高可用复制是双向的。无论在哪一个节点插入数据,都可以在另外一个节点中查到。六、Memcached的一致性
如果有两个不同终端连接同一Memcached服务,对同一key值进行修改,结果会如何呢?
下面来做实验
1、A终端连接Memcached并set入key为counter的一个值1

用gets命令看出,比用get命令多最后一个数字,这个数字表示这个key的版本号
2、B终端连接Memcached并set入key为counter的一个值2

3、用set命令去改会改变一致性,这里改用cas命令

我们用gets查看当前的版本号是3

用cas最后一个参数表示版本号,如果版本号不一样,不能修改,会有EXISTS提示,表示修改失败;如果版本号一致,就能修改成功。
Memcached的缺点
1、纯内存操作的数据库,关机或者关闭Memcached进程后数据全部丢失;
2、保存字节数,数据类型贫乏,其他数据类型都要通过应用程序来解释,这样应用端要做的事情会很多,服务器端要做的事情就很好很好了;
3、兼容性差,不同编程语言之间不能相互调用;
4、LRU算法导致数据不可控的丢失;
5、一致性处理简单;
6、应用场景有限,难以被看成是完整的数据库产品,仅仅用来做数据库和应用之间的缓存层。

大数据异构环境数据同步工具DataX 与Sqoop 之比较

唐半张 发表了文章 0 个评论 2443 次浏览 2015-10-07 09:33 来自相关话题

从接触DataX起就有一个疑问,它和Sqoop到底有什么区别,昨天部署好了DataX和Sqoop,就可以对两者进行更深入的了解了。 两者从原理上看有点相似,都是解决异构环境的数据交换问题,都支持oracle,mysql,hdfs,hive的互相交换 ...查看全部
从接触DataX起就有一个疑问,它和Sqoop到底有什么区别,昨天部署好了DataX和Sqoop,就可以对两者进行更深入的了解了。
两者从原理上看有点相似,都是解决异构环境的数据交换问题,都支持oracle,mysql,hdfs,hive的互相交换,对于不同数据库的支持都是插件式的,对于新增的数据源类型,只要新开发一个插件就好了,
但是只细看两者的架构图,很快就会发现明显的不同
DataX架构图
 
 
Job: 一道数据同步作业Splitter: 作业切分模块,将一个大任务与分解成多个可以并发的小任务. Sub-job: 数据同步作业切分后的小任务 Reader(Loader): 数据读入模块,负责运行切分后的小任务,将数据从源头装载入DataX Storage: Reader和Writer通过Storage交换数据 Writer(Dumper): 数据写出模块,负责将数据从DataX导入至目的数据地
Sqoop架构图

 
 
DataX 直接在运行DataX的机器上进行数据的抽取及加载。
而Sqoop充分里面了map-reduce的计算框架。Sqoop根据输入条件,生成一个map-reduce的作业,在Hadoop的框架中运行。

从理论上讲,用map-reduce框架同时在多个节点上进行import应该会比从单节点上运行多个并行导入效率高。而实际的测试中也是如此,测试一个Oracle to hdfs的作业,DataX上只能看到运行DataX上的机器的数据库连接,而Sqoop运行时,4台task-tracker全部产生一个数据库连接。调起的Sqoop作业的机器也会产生一个数据库连接,应为需要读取数据表的一些元数据信息,数据量等,做分区。

Sqoop现在作为Apache的顶级项目,如果要我从DataX和Sqoop中间选择的话,我想我还是会选择Sqoop。而且Sqoop还有很多第三方的插件。早上使用了Quest开发的OraOop插件,确实像quest说的一样,速度有着大幅的提升,Quest在数据库方面的经验,确实比旁人深厚。


Transfer highly clustered data more than five times faster than with Sqoop alone
Avoid scalability issues that can occur with Sqoop when data has no primary key or is not stored in primary key order
Reduce CPU by up to 80 percent and IO time by up to 95 percent
Prevent disruption to concurrently running Oracle workload
Get free use of Data Transporter for Hive, a Java command-line utility that allows you to execute a Hive query and insert the results into an Oracle table


在我的测试环境上,一台只有700m内存的,IO低下的oracle数据库,百兆的网络,使用Quest的Sqoop插件在4个并行度的情况下,导出到HDFS速度有5MB/s ,这已经让我很满意了。相比使用原生Sqoop的2.8MB/s快了将近一倍,sqoop又比DataX的760KB/s快了两倍。

另外一点Sqoop采用命令行的方式调用,比如容易与我们的现有的调度监控方案相结合,DataX采用xml 配置文件的方式,在开发运维上还是有点不方便。