Hadoop生态系统-新手快速入门(含HDFS、HBase系统架构)

Hadoop生态系统-新手快速入门(含HDFS、HBase系统架构),第1张

Hadoop是一个由Apache基金会所开发的分布式系统基础架构。

用户可以在不了解分布式底层细节的情况下,开发分布式程序。充分利用集群的威力进行高速运算和存储。

Hadoop实现了一个分布式文件系统(Hadoop Distributed File System),简称HDFS。HDFS有高容错性的特点,并且设计用来部署在低廉的(low-cost)硬件上;而且它提供高吞吐量(high throughput)来访问应用程序的数据,适合那些有着超大数据集(large data set)的应用程序。

Hadoop的框架最核心的设计就是:HDFS和MapReduce。HDFS为海量的数据提供了存储,而MapReduce则为海量的数据提供了计算。

广义的Hadoop,一般称为Hadoop生态系统,如下所示。

Hadoop生态系统中这些软件的作用:

HDFS 采用了主从(Master/Slave)结构模型,一个HDFS集群包括一个名称节点(NameNode)和若干个数据节点(DataNode)。

HDFS采用Java语言开发,因此任何支持JVM的机器都可以部署名称节点和数据节点。

在配置好Hadoop 集群之后,可以通过浏览器访问 >

Apache Hive 和 Apache HBase 都是大数据中不可思议的工具。虽然它们的功能存在一些重叠,但 Apache Hive 和 Apache HBase 都具有独特的品质,使它们更适合特定任务。一些主要区别包括:

虽然这两个工具都是Hadoop的衍生产品,但它们不为用户提供相同的功能。然而,尽管存在差异,Apache Hive 和 Apache HBase 都是处理大数据时优先考虑的两块工具和解决方案。

每个工具都有自己的优缺点。因此,Hive 和 HBase各自都存在一些限制。

首先,虽然Hive也具有非常基本的 ACID 功能,但它们没有像 MYSQL 那样成熟完备的产品架构,速度无法满足日常OLTP型业务。

Hive 查询通常也具有高延迟。由于它在 Hadoop 上运行批处理,因此获取查询结果可能需要几分钟甚至1小时。此外,更新数据可能既复杂又耗时。

Hive 不是擅长用于查询数据集(尤其是大数据集中)当中的部分数据,大多数用户倾向于依赖传统的 RDBMS (关系型数据)来处理这些数据集。

HBase 查询采用自定义语言,需要经过培训才能学习。HBase 并不完全符合 ACID,尽管它确实支持某些属性。

HBase 可以通过协同处理来处理小数据,但它仍然不如 RDBMS(关系型数据库) 有用。

1、Hive 应该用于对一段时间内收集的数据进行分析查询——例如,计算趋势或网站日志。

2、HDFS 的 SQL 查询引擎 - 您可以利用 Hive的HQL来查询处理 Hadoop 数据集,然后将它们连接到相应的BI工具,进行相关报表展示。

1、HBase 非常适合实时查询大数据(例如 Facebook 曾经将其用于消息传递)。Hive 不能用于实时查询,因为速度很慢。

2、HBase 主要用于将非结构化 Hadoop 数据作为一个湖来存储和处理。您也可以将 HBase 用作所有 Hadoop 数据的仓库。

3、大量数据需要长期保存, 且数量会持续增长,而且瞬间写入量很大。

不支持,Hbase不支持普通的SQL语句,它可以使用HBase查询语言(HBase Query Language, HBaseQL)进行查询,HBaseQL可以用来与HBase交互,它是一种基于NoSQL的查询语言,它允许用户与HBase表进行交互,以实现常见的数据库 *** 作,如插入,更新和删除。HBaseQL并不是标准的SQL,而是基于HBase的NoSQL查询语言,它更像是一个替代 SQL的查询语言,但它的语法和概念类似于SQL,容易理解和使用。因此,HBase不支持使用普通的SQL语句来查询。

现在遇到这样的情况:

1、关系型数据库里面(oracle)有全国用户的数据1千多亿; 而且存在不同的数据库里面,每个数据库里面表有50个左右。数据存放入Hbase里面,表怎么设计比较好。

2、怎么实现hbase表里面的关联查询。(用mapreduce速度比较慢还有其他方式没 )

可以一起查。

1scan原理

HBase的查询实现只提供两种方式:

1、按指定RowKey 获取唯一一条记录,get方法(orgapachehadoophbaseclientGet)

Get 的方法处理分两种 : 设置了ClosestRowBefore 和没有设置的rowlock 主要是用来保证行的事务性,即每个get 是以一个row 来标记的一个row中可以有很多family 和column

2、按指定的条件获取一批记录,scan方法(orgapacheHadoophbaseclientScan)实现条件查询功能使用的就是scan 方式

