说三道四技术文摘-感悟人生的经典句子
说三道四 > 文档快照

【问底】许鹏:使用Spark+Cassandra打造高性能数据分析平台(二)

HTML文档下载 WORD文档下载 PDF文档下载
Spark,强大的迭代计算框架,内存计算上无可匹敌。Cassandra,优异的列式存储NoSQL,在写入上难逢敌手。《问底》Spark+Cassandra高性能数据分析平台打造进入第二部分,本文主要探讨spark-cassandra-connector。

【导读】笔者( 许鹏)看Spark源码的时间不长,记笔记的初衷只是为了不至于日后遗忘。在源码阅读的过程中秉持着一种非常简单的思维模式,就是努力去寻找一条贯穿全局的主线索。在笔者看来,Spark中的线索就是如何让数据的处理在分布式计算环境下是高效,并且可靠的。

在对Spark内部实现有了一定了解之后,当然希望将其应用到实际的工程实践中,这时候会面临许多新的挑战,比如选取哪个作为数据仓库,是HBase、MongoDB还是Cassandra。即便一旦选定之后,在实践过程还会遇到许多意想不到的问题。

要想快速的解决开发及上线过程中遇到的系列问题,还需要具备相当深度的Linux知识,恰巧之前工作中使用Linux的经验在大数据领域中还可以充分使用。

笔者不才,就遇到的一些问题,整理出来与诸君共同分享。下文为本系列文章的第二部分(点击访问本系列文章开篇):

Cassandra高并发数据读取实现剖析

本文就spark-cassandra-connector的一些实现细节进行探讨,主要集中于如何快速将大量的数据从Cassandra中读取到本地内存或磁盘。

数据分区

存储在Cassandra中的数据一般都会比较多,记录数在千万级别或上亿级别是常见的事。如何将这些表中的内容快速加载到本地内存就是一个非常现实的问题。

解决这一挑战的思路从大的方面来说是比较简单的,那就是将整张表中的内容分成不同的区域,然后分区加载,不同的分区可以在不同的线程或进程中加载,利用并行化来减少整体加载时间。

顺着这一思路出发,要问的问题就是Cassandra中的数据如何才能分成不同的区域。

不同于MySQL,在Cassandra中是不存在Sequence Id这样的类型的,也就是说无法简单的使用seqId来指定查询或加载的数据范围。

既然没有SequenceID,在Cassandra中是否就没有办法了呢?答案显然是否定的,如果只是仅仅支持串行读取,Cassandra早就会被扔进垃圾桶了。

数据分区在Cassandra中至少可以通过两种途径实现 ,一是通过token range,另一个是slice range。这里主要讲解利用token range来实现目的。

1. Token Range

Cassandra将要存储的记录存储在不同的区域中,判断某一记录具体存储在哪个区域的依据是partition key的Hash值。 

在Cassandra 1.2之前,组成Cassandra集群的所有节点(Node),都需要手动指定该节点的Hash值范围也就是Token Range。

手工计算Token Range显然是很繁琐,同时也不怎么容易维护,在Cassandra 1.2之后,引进了虚拟节点(vnode)的概念,主要目的是减少不必要的人工指定,同时也将token range的划分变得更为细粒度。比如原先手工指定token range,只能达到10000这样一个精度,而有了vnode之后,默认安装是每一个物理节点上有256个虚拟节点,这样子的话每一个range的范围就是10000/256,这样变的更为精细。

有关token range的信息存储在cassandra的system命名空间(keyspace)下的local和peers两张表中。其中local表示本节点的token range情况,而peers表示集群中其它节点的token range情况。这两张表中的tokens字段就存储有详细的信息。如果集群中只由一台机器组成,那么peers中的就会什么内容都没有。

简单实验,列出本节点的token range:

use system;desc table local;select tokens from local;
2. Thrift接口

Token Range告诉我们Cassandra的记录是分片存储的,也就意味着可以分片读取。现在的问题转换成为如何知道每一个Token Range的起止范围。

Cassandra支持的Thrift接口中describe_ring就是用来获取token range的具体起止范围的。我们常用的nodetool工具使用的就是thrift接口,nodetool 中有一个describering指令使用的就是describe_ring原语。

可以做一个简单的实验,利用nodetool来查看某个keyspace的token range具体情况。

