使用Spark进行SQL与NoSQL编程
Spark SQL简介
结构化查询语言(SQL)是最常用的定义和表达数据问题的语言.许多数据分析师拥有把复杂问题解构为一系列SQL数据操作语言(Data Manipulation Language,DML),也就是SELECT语句的能力.
Hive简介
大数据处理平台上很多SQL抽象都基于Hive,Spark就是其中之一.对于Spark SQL这样的项目来说,Hive和Hive元数据库是不可或缺的组件
Apache Hive项目是2010年由Facebook发起,在Hadoop的MapReduce之上提供高层的类SQL抽象.Hive引入了一种新的语言,称为Hive查询语言.
- Hive对象和Hive元数据库
Hive实现了HDFS上对象的表格抽象,在编程模型中把目录和其中所有的文件当作数据表对待.和传统的关系型数据库一样,数据表中的各列都预先定义,并指定了数据类型.HDFS上的数据可以像传统的数据库管理系统一样通过SQL的DML语句访问.然而,Hive与传统数据库系统的相似之处也就仅止于此,毕竟Hive平台是读时系统,且下层存储HDFS是不可变的文件系统.因为Hive只是对HDFS上的原始文件实现了SQL表格抽象,所以和传统的关系型数据库平台有下列几点关键区别:
- 并不真正支持UPDATE操作.尽管HiveQL中有UPDATE语句,但HDFS是不可变的文件系统,因此UPDATE会是一个粗粒度操作,而在传统关系型数据库中真正的UPDATE操作是只会修改一条记录的细粒度操作.
- 没有事务、日志、回滚和真正的事务隔离级别。
- 没有声明引用完整性(DRI),也就是没有主键和外键的概念.
- 格式错误的数据,比如输入错误或数据错误,只是作为空值传给客户端
数据表与HDFS目录位置的对应关系,以及表中所包含的列和列的定义,都由Hive元数据库维护.元数据库是供Hive客户端读写的数据库.对象定义中还包含文件的输入、输出格式,由表对象和序列化-反序列化方式表示,它们告诉Hive如何从文件中获取记录和其中的字段.
元数据库可以是内嵌的Derby数据库(默认情况),也可以是本地或远程的数据库,比如MySQL或者Postgres.大多数情况下,用户会选择共享的数据库,这可以让开发人员和分析师共享对象定义.
访问Hive
Hive提供了命令行界面(CLI)客户端,可以接收并解析所输入的HiveQL命令.这是执行即席查询的常见方式.
当Hive客户端或驱动器应用以及元数据库连接都部署于本地机器时,会使用Hive CLI.对于大规模部署来说,客户端/服务器的方式更合适,因为这样只要有一台服务器端的机器维护元数据库的连接细节,元数据库的访问权限也可以限定到集群内.这种方式要使用Hive的服务器组件HiveServer2.Hive数据类型与数据定义语言(DDL)
和大多数数据库系统类似,Hive支持大多数常见的原生数据类型,以及几种复杂数据类型.
TINYINT:原生类型,占1字节的有符号整数
SMALLINT:原生类型,占2字节的有符号整数
INT:原生类型,占4字节的有符号整数
BIGINT:原生类型,占8字节的有符号整数
FLOAT:原生类型,占4字节的单精度浮点数
DOUBLE:原生类型,占8字节的双精度浮点数
BOOLEAN:原生类型,真/假
STRING:原生类型,字符串
BINARY:原生类型,字节数组
TIMESTAMP:原生类型,纳秒精度的时间戳
DATE:原生类型,年/月/日,格式为YYYYMMDD
ARRAY:复杂类型,由同类型字段组成的有序集合
MAP:复杂类型,由键值对组成的无序集合
STRUCT:复杂类型,由不同类型的具名字段组成的集合
比较Hive中的内部表与外部表
在Hive中建表时,默认创建的是Hive内部表.Hive管理内部表的目录,对内部表运行DROP TABLE语句会从HDFS上删除对应文件.推荐在CREATE TABLE语句中指定EXTERNAL 关键字创建外部表.这种建表语句需要提供表结构和HDFS上对应对象的路径.此时,DROP TABLE操作不会删除相应目录和文件.
Spark SQL架构
Spark SQL针对其基于RDD的存储、调度、执行模型,提供了基本兼容HiveQL的SQL抽象.Spark SQL包含Spark项目核心的很多关键特性,包括惰性执行和中间查询容错.另外,Spark SQL可以在一个应用中与Spark核心API一起使用.
Spark SQL为了优化关系型数据的典型访问模式,对核心API添加了一些关键扩展.具体包括如下所列:
- DAG部分执行(PDE):PDE让我们可以在执行时根据处理过程中发现的一些数据,动态修改和优化DAG.DAG修改包括优化表连接操作、处理数据倾斜、调整Spark使用的并行度.
- 分区统计信息:Spark SQL维护各数据分区的统计信息,这些统计信息可以在PDE中使用,也可以用于裁剪映射任务(根据分区内各列的统计信息,过滤或裁剪要加载的分区)和优化通常代价较大的连接操作.
- DataFrame API
- 列式存储:Spark SQL在内存中存储数据时使用列式存储,也就是把数据按列存储,而不是按行存储.这对不同SQL访问模式有显著的性能影响.
SparkSession入口
使用Spark核心API的应用以SparkContext对象作为程序主入口,而Spark SQL应用则以SparkSession对象作为程序主入口.
SparkSession与SQLContext
你可能还是会在各种示例程序中发现对SQLContext对象的引用,它的实例通常名为sqlContext.该对象可以在交互式Spark shell中使用,并且可以与spark对象互换使用.一般来说,使用SparkSession的对象实例更好,因为SQLContext可能会在将来的版本中被废弃.
DataFrame 入门
Spark SQL的DataFrame表示记录的分布式集合,有着同样的预定义结构,在概念上类似于关系型数据库中的共享表.Spark SQL中的DataFrame前身为SchemaRDD对象,采纳了R语言中数据框结构和Pandas(Python中用于数据操作与分析的库)的一些设计思想.
DataFrame是Spark RDD的抽象.然而,DataFrame有别于原生RDD,区别于DataFrame维护表结构,并且对许多常见的SQL函数和关系型操作符提供了原生支持.而DataFrame和RDD的相似之处包括它们都是作为DAG求值,都是用惰性求值,并且都提供了谱系和容错性.
Dataframe可以由很多不同方式创建,包括如下所列几种:
- 已有的RDD
- JSON文件
- 文本文件、Parquet文件,或ORC文件
- Hive表
- 外部数据库中的表
- Spark临时表
接下来会介绍使用已有的SparkSession对象构建DataFrame的一些常见方法
- 从已有RDD创建DataFrame
createDataFrame()1
SparkSession.createDataFrame(data,schema=None,samplingRatio=None)
createDataFrame()方法从已有RDD创建DataFrame对象.参数data接收由元组或列表元素组成的RDD对象.参数schema表示要对DataFrame对象投影的表结构.如果需要推断表结构,参数samplingRatio用于设置数据采样率。1
2myrdd=sc.parallelize([('Jeff',48),('Kellie',45)])
spark.createDataFrame(myrdd).collect()
- 从Hive表创建DataFrame
要从Hive表加载数据到Spark SQL的DataFrame中,需要先创建HiveContext实例.
HiveContext会读取Hive客户端配置来获取Hive元数据库的连接信息.这使得Spark应用可以无缝访问Hive表.从Hive表创建DataFrame的方法有好几种,包括使用sql()方法或table()方法
(1)sql()1
SparkSession.sql(sqlQuery)
sql()方法根据提供的sqlQuery参数,从Hive表或对Hive表执行的DML操作创建DataFrame对象.如果要操作的表在Hive当前所使用的数据库之外,则需要使用
1 | sql_cmd="""SELECT name,lat,long FROM stations WHERE landmark='San Jose'""" |
(2)table()1
SparkSession.table(tableName)
table()方法从Hive表创建DataFrame对象.与sql方法的区别是,table()方法不允许对原始表的列进行裁剪,也不允许使用WHERE子句过滤部分行.整张表都会加载到DataFrame中1
2
3df=spark.table('stations')
df.columns
df.count()
- 从JSON对象创建DataFrame
JSON是在网络服务响应中经常使用的一种常见的、标准的、人类可读的序列化方式或数据传输格式.
因为JSON是一种包含结构信息的半结构化数据源,Spark SQL内建支持JSON格式.
read.json()1
2
3
4
5
6
7
8
9
10
11
12
13
14DataFrameReader.read.json(path
schema=None,
primitivesAsString=None,
prefersDecimal=None,
allowComments=None,
allowUnquotedFieldNames=None,
allowSingleQuotes=None,
allowNumericLeadingZero=None,
allowBackslashEscapingAnyCharacter=None,
mode=None,
columnNameOfCorruptRecord=None,
dateFormat=None,
timestampFormat=None,
multiLine=None)
DataFrameReader的json()方法从JSON文件创建DataFrame对象.
4.从普通文件创建DataFrame
DataFrameReader也可以用于从其他类型的文件读取DataFrame,比如CSV文件,或者外部的SQL和NoSQL数据源.下面分别介绍了从纯文本文件以及Parquet、ORC等一些列式存储格式的文件,创建DataFrame的示例
(1)text()1
DataFrameReader.read.text(path)
DataFrameReader的text()方法用于从外部文件系统(本地文件系统、NFS、HDFS、S3等)上的文本文件读取DataFrame.该方法的行为类似于RDD中等价的sc.textFile()方法.参数path指代的路径可以是一个文件,也可以是目录,或是文件通配符。1
2
3
4df=spark.read.text('file:///opt/spark/data/bike-share/stations/stations.csv')
df.take(1)
df=spark.read.text('file:///opt/spark/data/bike-share/stations/')
df.count()
注意文本文件中的每一行会返回一个对应的Row对象,该对象包含一个字符串,即文件中对应的整行数据.
(2)parquet()1
DataFrameReader.read.parquet(paths)
DataFrameReader的parquet()方法用于读取以Parquet列式存储格式存储的文件.这些文件通常来自其他进程的输出,比如前一个Spark进程的输出.参数paths指向单个或多个Parquet文件,或是Parquet文件组成的目录.
Parquet格式将表的结构信息与数据封装在一个结构中,因此DataFrame可以使用文件中的表结构.
1 | df=spark.read.parquet('hdfs:///user/hadoop/stations.parquet') |
(3)orc()1
DataFrameReader.read.orc(path)
DataFrameReader的orc()方法用于从单个ORC格式文件或由多个ORC格式文件组成的目录读取DataFrame.ORC是一种来自Hive项目的格式.参数path指向一个包含ORC文件的目录,一般与Hive仓库中以ORC格式存储的一张表相关联.1
2
3df=spark.read.orc('hdfs:///user/hadoop/stations_orc/')
df.printSchema()
df.take(1)
也可以使用DataFrameReader和spark.read.jdbc()方法从MySQL、Oracle之类的外部数据源读取数据.
把DataFrame转为RDD
可以使用rdd()方法轻松地把DataFrame转化原生的RDD1
2
3
4stationsdf=spark.read.parquet('hdfs:///user/hadoop/stations.parquet')
stationsrdd=stationsdf.rdd
stationsrdd
stationsrdd.take(1)DataFrame数据模型:原生类型
DataFrame API的数据模型是基于Hive的数据模型设置的 .DataFrame中使用的数据类型与Hive中的等价类型直接对应.这些类型包括所有常见的原生类型,以及等价于列表、字典和元组的复杂嵌套类型DataFrame数据模型:复杂类型
Spark SQL可以原生使用基于HiveQL的操作符访问复杂嵌套类型
ArrayType:等价于Hive类型ARRAY,等价于Python list,tuple或array
MapType:等价于Hive类型的MAP,等价于Python dict
StructType:等价于Hive类型的STRUCT,等价于Python的list或tuple
- 推断DataFrame表结构
在Spark SQL中,DataFrame的表结构可以显式定义,也可以隐式推断.
Spark SQL使用反射来推断DataFrame对象的表结构.反射会检查对象来判断其组成结构,可以在把RDD转为DataFrame时生成相应的表结构.在这种情况下,RDD的每条记录都会生成一个对应的Row对象,并且每个字段都会分配一个数据类型.各字段的数据类型是从第一条记录推断而来的,因此第一条记录必须要具有代表性,而且所有的字段都不能为空.1
2rdd=sc.textFile('file:///home/hadoop/stations.csv').map(lambda x:x.split(',')).map(lambda x:(int(x[0]),str(x[1]),float(x[2]),float(x[3]),int(x[4]),str(x[5]),str(x[6])))
rdd.take(1)
注意字段的命名使用了_
9.定义DataFrame表结构
在代码中显式定义DataFrame的表结构,比隐式推断更好.你需要创建一个包含一组StructField对象的StructType对象,以创建表结构.然后就可以在创建DataFrame时使用该表结构.
使用DataFrame
DataFrame API是目前Spark项目中发展最快的部分之一.每个小版本发布都会包含明显的新特性与函数.Spark SQL的DataFrame模型还有一些扩展,比如Datasets API.这些扩展也以同样的速度告诉发展.事实上,Spark SQL及其核心组件 DataFrame API就足以单独撑起一整本书.
- DataFrame元数据操作
DataFrame API提供了几个元数据函数.这些函数返回的是关于数据结构的信息,而不是数据本身.
(1)columns()1
DataFrame.columns()
columns()方法返回由给定DataFrame的列名组成的列表.
(2)dtypes()1
DataFrame.dtypes()
dtypes()方法返回由二元组组成的列表.其中每个二元组包含给定DataFrame对象的一个列名和该列的数据类型.
- DataFrame基本操作
因为DataFrame是RDD的列式抽象,所以它们有很多相似的函数,比如一些转化操作和行动操作都是直接来自RDD的方法.DataFrame还有一些额外的关系型方法,比如select()、drop()和where().像count()、collect()、take()和foreach()这样的核心函数,与RDD API中的同名函数,无论是功能上还是句法上都保持一致.与RDD API一样,有些方法中的行动操作会触发DataFrame及其谱系的求值
与行动操作collect()和take()一样,之前的例子中,我们使用了一个替代方法show().show()是行动操作,如果DataFrame在缓存中不存在,他会触发DataFrame的求值
(1)show()1
DataFrame.show(n=20,truncate=True)
show()方法将DataFrame的前n行打印到控制台上.与collect()或take(n)不同,show()并不把结果返回到变量.
(2)select()1
DataFrame.select(*cols)
select()方法根据cols参数指定的列返回一个新的DataFrame对象.你可以使用星号(* )选出DataFrame中所有的列而不进行任何操作
(3)drop()1
DataFrame.drop(col)
drop()方法返回一个删去了col参数指定的列的新DataFrame.
(4)filter()1
DataFrame.filter(condition)
filter()方法返回仅包含满足给定条件的行的新DataFrame,筛选条件由condition参数提供,求值结果为True或False.1
2df=spark.read.parquet('hdfs:///user/hadoop/stations.parquet')
df.filter(df.name=='St James Park').select(df.name,df.lat,df.long).show()
(5)distinct()1
DataFrame.distinct()
distinct()方法返回包含输入DataFrame中不重复的行的新的DataFrame,本质就是过滤掉重复的行.当同一个DataFrame中一行数据的所有列的值都与另一行相同,我们就把它看作重复的行.1
2
3rdd=sc.parallelize([('Jeff',48),('Kellie',45),('Jeff',48)])
df=spark.createDataFrame(rdd)
df.show()
注意drop_duplicates()方法具有类似功能,还可以让用户选择仅过滤选定列的重复数据.
另外,map()和flatMap()可以分别通过DataFrame.rdd.map()和DataFrame.rdd.flatMap()使用.
从概念上来说,这些方法和他们在RDD API中同名的方法功能类似.不过,在处理各列有名字的DataFrame时,lambda函数稍有不同.1
2
3
4df=spark.read.parquet('hdfs:///user/hadoop/stations.parquet')
rdd=df.rdd.map(lambda r:r.name)
rdd
rdd.take(1)
Spark SQL的DataFrame API中还有其他一些操作值得介绍.sample()方法he sampleBy()方法与RDD API中的等价操作效果类似,而limit()函数则会创建一个包含原始DataFrame中指定数量的任意行的新DataFrame。
explain()也是一个在开发时有帮助的函数.它返回查询计划,包含对DataFrame进行求值时使用的逻辑计划和物理计划.在排查问题或优化Spark SQL程序时,会用到这个函数.
探索文档可以对DataFrame API中提供的所有函数有更多了解.值得注意的是,Python语言的Spark SQL API的所有函数都包含docstring.你可以使用它们探索Spark SQL中任意函数的语法和用法,也可以探索Spark 的Python语言API提供的其他函数.1
2from pyspark.sql import DataFrame
print(DataFrame.sample.__doc__)
DataFrame内建函数
Spark SQL中有大量可供使用的函数,包含其他常见的数据库管理系统的SQL中所有支持的大多数函数。
字符串函数:startswith、substr、concat、lower、upper、regexp_extract、regexp_replace
数学函数:abs、ceil、floor、log、round、sqrt
统计函数:avg、max、min、mean、stddev
日期函数:date_add、datediff、from_utc_timestamp
哈希函数:md5、sha1、sha2
算法函数:soundex、levenshtein
窗口函数:over、rank、dense_rank、lead、lag、ntile在DataFrame API中实现用户自定义函数
如果现成的函数中找不到能满足需求的,你可以在Spark SQL中创建用户自定义函数(user-defined function,即UDF).你可以通过使用udf()方法,创建列级别的UDF,把所需操作整合到Spark程序中.1
pyspark.sql.function.udf(func,returnType=StringType)
udf()方法创建表示用户自定义函数的列表达式.func参数可以是具名函数或使用lambda语法的匿名函数,它对DataFrame中单行内的一列进行操作.returnType参数制定函数返回对象的数据类型.
- 多DataFrame操作
join()、union()等集合操作是DataFrame的常见需求,因为它们都是关系型SQL编程中不可或缺的操作.
DataFrame的连接操作支持RDD API和HiveQL所支持的所有连接操作,包括内连接、外连接以及左半连接.
(1)join()1
DataFrame.join(other,on=None,how=None)
(2)orderBy()1
DataFrame.orderBy(cols,ascending)
orderBy()方法根据cols参数指定的列对DataFrame进行排序,生成新的DataFrame。
ascending是布尔型的参数,默认为True.
(3)groupBy()1
DataFrame.groupBy(cols)
groupBy()方法将输入的DataFrame按照cols参数指定的列进行分组,使用分组结果创建新的DataFrame.
DataFrame缓存、持久化与重新分区
DataFrame API支持缓存、持久化与重新分区,所用方法类似于Spark RDD API中的对应操作.
缓存与持久化DataFrame的方法包括cache()、persist()和unpersist(),它们与用于RDD的同名函数作用类似.此外,Spark SQL还添加了cacheTable()方法,用于在内存中缓存Spark SQL或Hive中的表.clearCache()方法可以从内存中删除缓存的表。DataFrame也支持使用coalesce()和repartition()方法进行重新分区.
保存DataFrame输出
DataFrameWriter是用来把DataFrame写到文件系统或数据库等外部存储系统的接口.DataFrameWriter可以通过DataFrame.write()访问.
数据写入Hive表
saveAsTable()函数实现此功能1
DataFrame.write.saveAsTable(name,format=None,mode=None,patitionBy=None)
数据写入文件
DataFrame中的数据写入的文件可以位于所支持的任意文件系统:本地文件系统、网络文件系统或分布式文件系统.输出会作为目录写出,每个分区对应生成一个文件,这和前面的RDD输出示例颇为相似.
逗号分割值(CSV)是一种常见的文件导出格式.DataFrame可以通过使用DataFrameWriter.write.csv()方法导出到CSV文件
Parquet是一种流行的列式存储格式,并且转为Spark SQL进行了优化.通过使用DataFrameWriter.write.parquet()方法,你可以把DataFrame以Parquet格式写出去
(1) write.csv()1
2
3
4
5
6
7
8
9
10
11
12
13
14
15DataFrameWriter.write.csv(path,
mode=None,
compression=None,
sep=None,
quote=None,
escape=None,
header=None,
nullValue=None,
escapeQuotes=None,
quoteAll=None,
dataFormat=None,
timestampFormat=None,
ignoreLeadingWhiteSpace=None,
ignoreTrailingWhiteSpace=None
)
DataFrameWriter类的write.csv()方法可以通过DataFrame.write.csv()接口访问,把DataFrame的内容以CSV文件的形式写入path参数所指定的路径。mode参数定义了如果目标目录在操作时已经存在的行为,其有效值为append(追加写)、overwrite(覆盖)、ignore(忽略)和error(报错,为默认值).mode参数在所有的DataFrame.write()方法中都适用.
(2)parquet()1
DataFrameWriter.write.parquet(path,mode=None,partitionBy=None)
write.parquet()方法把DataFrame中的数据以Parquet格式写入指定目录.文件压缩的设置来自当前SparkContext中压缩相关的配置属性.mode参数指定当目录或文件不存在时的行为,其有效值为append(追加写),overwrite(覆盖),ignore(忽略)和error(报错,为默认值)。PartitionBy参数指定了作为输出文件的分区依据的列的名字(使用哈希分区)
保存DataFrame为Parquet文件1
2
3spark=SparkSession.builder.config("spark.sql.parquet.compression.codec.","snappy").getOrCreate()
stations=spark.table("stations")
stations.select([stations.station_id,stations.name]).write.parquet("file:///home/hadoop/stations.parquet",mode='overwrite')
ORC文件可以使用orc方法写出,该方法的用法与parquet()类似.JSON文件也可以使用json()方法写出.
在Spark中使用NoSQL系统
NoSQL是一种新的数据范式,允许用户以单元格的形式查看数据,而不是智能使用表格、行、列这种关系型范式.
NoSQL简介
- NoSQL系统特点
NoSQL系统有具体的定义特征
所有类型的NoSQL系统共有的属性如下所列:
- NoSQL系统中没有静态表结构,是运行时才有表结构的读时系统:这意味着NoSQL系统中没有预定义的列,列是在每次PUT或INSERT操作时创建的。每条记录、每个文档、每个数据都可以有不同于其他实例的结构.
- 数据与其他对象之间不存在预定义的关联:这意味着NoSQL系统中没有外键和参照完整性的概念.不论是在声明中还是其他地方.数据对象或实例之间可以存在关联,但是这些关联只能在运行时发现和使用,而不可以在设计表时定义.
- 一般避免表连接操作:在大多数NoSQL实现中,表连接功能很弱,甚至完全不支持.这一般需要通过对数据进行非正则化实现,通常要以存储重复数据为代价.不过,大多数NoSQL实现都使用了高性价比的硬件或云端基础设施,由于在访问数据时不需要执行多余的表连接操作,节省出来的计算开销可以弥补存储多花费的开销.
另外,NoSQL系统一般都是分布式的,并且支持快速查找.写操作比起传统的关系型数据库系统,一般也更快和更具伸缩性,因为免去了传统的关系型数据库系统中一些导致额外开销的过程,比如数据类型检查或域检查、原子/阻塞性事务,以及对事务隔离级别的管理.
- NoSQL系统的类别
NoSQL系统出现了一些变种.它们可以分为如下几类:键值对存储、文档存储、图数据存储。
Spark中使用HBase
HBase是Hadopp生态系统的项目,用于提供基于HDFS的分布式大规模可伸缩的键值对存储.
- HBase简介
HBase把数据存储为稀疏的多维有序映射表.映射表根据键(行键)进行索引,值则存储在单元格中,每个单元格由列键和列值组成.行键和列键是字符串,而列值是未解释的字节数组,可以表示任何基本数据类型或复杂数据类型.HBase是多维的,也就是说每个单元格都带有时间戳的版本号.
在设计表时会定义一个或多个列族.在列数据的物理存储中,数据按所属列族分组存储.不同的列族可以使用不同的物理存储属性,比如区块大小、压缩设置,还有要留存的单元格版本数量.
虽然有些项目(比如Hive和Phoenix)可以用类SQL的方式访问HBase上的数据,但访问和更新HBase数据的自然方法本质上可以归结为get(查)、put(增)、scan(扫描)和delete(删除)这几种.HBase包含了一个shell程序,以及支持多种语言的编程接口.
HBase支持稀疏数据.也就是说,并非所有列都要在表内的每一行存在,而且不存储空值.
虽然HBase的数据存储在HDFS这个不可变的文件系统上,HBase依然支持对HBase表中的单元格进行原地更新.当列键已经存在时,HBase会创建带有新时间戳的新版本的单元格,然后后台的合并进程会将多个文件合并为数量更少、占空间更大的文件.
HBase数据以HFile对象的形式存储在HDFS上.HFile对象是列族(存储分组)和行键顺序范围的交集.行键的范围被称为分区,在其他实现中也别称为分片.HBase把分区数据放到分区服务器上.分区可以提供快速的行键查询,因为给定行键属于哪个分区是HBase已知的.HBase按需拆分或合并分区,作为其常规操作的一部分.无关行键的查询速度较慢,比如查询满足某种标准的列键和值.不过,HBase会使用布隆过滤器来加快搜索速度.
- HBase与Spark
使用Python API 从Spark读写HBase最可靠的方法是使用Python软件包HappyBase.HappyBase是为访问和操作HBase集群上的数据而构建的Python库.要使用HappyBase,必须先使用pip或easy_install安装对应的Python软件包.