1)scan 可以通过setCaching 与setBatch 方法提高速度(以空间换时间);

2)scan 可以通过setStartRow 与setEndRow 来限定范围([start,end)start 是闭区间,

end 是开区间)。范围越小,性能越高。

3)、scan 可以通过setFilter 方法添加过滤器,这也是分页、多条件查询的基础。

HBase中scan并不像大家想象的一样直接发送一个命令过去,服务器就将满足扫描条件的所有数据一次性返回给客户端。而实际上它的工作原理如下图所示:

上图右侧是HBase scan的客户端代码,其中for循环中每次遍历ResultScanner对象获取一行记录,实际上在客户端层面都会调用一次next请求。next请求整个流程可以分为如下几个步骤:

next请求首先会检查客户端缓存中是否存在还没有读取的数据行,如果有就直接返回,否则需要将next请求给HBase服务器端(RegionServer)。

如果客户端缓存已经没有扫描结果,就会将next请求发送给HBase服务器端。默认情况下,一次next请求仅可以请求100行数据(或者返回结果集总大小不超过2M)

服务器端接收到next请求之后就开始从BlockCache、HFile以及memcache中一行一行进行扫描,扫描的行数达到100行之后就返回给客户端,客户端将这100条数据缓存到内存并返回一条给上层业务。

HBase 每次 scan 的数据量可能会比较大,客户端不会一次性全部把数据从服务端拉回来。而是通过多次 rpc 分批次的拉取。类似于 TCP 协议里面一段一段的传输,可以做到细粒度的流量控制。至于如何调优,控制每次 rpc 拉取的数据量,就可以通过三个参数来控制。

setCaching => setNumberOfRowsFetchSize (客户端每次 rpc fetch 的行数)

setBatch => setColumnsChunkSize (客户端每次获取的列数)

setMaxResultSize => setMaxResultByteSize (客户端缓存的最大字节数)

hbaseclientscannercaching - (setCaching):HBase-098 默认值为为 100,HBase-12 默认值为 2147483647,即 IntegerMAX_VALUE。Scannext() 的一次 RPC 请求 fetch 的记录条数。配置建议:这个参数与下面的setMaxResultSize配合使用,在网络状况良好的情况下,自定义设置不宜太小, 可以直接采用默认值,不配置。

setBatch() 配置获取的列数,假如表有两个列簇 cf,info,每个列簇5个列。这样每行可能有10列了,setBatch() 可以控制每次获取的最大列数,进一步从列级别控制流量。配置建议:当列数很多,数据量大时考虑配置此参数,例如100列每次只获取50列。一般情况可以默认值(-1 不受限)。

hbaseclientscannermaxresultsize - (setMaxResultSize):HBase-098 无该项配置,HBase-12 默认值为 210241024,即 2M。Scannext() 的一次 RPC 请求 fetch 的数据量大小,目前 HBase-12 在 Caching 为默认值(Integer Max)的时候,实际使用这个参数控制 RPC 次数和流量。配置建议:如果网络状况较好(万兆网卡),scan 的数据量非常大,可以将这个值配置高一点。如果配置过高:则可能 loadCache 速度比较慢,导致 scan timeout 异常

hbaseserverscannermaxresultsize:服务端配置。HBase-098 无该项配置,HBase-12 新增,默认值为 10010241024,即 100M。该参数表示当 Scannext() 发起 RPC 后,服务端返回给客户端的最大字节数,防止 Server OOM。

要计算一次扫描 *** 作的RPC请求的次数,用户需要先计算出行数和每行列数的乘积。然后用这个值除以批量大小和每行列数中较小的那个值。最后再用除得的结果除以扫描器缓存值。 用数学公式表示如下:

RPC 返回的个数 = (row数 每行的列数)/ Min(每行列数,Batch大小) / Caching大小

Result 返回的个数 =( row数 每行的列数 )/ Min(每行列数,Batch大小)

复制

2Hbase Shell中使用

在hbase shell中查询数据,可以在hbase shell中直接使用过滤器:

# hbase shell > scan 'tablename',STARTROW=>'start',COLUMNS=>['family:qualifier'],FILTER=>"ValueFilter(=,'substring:88')"

复制

如上命令所示,查询的是表名为testByCrq,过滤方式是通过value过滤,匹配出value含111的数据。

因在hbase shell中一些 *** 作比较麻烦(比如删除字符需先按住ctrl在点击退格键),且退出后,查询的历史纪录不可考,故如下方式是比较方便的一种:

# echo "scan 'testByCrq', FILTER=>\"ValueFilter(=,'substring:111')\"" | hbase shell

复制

如上命令,可在bash中直接使用,表名是testByCrq,过滤方式是通过value过滤,匹配出value含111的数据,中间的"需要用\转义。