nodetool -hcassandra_server_addr describering keyspacename

注意将cassandra_server和keyspacename换成实际的内容。

Spark-Cassandra-Connector

在第一节中讲解了Cassandra中Token Range信息的存储位置,以及可以使用哪些API来获取token range信息。

接下来就分析spark-cassandra-connector是如何以cassandra为数据源将数据加载进内存的。

以简单的查询语句为例,假设用户要从demo这个keyspace的tableX表中加载所有数据,用CQL来表述就是:

select * from demo.tableX
上述的查询使用spark-cassandra-connector来表述就是:

sc.cassandraTable(“demo”,”tableX”)

尽管上述语句没有触发Spark Job的提交,也就是说并不会将数据直正的从Cassandra的tableX表中加载进来,但spark-cassandra-connector还是需要进行一些数据库的操作。要解决的主要问题就是schema相关。

cassandraTable(“demo”,”tableX”)只是说要从tableX中加载数据,并没有告诉connector有哪些字段,每个字段的类型是什么。这些信息对后面使用诸如get[String](“fieldX”)来说却是非常关键的。

为了获取字段类型信息的元数据,需要读取system.schema_columns表,利用如下语句可以得到schema_columns表结构的详细信息:

desc table system.schema_columns
如果在conf/log4j.properties中将日志级别设置为DEBUG,然后再执行sc.cassandraTable语句就可以看到具体的CQL查询语句是什么。

1. CassandraRDDPartitioner

Spark-cassandra-connector添加了一种新的RDD实现,即CassandraRDD。我们知道对于一个Spark RDD来说,非常关键的就是确定getPartitions和compute函数。

getPartitions函数会调用CassandraRDDPartitioner来获取分区数目:

override def getPartitions: Array[Partition] = {    verify // let's fail fast    val tf = TokenFactory.forCassandraPartitioner(cassandraPartitionerClassName)    val partitions = new CassandraRDDPartitioner(connector, tableDef, splitSize)(tf).partitions(where)    logDebug(s"Created total ${partitions.size} partitions for $keyspaceName.$tableName.")    logTrace("Partitions: \n" + partitions.mkString("\n"))    partitions  }
CassandraRDDPartitioner中的partitions的处理逻辑大致如下:

  1. 首先确定token range,使用describe_ring
  2. 然后根据Cassandra中使用的Partitioner来确定某一个token range中可能的记录条数,这么做的原因就是为进一步控制加载的数据,提高并发度。否则并发度就永远是256了,比如有一个物理节点,其中有256个vnodes,也就是256个token分区。如果每个分区中大致的记录数是20000,而每次加载最大只允许1000的话,整个数据就可以分成256x2=512个分区。
  3. 对describeRing返回的token range进一步拆分的话,需要使用splitter,splitter的构建需要根据keyspace中使用了何种Partitioner来决定,Cassandra中默认的Partitioner是Murmur3Partitioner,Murmur3Hash算法可以让Hash值更为均匀的分布到不同节点。
  4. splitter中会利用到配置项spark.cassandra.input.split.size和spark.cassandra.page.row.size,分别表示一个线程最多读取多少记录,另一个表示每次读取多少行。

partitions的源码详见CasssandraRDDParitioner.scala

compute函数就利用确定的token的起止范围来加载内容,这里在理解的时候需要引起注意的就是flatMap是惰性执行的,也就是说只有在真正需要值的时候才会被执行,延迟触发。

数据真正的加载是发生在fetchTokenRange函数,这时使用到的就是Cassandra Java Driver了,平淡无奇。

2. fetchTokenRange

fetcchTokenRange函数使用Cassandra Java Driver提供的API接口来读取数据,利用Java API读取数据一般遵循以下步骤:

val cluster = ClusterBuilder.addContactPoint(“xx.xx.xx.xx”).buildval session = cluster.connectval stmt = new SimpleStatement(queryCQL)session.execute(session)session.closecluster.close

addContactPoint的参数是cassandra server的ip地址,在后面真正执行cql语句的时候,如果集群有多个节点构成,那么不同的cql就会在不同的节点上执行,自动实现了负载均衡。可以在addContactPoint的参数中设定多个节点的地址,这样可以防止某一节点挂掉,无法获取集群信息的情况发生。

session是线程安全的,在不同的线程使用同一个session是没有问题的,建议针对一个keySpace只使用一个session。

3. RDD中使用Session

在Spark RDD中是无法使用SparkContext的,否则会形成RDD嵌套的现象,因为利用SparkContext很容易构造出RDD,如果在RDD的函数中如map中调用SparkContext创建一个新的RDD,则形成深度嵌套进而导致Spark Job有嵌套。

但在实际的情况下,我们需要根据RDD中的值再去对数据库进行操作,那么有什么办法来打开数据库连接呢?

解决的办法就是直接使用Cassandra Java Driver而不再使用spark-cassandra-connector的高级封装,因为不能像这样子来使用cassandraRDD。

sc.cassandraRDD(“ks”,”tableX”).map(x=>sc.cassandraRDD(“ks”,”tableX”).where(filter))
如果是直接使用Cassandra Java Driver,为了避免每个RDD中的iterator都需要打开一个session,那么可以使用foreachPartition函数来进行操作,减少打开的session数。

val  rdd1 = sc.cassandraTable(“keyspace”,”tableX”)	rdd1.foreachPartition( lst => {		val cluster = ClusterBuilder.addContactPoint(“xx.xx.xx.xx”).build		val session = cluster.connect		while ( iter.hasNext ) {		 	val  elem = iter.next			//do something by using session and elem		}		session.close		cluster.close	})

其实最好的办法是在外面建立一个session,然后在不同的partition中使用同一个session,但这种方法不行的原因是在执行的时候会需要”Task not Serializable”的错误,于是只有在foreachPartition函数内部新建session。

数据备份

尽管Cassandra号称可以做到宕机时间为零,但为了谨慎起见,还是需要对数据进行备份。

Cassandra提供了几种备份的方法

  1. 将数据导出成为json格式
  2. 利用copy将数据导出为csv格式
  3. 直接复制sstable文件

导出成为json或csv格式,当表中的记录非常多的时候,这显然不是一个好的选择。于是就只剩下备份sstable文件了。

问题是将sstable存储到哪里呢?放到HDFS当然没有问题,那有没有可能对放到HDFS上的sstable直接进行读取呢,在没有经过任务修改的情况下,这是不行的。

试想一下,sstable的文件会被拆分为多个块而存储到HDFS中,这样会破坏记录的完整性,HDFS在存储的时候并不知道某一block中包含有完成的记录信息。

为了做到记录信息不会被拆分到多个block中,需要根据sstable的格式自行提取信息,并将其存储到HDFS上。这样存储之后的文件就可以被并行访问。

Cassandra中提供了工具sstablesplit来将大的sstable分割成为小的文件。

DataStax的DSE企业版中提供了和Hadoop及Spark的紧密结合,其一个很大的基础就是先将sstable的内容存储到CFS中,大体的思路与刚才提及的应该差不多。

对sstable存储结构的分析是一个研究的热门,可以参考如下的链接。

  • https://www.fullcontact.com/blog/cassandra-sstables-offline/

之所以要研究备份策略是想将对数据的分析部分与业务部分相分离开,避免由于后台的数据分析导致Cassandra集群响应变得缓慢而致前台业务不可用,即将OLTP和OLAP的数据源分离开。

通过近乎实时的数据备份,后台OLAP就可以使用Spark来对数据进行分析和处理。

高级查询 Cassandra+Solr

与传统的RDBMS相比,Cassandra所能提供的查询功能实在是弱的可以,如果想到实现非常复杂的查询功能的,需要将Cassandra和Solr进行结合。

DSE企业版提供了该功能,如果想手工搭建的话,可以参考下面的链接:

  1. http://www.slideshare.net/planetcassandra/an-introduction-to-distributed-search-with-cassandra-and-solr 
  2. https://github.com/Stratio/stratio-cassandra开源方面的尝试 Cassandra和Lucene的结合

共享SparkContext

SparkContext可以被多个线程使用,这意味着同个Spark Application中的Job可以同时提交到Spark Cluster中,减少了整体的等待时间。

在同一个线程中, Spark只能逐个提交Job,当Job在执行的时候,Driver Application中的提交线程是处于等待状态的。如果Job A没有执行完,Job B就无法提交到集群,就更不要提分配资源真正执行了。

那么如何来减少等待时间呢,比如在读取Cassandra数据的过程中,需要从两个不同的表中读取数据,一种办法就是先读取完成表A与读取表B,总的耗时是两者之和。

如果利用共享SparkContext的技术,在不同的线程中去读取,则耗时只是两者之间的最大值。

在Scala中有多种不同的方式来实现多线程,现仅以Future为例来说明问题:

val ll  = (1 to 3 toList).map(x=>sc.makeRDD(1 to 100000 toList, 3))val futures = ll.map ( x => Future {		x.count()	})val fl = Future.sequencce(futures)Await.result(fl,3600 seconds)

  1. 简要说明一下代码逻辑
  2. 创建三个不同的RDD
  3. 在不同的线程(Future)中通过count函数来提交Job
  4. 使用Await来等待Future执行结束

更多《问底》内容

  • 【问底】严澜:数据挖掘入门(一)——分词
  • 【问底】Yao Yu谈Twitter的百TB级Redis缓存实践
  • 【问底】王帅:深入PHP内核(一)——弱类型变量原理探究 
  • 【问底】王帅:深入PHP内核(二)——SAPI探究
  • 【问底】王帅:深入PHP内核(三)——内核利器哈希表与哈希碰撞攻击
  • 【问底】静行:FastJSON实现详解
  • 【问底】李平:大型网站的灵魂——性能
  • 【问底】许鹏:使用Spark+Cassandra打造高性能数据分析平台(一)
  • 【问底】许鹏:使用Spark+Cassandra打造高性能数据分析平台(二)
  • 【问底】徐汉彬:大规模网站架构的缓存机制和几何分形学
  • 【问底】徐汉彬:亿级Web系统搭建——单机到分布式集群
《问底》是CSDN云计算频道新建栏目,以实践为本,分享个人对于新时代软件架构与研发的深刻见解。在含有“【问底】”字样标题的文章中,你会看到某个国外IT巨头的架构分享,会看到国内资深工程师对某个技术的实践总结,更会看到一系列关于某个新技术的探索。《问底》邀请对技术具有独特/深刻见解的你一起打造一片只属于技术的天空,详情可邮件至zhonghao@csdn.net。

CSDN诚邀您参加中国大数据有奖大调查活动,只需回答23个问题就有机会获得最高价值2700元的大奖(共10个), 速度参与进来吧!

第八届中国大数据技术大会(Big Data Technology Conference 2014,BDTC 2014)将于2014年12月12日-14日在北京新云南皇冠假日酒店召开。传承自2008年,历经七届沉淀,“中国大数据技术大会”是目前国内最具影响、规模最大的大数据领域技术盛会。本届会议,你不仅可以了解到Apache Hadoop提交者Uma Maheswara Rao G(兼项目管理委员会成员)、Yi Liu,以及Apache Hadoop和Tez项目管理委员会成员Bikas Saha等分享的通用大数据开源项目的最新成果和发展趋势,还将斩获来自腾讯、阿里、Cloudera、LinkedIn、网易等机构的数十场干货分享。 门票限时折扣中, 预购从速。


免费订阅“CSDN大数据”微信公众号,实时了解最新的大数据进展!

CSDN大数据,专注大数据资讯、技术和经验的分享和讨论,提供Hadoop、Spark、Impala、Storm、HBase、MongoDB、Solr、机器学习、智能算法等相关大数据观点,大数据技术,大数据平台,大数据实践,大数据产业资讯等服务。

VB中将数据转换为数据库文件 VB中远程数据库的访问 Visual Basic 的数据库编程 Visual Basic数据库数据的选项录入及选项增减与编辑 把资料输往Excel来列印-VB资料 报表中的滚动问题-VB资料 编写Connect Strings-VB资料 表或查询是否存在-VB资料 不创建DSN直接在程序中使用ODBC数据源-VB资料 不用 DATA 控件操作数据库文件 -VB资料 不用 EOF 以加快记录循环-VB资料 处理Select语句中的单引号 -VB资料 断开所有的数据连接 -VB资料 访问VB外来数据库 非access数据库在vb中的编程及应用 关闭程序中所有创建的数据库连接。 -VB资料 关闭所有的数据连接 -VB资料 关于数据报表的打印设置-VB资料 VB建立、改变及重构Access数据库-VB资料 结构化查询语言(SQL)详解之一-VB资料 利用VB使ACCESS数据库在网络使用中保持同步 返回 连接Data到多个表单-VB资料 列出 SQL Server 数据库中所有的存储过程-VB资料 列出MDB档当中所有table的名称-VB资料 您想知道有谁正在使用您的 Access 文件吗?(多人环境中)-VB资料 主题:浅谈Excel 的VB编程 巧用VB6的DataGrid实现通用电子表格 VB取得正确的 RecordCount 值 主題:如何动态新增、移除 ODBC DSN? -VB资料 VB如何将表中的数据导出到电子表格中 VB如何将文本文件转换为ACCESS数据库 如何将DataGrid1中的数据打印出来, 如何使程序自动修改CEdit中的文字? 请问在.NET类库中有这样的类吗? 如何知道某个表格被锁 求助capboy等网络视频播放高手!!! 菜鸟问题:在VC中用ALTER TABLE指令需要什么头文件? 请高手帮忙,感觉难度很高 大家做GUI时有什么好用又免费的日期控件呢? 在javabean中用sql server2000的jdbc驱动连不起数据库! 添加图片到数据库中,奇怪现象! 如果我已经得到了一个DOM树, 我第一次运行HTML HELP时,new怎么会是无效的? 我的 程序在调用FORM.ACTION的用法不正确。能告诉我怎样用它 怎幺样实现一对主从表的数据库基本操作?用ADOQuery连接SQL2000. 有关统计打印的问题 一个关于用VB调用C++builder编写的DLL文件,DLL的API函数中存在函数指针。请各位高手给点意见! 大专生如何考研?(up有分) ★★ 为高兴而送分 ★★ 关于打印的问题,请各位帮忙 请问应该用何种信用卡 利用INTERNET远程连接问题 移动办公相关问题?如在两台机器里编写同一段Web程序。 怎样将oracle sql中的in语句改为exists语句? 求教win 2000双网卡上宽带的问题,请高手帮忙! win2000,给文件改日期的命令是什莫??? 为什么我的背景图片显示不出来? 请问如何在DAO的SELECT语句中使用COUNT等运算符? 请教:请问在程序中怎么在Check box 前边的小框上打上对号 老板让我一个月内学会vb,大家说可能吗? 我装了project 2000后,原来的office 2000只能用50次了,怎么回事? 请能提供DES和RSA算法的源程序呀,vb或VC的都行,最好是VB 请问如何让一个报表的内容同一个DBGrid一致,可以自由设置字段的是否显示? 我用vb新建了一个文件请问如何能双击就能打开我的程序,就跟word一样例:我新建一个kk.doc 只要双击kk.doc 就能打开word 使用Enumwindows的问题 我的jsp页面能够运行但是WEBLOGIC显示了<2002-9-2 上午10时41分13秒> <Error> <HTTP> <Connection failure 大家是怎么看下载来的MFC的程序原代码的? 在JAVASCRIPT中,我怎样用语句控制一组选钮中那一个被选中? 如何对 redhat-config-network 进行配制? 各位师兄:哪里可以下载讲Delphi6多层结构的教材?很急!!!! 小问题 看完了钱能的《C++程序设计教程》继续深入下去该找什么书来看看呢? 关闭窗口 除了onunload 是否还有其他事件? 为什么我的电脑每个目录下都有一个folder文件? 请教一下:delphi6中nmftp的用法 急!!!水晶报表问题 用代码把数据窗口指定到某一打印机打印? 如何配置jConnect连接Sybase数据库?需要安装jdk吗? 奇怪的出错:java.lang.NoClassDefFoundError: com/sun/java/util/collections/HashMap 我装了project 2000后,原来的office 2000只能用50次了,怎么回事? 各位有没考虑过买房一事,看看北京的房价再看看我的收入,怎么办??? EDITBOX和URl字符串 一个圆锥形沙漏,它的底面周长12.56米高1.8米,用这样沙子在8米宽的公路上铺3厘米后的路面,能铺多少米? 90度直径595毫米,焊接弯头展开图 蒸馏设备的原理?里面要加多少水?要蒸到什么时候?多长时间么时候?酿酒 液体其中都有水吗?是不是所有的液体中.都含有水? 平均年龄69.9±19.如何计算?公式是怎样的? 求助一下大家奥数题大全就告诉我吧本人先在此谢谢各位4Z 水是( )( )( )的液体. 9 13 9 19 19 25 15 21什么意思 六年级数学题大全 水是一种怎么样的液体 2.1.15,26.9:23.15,1.9,14.19,15!请速度点帮半忙给我翻译下. 最好多一点 我想复习 想知道工艺品一般用什么材料制作才不会腐蚀? 毛泽东写的《菩萨蛮·大柏地》中的“彩练”、“苍”、“急”和“装点”分别是什么意思? 麻烦大家看下奥数题大全n有点急, 离心风机型号为9-19-7.3各数字代表什么,急盼回复 普萨蛮,大柏地里的彩练是什么意思? 送沙漏代表什么!我是男生,有一个男性朋友要过生日,我想送他一个沙漏.但不知道该送什么颜色的? 怎么计算阴极保护的保护距离 彩练的意思是什么 绿色沙漏代表什么意思? 用什么化学试剂可以快速腐蚀掉金属?越快越好 我要腐蚀断一根不锈钢管我什么都能搞到 说出来我去试验~ 什么样的制氧机适合高原地区使用 世上最大的 峡谷是哪个峡谷 有什么化学品能快速腐蚀木材 在高原使用哪种制氧机好 下列哪个峡谷是世界上海拔最高、最深、最长的峡谷? 通过气体交换,空气中的氧气经过()的系统和()系统,最终到达人体的每个()中,氧化分解有机物,获得人体各项生命活动的() 怎样除去硫酸铜里还有少量的硫酸钠 世界上最大的峡谷是哪里? 黄土高原的形成是由于风力侵蚀还是流水侵蚀,请说明理由 硫酸铜溶液与硫酸钠溶液为什么颜色不同 世界上最大的峡谷是什么峡谷? 大苏打的化学名称怎么读? 如何除去硫酸亚铁中的硫酸钠并提纯硫酸铜原溶液中有硫酸亚铁,硫酸钠和硫酸铜,如何除去硫酸亚铁中的硫酸钠并提纯硫酸铜中的铜离子 世界上最长的峡谷 请问小苏打和大苏打的化学名和俗称 我国最大的三角洲是呃…… 日本和英国都是岛屿国家,日本多火山地震,而英国火山地震很少,为什么? 两个二极管反向串联接地? 结晶度的高低对聚合物性能有哪些影响 请帮忙说说几种换热器形式 4007二极管串联有什么作用 什么是聚合物的结晶 请问有谁知道谁能做一道初中找规律的数学题?要用呀,打心底谢谢给位朋友了1d 四个二极管串联起什么作用 有同学将记录表中4次测量的电阻的平均值作为小灯泡的电阻,你认为这样做正确吗?为什么? c语言中 =n 怎么理解 小苏打和大苏打的化学成分 结晶度的大小对聚合物的性能有哪些影响? 润滑油对金属漆有腐蚀性吗请问汽车润滑油对金属表面喷漆或者刷的漆有腐蚀性吗 用什么区分硫酸钠和稀盐酸 为什么通过蒸馏就可以除去自来水中的氯离子等杂质?原理是什么,只有这种方法吗 机油有腐蚀性吗我经常用缝纫机油擦手电筒,这对手电有腐蚀吗. 稀盐酸溶液和稀硫酸溶液用硫酸钠溶液鉴别对不对,为什么 日本地震对相机影响会持续多久?什么时候能恢复正常?想买相机,结果就地震了.这次地震对数码影响究竟多大很多厂子都关了,松下停产.数码业全部涨价,这叫没影响? 请问航空油润滑油的话腐蚀性有一定的腐蚀性,那么容器采用何种材料或者热处理工艺来满足腐蚀性要求呢 液态的水叫什么? 请问弯头体积怎么算?1727*18mm 90°长半径弯头 对焊 ,请算出体积! 求内行来指点:有关地震导致数码相机涨价~想买个相机,春游用,可是目前单反都在涨,Canon600D,60D;NikonD90,D7000;……等等,商家都说有货,但是国美,大中这种地方,一分钱都不能降,标多少就是多
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn 说三道四技术文摘