建表

create 'test1', 'lf', 'sf'

-- lf: column family of LONG values (binary value)

-- sf: column family of STRING values

复制

导入数据

put 'test1', 'user1|ts1', 'sf:c1', 'sku1'

put 'test1', 'user1|ts2', 'sf:c1', 'sku188'

put 'test1', 'user1|ts3', 'sf:s1', 'sku123'

put 'test1', 'user2|ts4', 'sf:c1', 'sku2'

put 'test1', 'user2|ts5', 'sf:c2', 'sku288'

put 'test1', 'user2|ts6', 'sf:s1', 'sku222'

put 'test1', 'user3|ts7', 'lf:c1', 12345

put 'test1', 'user3|ts8', 'lf:c1', 67890

复制

1限制条件

scan 'hbase:meta'

scan 'hbase:meta', {COLUMNS => 'info:regioninfo'}

scan 'ns1:t1', {COLUMNS => ['c1', 'c2'], LIMIT => 10, STARTROW => 'xyz'}

scan 't1', {COLUMNS => ['c1', 'c2'], LIMIT => 10, STARTROW => 'xyz'}

scan 't1', {COLUMNS => 'c1', TIMERANGE => [1303668804, 1303668904]}

scan 't1', {REVERSED => true}

复制

2Filter过滤

1rowkey查询

rowkey为user1开头的

scan 'test1', FILTER => "PrefixFilter ('user1')"

ROW COLUMN+CELL

user1|ts1 column=sf:c1, timestamp=1409122354868, value=sku1

user1|ts2 column=sf:c1, timestamp=1409122354918, value=sku188

user1|ts3 column=sf:s1, timestamp=1409122354954, value=sku123

复制

FirstKeyOnlyFilter: 一个rowkey可以有多个version,同一个rowkey的同一个column也会有多个的值, 只拿出key中的第一个column的第一个version KeyOnlyFilter: 只要key,不要value

scan 'test1', FILTER=>"FirstKeyOnlyFilter() AND ValueFilter(=,'binary:sku188') AND KeyOnlyFilter()"

ROW COLUMN+CELL

user1|ts2 column=sf:c1, timestamp=1409122354918, value=

复制

查询rowkey里面包含ts3的

scan 'test1', FILTER=>"RowFilter(=,'substring:ts3')"

ROW COLUMN+CELL

user1|ts3 column=sf:s1, timestamp=1554865926412, value=sku123

复制

从user1|ts2开始,找到所有的rowkey以user1开头的

scan 'test1', {STARTROW=>'user1|ts2', FILTER => "PrefixFilter ('user1')"}

ROW COLUMN+CELL

user1|ts2 column=sf:c1, timestamp=1409122354918, value=sku188

user1|ts3 column=sf:s1, timestamp=1409122354954, value=sku123

复制

从user1|ts2开始,找到所有的到rowkey以user2开头

scan 'test1', {STARTROW=>'user1|ts2', STOPROW=>'user2'}

ROW COLUMN+CELL

user1|ts2 column=sf:c1, timestamp=1409122354918, value=sku188 user1|ts3 column=sf:s1, timestamp=1409122354954, value=sku123

复制

2值查询

谁的值=sku188

scan 'test1', FILTER=>"ValueFilter(=,'binary:sku188')"

ROW COLUMN+CELL

user1|ts2 column=sf:c1, timestamp=1409122354918, value=sku188

复制

谁的值包含88

scan 'test1', FILTER=>"ValueFilter(=,'substring:88')"

ROW COLUMN+CELL

user1|ts2 column=sf:c1, timestamp=1409122354918, value=sku188

user2|ts5 column=sf:c2, timestamp=1409122355030, value=sku288

复制

值小于等于20000

scan 'test1', FILTER=>"ValueFilter(<=,'binary:20000')"

ROW COLUMN+CELL

user3|ts7 column=lf:c1, timestamp=1554866187587, value=12345

复制

注意:如果查询值大于20000,会查出所有值,因为“sku188”等值转为二进制后都大于20000。

substring不能使用小于等于等符号。

3列查询

column为c2,值包含88的用户

scan 'test1', FILTER=>"ColumnPrefixFilter('c2') AND ValueFilter(=,'substring:88')"

ROW COLUMN+CELL

user2|ts5 column=sf:c2, timestamp=1409122355030, value=sku288

复制

通过搜索进来的(column为s)值包含123或者222的用户

scan 'test1', FILTER=>"ColumnPrefixFilter('s') AND ( ValueFilter(=,'substring:123') OR ValueFilter(=,'substring:222') )"

ROW COLUMN+CELL

user1|ts3 column=sf:s1, timestamp=1409122354954, value=sku123

user2|ts6 column=sf:s1, timestamp=1409122355970, value=sku222

复制

列族查询

scan 'test1', FILTER=>"FamilyFilter(=,'substring:lf')"

ROW COLUMN+CELL

user3|ts7 column=lf:c1, timestamp=1554866187587, value=12345

user3|ts8 column=lf:c1, timestamp=1554866294485, value=67890

复制

4时间戳

scan 'test1',{FILTER=>"TimestampsFilter(1448069941270,1548069941230)" }

复制

3java查询

过滤器

HBase 的基本 API,包括增、删、改、查等。

增、删都是相对简单的 *** 作,与传统的 RDBMS 相比,这里的查询 *** 作略显苍白,只能根据特性的行键进行查询(Get)或者根据行键的范围来查询(Scan)。

HBase 不仅提供了这些简单的查询,而且提供了更加高级的过滤器(Filter)来查询。

过滤器的两类参数

过滤器可以根据列族、列、版本等更多的条件来对数据进行过滤,基于 HBase 本身提供的三维有序(行键,列,版本有序),这些过滤器可以高效地完成查询过滤的任务,带有过滤器条件的 RPC 查询请求会把过滤器分发到各个 RegionServer(这是一个服务端过滤器),这样也可以降低网络传输的压力。

使用过滤器至少需要两类参数:

一类是抽象的 *** 作符

HBase 提供了枚举类型的变量来表示这些抽象的 *** 作符:

LESS

LESS_OR_EQUAL

EQUAL

NOT_EQUAL

GREATER_OR_EQUAL

GREATER

NO_OP

另一类是比较器

代表具体的逻辑,例如字节级的比较,字符串级的比较等。

参数基础

有两个参数类在各类Filter中经常出现,统一介绍下:

(1)比较运算符 CompareFilterCompareOp

比较运算符用于定义比较关系,可以有以下几类值供选择:

EQUAL 相等

GREATER 大于

GREATER_OR_EQUAL 大于等于

LESS 小于

LESS_OR_EQUAL 小于等于

NOT_EQUAL 不等于

(2)比较器 ByteArrayComparable

通过比较器可以实现多样化目标匹配效果,比较器有以下子类可以使用:

BinaryComparator 匹配完整字节数组

BinaryPrefixComparator 匹配字节数组前缀

BitComparator

NullComparator

RegexStringComparator 正则表达式匹配

SubstringComparator 子串匹配

1,FilterList

FilterList 代表一个过滤器链,它可以包含一组即将应用于目标数据集的过滤器,过滤器间具有“与” FilterListOperatorMUST_PASS_ALL 和“或” FilterListOperatorMUST_PASS_ONE 关系。

官网实例代码,两个“或”关系的过滤器的写法:

FilterList list = new FilterList(FilterListOperatorMUST_PASS_ONE); //数据只要满足一组过滤器中的一个就可以

SingleColumnValueFilter filter1 = new SingleColumnValueFilter(cf,column,CompareOpEQUAL,BytestoBytes("my value"));

listadd(filter1);

SingleColumnValueFilter filter2 = new SingleColumnValueFilter(cf,column,CompareOpEQUAL,BytestoBytes("my other value"));

listadd(filter2);

Scan scan = new Scan();

scansetFilter(list);

复制

2,列值过滤器--SingleColumnValueFilter

SingleColumnValueFilter 用于测试列值相等 (CompareOpEQUAL ), 不等 (CompareOpNOT_EQUAL),或单侧范围 (eg, CompareOpGREATER)。

构造函数:

(1)比较的关键字是一个字符数组

SingleColumnValueFilter(byte[] family, byte[] qualifier, CompareFilterCompareOp compareOp, byte[] value)

(2)比较的关键字是一个比较器(比较器下一小节做介绍)

SingleColumnValueFilter(byte[] family, byte[] qualifier, CompareFilterCompareOp compareOp, ByteArrayComparable comparator)

注意:根据列的值来决定这一行数据是否返回,落脚点在行,而不是列。我们可以设置filtersetFilterIfMissing(true);如果为true,当这一列不存在时,不会返回,如果为false,当这一列不存在时,会返回所有的列信息

测试表user内容如下:

Table table = connectiongetTable(TableNamevalueOf("user"));

SingleColumnValueFilter scvf= new SingleColumnValueFilter(BytestoBytes("account"), BytestoBytes("name"),

CompareOpEQUAL,"zhangsan"getBytes());

scvfsetFilterIfMissing(true); //默认为false, 没有此列的数据也会返回 ,为true则只返回name=lisi的数据

Scan scan = new Scan();

scansetFilter(scvf);

ResultScanner resultScanner = tablegetScanner(scan);

for (Result result : resultScanner) {

List<Cell> cells= resultlistCells();

for (Cell cell : cells) {

String row = BytestoString(resultgetRow());

String family1 = BytestoString(CellUtilcloneFamily(cell));

String qualifier = BytestoString(CellUtilcloneQualifier(cell));

String value = BytestoString(CellUtilcloneValue(cell));

Systemoutprintln("[row:"+row+"],[family:"+family1+"],[qualifier:"+qualifier+"]"+ ",[value:"+value+"],[time:"+cellgetTimestamp()+"]");

}

}

复制

如果setFilterIfMissing(true), 有匹配只会返回当前列所在的行数据,基于行的数据 country 也返回了,因为他么你的rowkey是相同的

[row:zhangsan_1495527850824],[family:account],[qualifier:country],[value:china],[time:1495636452285]

[row:zhangsan_1495527850824],[family:account],[qualifier:name],[value:zhangsan],[time:1495556648729]

复制

如果setFilterIfMissing(false),有匹配的列的值相同会返回,没有此列的 name的也会返回,, 不匹配的name则不会返回。

下面 红色是匹配列内容的会返回,其他的不是account:name列也会返回,, name=lisi的不会返回,因为不匹配。

[row:lisi_1495527849910],[family:account],[qualifier:idcard],[value:42963319861234561230],[time:1495556647872]

[row:lisi_1495527850111],[family:account],[qualifier:password],[value:123451231236],[time:1495556648013]

[row:lisi_1495527850114],[family:address],[qualifier:city],[value:黄埔],[time:1495556648017]

[row:lisi_1495527850136],[family:address],[qualifier:province],[value:shanghai],[time:1495556648041]

[row:lisi_1495527850144],[family:info],[qualifier:age],[value:21],[time:1495556648045]

[row:lisi_1495527850154],[family:info],[qualifier:sex],[value:女],[time:1495556648056]

[row:lisi_1495527850159],[family:userid],[qualifier:id],[value:002],[time:1495556648060]

[row:wangwu_1495595824517],[family:userid],[qualifier:id],[value:009],[time:1495624624131]

[row:zhangsan_1495527850759],[family:account],[qualifier:idcard],[value:9897645464646],[time:1495556648664]

[row:zhangsan_1495527850759],[family:account],[qualifier:passport],[value:5689879898],[time:1495636370056]

[row:zhangsan_1495527850824],[family:account],[qualifier:country],[value:china],[time:1495636452285]

[row:zhangsan_1495527850824],[family:account],[qualifier:name],[value:zhangsan],[time:1495556648729]

[row:zhangsan_1495527850951],[family:address],[qualifier:province],[value:guangdong],[time:1495556648855]

[row:zhangsan_1495527850975],[family:info],[qualifier:age],[value:100],[time:1495556648878]

[row:zhangsan_1495527851080],[family:info],[qualifier:sex],[value:男],[time:1495556648983]

[row:zhangsan_1495527851095],[family:userid],[qualifier:id],[value:001],[time:1495556648996]

复制

3 键值元数据

由于HBase 采用键值对保存内部数据,键值元数据过滤器评估一行的键(ColumnFamily:Qualifiers)是否存在

31 基于列族过滤数据的FamilyFilter

构造函数:

FamilyFilter(CompareFilterCompareOp familyCompareOp, ByteArrayComparable familyComparator)

代码如下:

public static ResultScanner getDataFamilyFilter(String tableName,String family) throws IOException{

Table table = connectiongetTable(TableNamevalueOf("user"));

FamilyFilter ff = new FamilyFilter(CompareOpEQUAL ,

new BinaryComparator(BytestoBytes("account"))); //表中不存在account列族,过滤结果为空

// new BinaryPrefixComparator(value) //匹配字节数组前缀

// new RegexStringComparator(expr) // 正则表达式匹配

// new SubstringComparator(substr)// 子字符串匹配

Scan scan = new Scan();

// 通过scanaddFamily(family) 也可以实现此 *** 作

scansetFilter(ff);

ResultScanner resultScanner = tablegetScanner(scan);

return resultScanner;

}

复制

测试结果:查询的都是account列簇的内容

[row:lisi_1495527849910],[family:account],[qualifier:idcard],[value:42963319861234561230],[time:1495556647872]

[row:lisi_1495527850081],[family:account],[qualifier:name],[value:lisi],[time:1495556647984]

[row:lisi_1495527850111],[family:account],[qualifier:password],[value:123451231236],[time:1495556648013]

[row:zhangsan_1495527850759],[family:account],[qualifier:idcard],[value:9897645464646],[time:1495556648664]

[row:zhangsan_1495527850759],[family:account],[qualifier:passport],[value:5689879898],[time:1495636370056]

[row:zhangsan_1495527850824],[family:account],[qualifier:country],[value:china],[time:1495636452285]

[row:zhangsan_1495527850824],[family:account],[qualifier:name],[value:zhangsan],[time:1495556648729]

复制

32 基于限定符Qualifier(列)过滤数据的QualifierFilter

构造函数:

QualifierFilter(CompareFilterCompareOp op, ByteArrayComparable qualifierComparator)

Table table = connectiongetTable(TableNamevalueOf("user"));

QualifierFilter ff = new QualifierFilter(

CompareOpEQUAL , new BinaryComparator(BytestoBytes("name")));

// new BinaryPrefixComparator(value) //匹配字节数组前缀

// new RegexStringComparator(expr) // 正则表达式匹配

// new SubstringComparator(substr)// 子字符串匹配

Scan scan = new Scan();

// 通过scanaddFamily(family) 也可以实现此 *** 作

scansetFilter(ff);

ResultScanner resultScanner = tablegetScanner(scan);

复制

测试结果:只返回 name 的列内容

[row:lisi_1495527850081],[family:account],[qualifier:name],[value:lisi],[time:1495556647984]

[row:zhangsan_1495527850824],[family:account],[qualifier:name],[value:zhangsan],[time:1495556648729]

复制

33 基于列名(即Qualifier)前缀过滤数据的ColumnPrefixFilter

( 该功能用QualifierFilter也能实现 )

构造函数:

ColumnPrefixFilter(byte[] prefix)

Table table = connectiongetTable(TableNamevalueOf("user"));

ColumnPrefixFilter ff = new ColumnPrefixFilter(BytestoBytes("name"));

Scan scan = new Scan();

// 通过QualifierFilter的 newBinaryPrefixComparator也可以实现

scansetFilter(ff);

ResultScanner resultScanner = tablegetScanner(scan);

复制

返回结果:

[row:lisi_1495527850081],[family:account],[qualifier:name],[value:lisi],[time:1495556647984]

[row:zhangsan_1495527850824],[family:account],[qualifier:name],[value:zhangsan],[time:1495556648729]

复制

34 基于多个列名(即Qualifier)前缀过滤数据的MultipleColumnPrefixFilter

MultipleColumnPrefixFilter 和 ColumnPrefixFilter 行为差不多,但可以指定多个前缀

byte[][] prefixes = new byte[][] {BytestoBytes("name"), BytestoBytes("age")};

//返回所有行中以name或者age打头的列的数据

MultipleColumnPrefixFilter ff = new MultipleColumnPrefixFilter(prefixes);

Scan scan = new Scan();

scansetFilter(ff);

ResultScanner rs = tablegetScanner(scan);

复制

结果:

[row:lisi_1495527850081],[family:account],[qualifier:name],[value:lisi],[time:1495556647984]

[row:lisi_1495527850144],[family:info],[qualifier:age],[value:21],[time:1495556648045]

[row:zhangsan_1495527850824],[family:account],[qualifier:name],[value:zhangsan],[time:1495556648729]

[row:zhangsan_1495527850975],[family:info],[qualifier:age],[value:100],[time:1495556648878]

复制

35 基于列范围过滤数据ColumnRangeFilter

构造函数:

ColumnRangeFilter(byte[] minColumn, boolean minColumnInclusive, byte[] maxColumn, boolean maxColumnInclusive)

参数解释:

minColumn - 列范围的最小值,如果为空,则没有下限;

minColumnInclusive - 列范围是否包含minColumn ;

maxColumn - 列范围最大值,如果为空,则没有上限;

maxColumnInclusive - 列范围是否包含maxColumn 。

代码:

Table table = connectiongetTable(TableNamevalueOf("user"));

byte[] startColumn = BytestoBytes("a");

byte[] endColumn = BytestoBytes("d");

//返回所有列中从a到d打头的范围的数据,

ColumnRangeFilter ff = new ColumnRangeFilter(startColumn, true, endColumn, true);

Scan scan = new Scan();

scansetFilter(ff);

ResultScanner rs = tablegetScanner(scan);

复制

结果:返回列名开头是a 到 d的所有列数据

[row:lisi_1495527850114],[family:address],[qualifier:city],[value:黄埔],[time:1495556648017]

[row:lisi_1495527850144],[family:info],[qualifier:age],[value:21],[time:1495556648045]

[row:zhangsan_1495527850824],[family:account],[qualifier:country],[value:china],[time:1495636452285]

[row:zhangsan_1495527850975

系统bug。在使用软件hbase写代码时,当代码写入成功却查不出来,是系统bug的问题,将代码重新运行即可。HBase是一个分布式的、面向列的开源数据库,该技术来源于FayChang所撰写的Google论文“Bigtable:一个结构化数据的分布式存储系统”。

在说HBase之前,我想再唠叨几句。做互联网应用的哥们儿应该都清楚,互联网应用这东西,你没办法预测你的系统什么时候会被多少人访问,你面临的用户到底有多少,说不定今天你的用户还少,明天系统用户就变多了,结果您的系统应付不过来了了,不干了,这岂不是咱哥几个的悲哀,说时髦点就叫“杯具啊”。

其实说白了,这些就是事先没有认清楚互联网应用什么才是最重要的。从系统架构的角度来说,互联网应用更加看重系统性能以及伸缩性,而传统企业级应用都是比较看重数据完整性和数据安全性。那么我们就来说说互联网应用伸缩性这事儿对于伸缩性这事儿,哥们儿我也写了几篇博文,想看的兄弟可以参考我以前的博文,对于web server,app server的伸缩性,我在这里先不说了,因为这部分的伸缩性相对来说比较容易一点,我主要来回顾一些一个慢慢变大的互联网应用如何应对数据库这一层的伸缩。

首先刚开始,人不多,压力也不大,搞一台数据库服务器就搞定了,此时所有的东东都塞进一个Server里,包括web server,app server,db server,但是随着人越来越多,系统压力越来越多,这个时候可能你把web server,app server和db server分离了,好歹这样可以应付一阵子,但是随着用户量的不断增加,你会发现,数据库这哥们不行了,速度老慢了,有时候还会宕掉,所以这个时候,你得给数据库这哥们找几个伴,这个时候Master-Salve就出现了,这个时候有一个Master Server专门负责接收写 *** 作,另外的几个Salve Server专门进行读取,这样Master这哥们终于不抱怨了,总算读写分离了,压力总算轻点了,这个时候其实主要是对读取 *** 作进行了水平扩张,通过增加多个Salve来克服查询时CPU瓶颈。一般这样下来,你的系统可以应付一定的压力,但是随着用户数量的增多,压力的不断增加,你会发现Master server这哥们的写压力还是变的太大,没办法,这个时候怎么办呢?你就得切分啊,俗话说“只有切分了,才会有伸缩性嘛”,所以啊,这个时候只能分库了,这也是我们常说的数据库“垂直切分”,比如将一些不关联的数据存放到不同的库中,分开部署,这样终于可以带走一部分的读取和写入压力了,Master又可以轻松一点了,但是随着数据的不断增多,你的数据库表中的数据又变的非常的大,这样查询效率非常低,这个时候就需要进行“水平分区”了,比如通过将User表中的数据按照10W来划分,这样每张表不会超过10W了。

综上所述,一般一个流行的web站点都会经历一个从单台DB,到主从复制,到垂直分区再到水平分区的痛苦的过程。其实数据库切分这事儿,看起来原理貌似很简单,如果真正做起来,我想凡是sharding过数据库的哥们儿都深受其苦啊。对于数据库伸缩的文章,哥们儿可以看看后面的参考资料介绍。

好了,从上面的那一堆废话中,我们也发现数据库存储水平扩张scale out是多么痛苦的一件事情,不过幸好技术在进步,业界的其它弟兄也在努力,09年这一年出现了非常多的NoSQL数据库,更准确的应该说是No relation数据库,这些数据库多数都会对非结构化的数据提供透明的水平扩张能力,大大减轻了哥们儿设计时候的压力。下面我就拿Hbase这分布式列存储系统来说说。

一 Hbase是个啥东东?

在说Hase是个啥家伙之前,首先我们来看看两个概念,面向行存储和面向列存储。面向行存储,我相信大伙儿应该都清楚,我们熟悉的RDBMS就是此种类型的,面向行存储的数据库主要适合于事务性要求严格场合,或者说面向行存储的存储系统适合OLTP,但是根据CAP理论,传统的RDBMS,为了实现强一致性,通过严格的ACID事务来进行同步,这就造成了系统的可用性和伸缩性方面大大折扣,而目前的很多NoSQL产品,包括Hbase,它们都是一种最终一致性的系统,它们为了高的可用性牺牲了一部分的一致性。好像,我上面说了面向列存储,那么到底什么是面向列存储呢?Hbase,Casandra,Bigtable都属于面向列存储的分布式存储系统。看到这里,如果您不明白Hbase是个啥东东,不要紧,我再总结一下下:

Hbase是一个面向列存储的分布式存储系统,它的优点在于可以实现高性能的并发读写 *** 作,同时Hbase还会对数据进行透明的切分,这样就使得存储本身具有了水平伸缩性。

二 Hbase数据模型

HBase,Cassandra的数据模型非常类似,他们的思想都是来源于Google的Bigtable,因此这三者的数据模型非常类似,唯一不同的就是Cassandra具有Super cloumn family的概念,而Hbase目前我没发现。好了,废话少说,我们来看看Hbase的数据模型到底是个啥东东。

在Hbase里面有以下两个主要的概念,Row key,Column Family,我们首先来看看Column family,Column family中文又名“列族”,Column family是在系统启动之前预先定义好的,每一个Column Family都可以根据“限定符”有多个column下面我们来举个例子就会非常的清晰了。

假如系统中有一个User表,如果按照传统的RDBMS的话,User表中的列是固定的,比如schema 定义了name,age,sex等属性,User的属性是不能动态增加的。但是如果采用列存储系统,比如Hbase,那么我们可以定义User表,然后定义info 列族,User的数据可以分为:info:name = zhangsan,info:age=30,info:sex=male等,如果后来你又想增加另外的属性,这样很方便只需要info:newProperty就可以了。

也许前面的这个例子还不够清晰,我们再举个例子来解释一下,熟悉SNS的朋友,应该都知道有好友Feed,一般设计Feed,我们都是按照“某人在某时做了标题为某某的事情”,但是同时一般我们也会预留一下关键字,比如有时候feed也许需要url,feed需要image属性等,这样来说,feed本身的属性是不确定的,因此如果采用传统的关系数据库将非常麻烦,况且关系数据库会造成一些为null的单元浪费,而列存储就不会出现这个问题,在Hbase里,如果每一个column 单元没有值,那么是占用空间的。下面我们通过两张图来形象的表示这种关系:

上图是传统的RDBMS设计的Feed表,我们可以看出feed有多少列是固定的,不能增加,并且为null的列浪费了空间。但是我们再看看下图,下图为Hbase,Cassandra,Bigtable的数据模型图,从下图可以看出,Feed表的列可以动态的增加,并且为空的列是不存储的,这就大大节约了空间,关键是Feed这东西随着系统的运行,各种各样的Feed会出现,我们事先没办法预测有多少种Feed,那么我们也就没有办法确定Feed表有多少列,因此Hbase,Cassandra,Bigtable的基于列存储的数据模型就非常适合此场景。说到这里,采用Hbase的这种方式,还有一个非常重要的好处就是Feed会自动切分,当Feed表中的数据超过某一个阀值以后,Hbase会自动为我们切分数据,这样的话,查询就具有了伸缩性,而再加上Hbase的弱事务性的特性,对Hbase的写入 *** 作也将变得非常快。

上面说了Column family,那么我之前说的Row key是啥东东,其实你可以理解row key为RDBMS中的某一个行的主键,但是因为Hbase不支持条件查询以及Order by等查询,因此Row key的设计就要根据你系统的查询需求来设计了额。我还拿刚才那个Feed的列子来说,我们一般是查询某个人最新的一些Feed,因此我们Feed的Row key可以有以下三个部分构成<userId><timestamp><feedId>,这样以来当我们要查询某个人的最进的Feed就可以指定Start Rowkey为<userId><0><0>,End Rowkey为<userId><LongMAX_VALUE><LongMAX_VALUE>来查询了,同时因为Hbase中的记录是按照rowkey来排序的,这样就使得查询变得非常快。

三 Hbase的优缺点

1 列的可以动态增加,并且列为空就不存储数据,节省存储空间

2 Hbase自动切分数据,使得数据存储自动具有水平scalability

3 Hbase可以提供高并发读写 *** 作的支持

Hbase的缺点:

1 不能支持条件查询,只支持按照Row key来查询

2 暂时不能支持Master server的故障切换,当Master宕机后,整个存储系统就会挂掉

四补充

1数据类型,HBase只有简单的字符类型,所有的类型都是交由用户自己处理,它只保存字符串。而关系数据库有丰富的类型和存储方式。

2数据 *** 作:HBase只有很简单的插入、查询、删除、清空等 *** 作,表和表之间是分离的,没有复杂的表和表之间的关系,而传统数据库通常有各式各样的函数和连接 *** 作。

3存储模式:HBase是基于列存储的,每个列族都由几个文件保存,不同的列族的文件时分离的。而传统的关系型数据库是基于表格结构和行模式保存的

4数据维护,HBase的更新 *** 作不应该叫更新,它实际上是插入了新的数据,而传统数据库是替换修改

5可伸缩性,Hbase这类分布式数据库就是为了这个目的而开发出来的,所以它能够轻松增加或减少硬件的数量,并且对错误的兼容性比较高。而传统数据库通常需要增加中间层才能实现类似的功能

以上就是关于Hadoop生态系统-新手快速入门(含HDFS、HBase系统架构)全部的内容,包括:Hadoop生态系统-新手快速入门(含HDFS、HBase系统架构)、hbase和hive的差别是什么,各自适用在什么场景中、hive与hbase区别等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/sjk/10191087.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-06
下一篇 2023-05-06

发表评论

登录后才能评论

评论列表(0条)

保存