讨论此问题前,先理解一个问题。
关于Hbase热点问题
当处理由连续事件得到的数据时,即时间上连续的数据。这些数据可能来自于某个传感器网络、证券交易或者一个监控系统。它们显著的特点就是rowkey中含有事件发生时间。带来的一个问题便是HBase对于row的不均衡分布,它们被存储在一个唯一的rowkey区间中,被称为region,区间的范围被称为Start Key和End Key。
对于单调递增的时间类型数据,很容易被散列到同一个Region中,这样它们会被存储在同一个服务器上,从而所有的访问和更新操作都会集中到这一台服务器上,从而在集群中形成一个hotspot,从而不能将集群的整体性能发挥出来。
要解决这个问题是非常容易的,只需要将所有的数据散列到全部的Region上即可。这是可以做到的,比如,在rowkey前面加上一个非线程序列。
OpenTsdb 数据库设计
我们来看一下它的建表语句
表 tsdb包含一个列族“T”,tsdb-uid包含两个列族 id和name,在hbase中列族名字越短越有利于存储,因为它占用的字节数越短。
tsdb 有标签的概念:我理解为就是指标,例如mysql.bytes_sent就是一个指标,每个指标在tsdb表中有一个唯一的标识unique ID 简称UID.
tsdb_uid是 tsdb 的辅助表,用于查找uid对应的指标或者指标对应的uid
新增一个指标,会在这个表中增加两行记录,一行rowkey是指标名,id是uid,另外一行rowkey是uid,name是指标名.
在tsdb_uid表中列族 id 和name 都分别包含三列
metrics 指标名或者指标uid
tagk 标签名或者标签名uid
tagv 标签值或者标签值uid
id 列族:用来将字符串映射到UID
name 列族:用来将一个UID映射到一个字符串
例如 我要存储 host=web、user=admin、project=uc 的web访问量(web.pv)
tsdb表中其实 仅用到和rowkey 列族T是没有用到的,知识因为HBase规定必须至少有一个列族。tsdb表的rowkey设计很值得我们去了解。
在OpenTSDB中,所有数据存储在tsdb的表中,这是为了充分利用hbase有序和region分布式的特点。所有的值都保存在列族t中。
rowkey为<metric_uid><timestamp><tagk1><tagv1>[...<tagkN><tagvN>],
这样设计的目的是:OpenTsdb以监控指标为中心的查询进行优化,所以监控指标UID排在最前面,在同一个监控指标中,按时间戳进行排序,行健里时间戳存储四舍五入到60分钟,所以1个小时内统一监控指标的数据会存储在一起。
OpenTSDB的tsdb启动之后,会监控指定的socket端口(默认为4242),接收到监控数据,包括指标、时间戳、数据、tag标签,tag标签包括tag名称ID和tag值ID。例如:
继续用上面的例子
web.pv 1292148123 42 host=web user=admin project=uc
对于指标web.pv的ID为:[0, 0, 1],host标签名称的ID为:[0, 0, 2], web标签值的ID为:[0, 0, 3], 标签名称user的ID为:[0, 0, 4] admin标签值的ID为:[0, 0, 5],标签名称project的ID为:[0, 0, 6] uc标签值的ID为:[0, 0, 7],他们组成rowkey:
[0,0,1, 77,4,-99,32, 0,0,2, 0,0,3, 0,0,4, 0,0,5, 0,0,6, 0,0,7]
metricid timestamp tagk1 tagv1 tagk2 tagv2 tagk3 tagv3
可以看到,对于metric + tags相同的数据都会连续存放,且metic相同的数据也会连续存放,这样对于scan以及做aggregation都非常有帮助
对于时间戳为1292148123的数据点来说,其转换为以小时为单位的基准时间(去掉小时后的秒)为129214800,偏移为123,转换为二进制为1111011,因为该值为整数且长度为8位(对应为2byte,故最后3bit为100),故其对应的列族名为:0000011110110100,将其转换为十六进制为07B4
OPENTSDB两种查询数据下hbase表设计优势的体现:
1:自动补全功能
了解此功能前首先了解一下hbase 的block cache 功能
HBase上Regionserver的内存分为两个部分,一部分作为Memstore,主要用来写;另外一部分作为BlockCache,主要用于读。
写请求会先写入Memstore,Regionserver会给每个region提供一个Memstore,当Memstore满64MB以后,会启动 flush刷新到磁盘。当Memstore的总大小超过限制时(heapsize * hbase.regionserver.global.memstore.upperLimit * 0.9),会强行启动flush进程,从最大的Memstore开始flush直到低于限制。
读请求先到Memstore中查数据,查不到就到BlockCache中查,再查不到就会到磁盘上读,并把读的结果放入BlockCache。由于BlockCache采用的是LRU策略,因此BlockCache达到上限(heapsize * hfile.block.cache.size * 0.85)后,会启动淘汰机制,淘汰掉最老的一批数据。
hbase 在每张表的rowkey上建立索引,定位起始位置会非常快,然后通过block cache 查询数据,连续的数据例如 mysql_xxx 会连续存放在一起,scan tsdb_uid 表的 name_uid映射记录即可快速查询。
2:读取时间序列数据
使用一种建立在行健上的正则表达式
可以试着读一下源代码。
关于Hbase热点问题
当处理由连续事件得到的数据时,即时间上连续的数据。这些数据可能来自于某个传感器网络、证券交易或者一个监控系统。它们显著的特点就是rowkey中含有事件发生时间。带来的一个问题便是HBase对于row的不均衡分布,它们被存储在一个唯一的rowkey区间中,被称为region,区间的范围被称为Start Key和End Key。
对于单调递增的时间类型数据,很容易被散列到同一个Region中,这样它们会被存储在同一个服务器上,从而所有的访问和更新操作都会集中到这一台服务器上,从而在集群中形成一个hotspot,从而不能将集群的整体性能发挥出来。
要解决这个问题是非常容易的,只需要将所有的数据散列到全部的Region上即可。这是可以做到的,比如,在rowkey前面加上一个非线程序列。
OpenTsdb 数据库设计
我们来看一下它的建表语句
TSDB_TABLE=${TSDB_TABLE-'tsdb'} UID_TABLE=${UID_TABLE-'tsdb-uid'} create '$UID_TABLE', {NAME => 'id', COMPRESSION => '$COMPRESSION', BLOOMFILTER => '$BLOOMFILTER'}, {NAME => 'name', COMPRESSION => '$COMPRESSION', BLOOMFILTER => '$BLOOMFILTER'} create '$TSDB_TABLE', {NAME => 't', VERSIONS => 1, COMPRESSION => '$COMPRESSION', BLOOMFILTER => '$BLOOMFILTER'}
表 tsdb包含一个列族“T”,tsdb-uid包含两个列族 id和name,在hbase中列族名字越短越有利于存储,因为它占用的字节数越短。
tsdb 有标签的概念:我理解为就是指标,例如mysql.bytes_sent就是一个指标,每个指标在tsdb表中有一个唯一的标识unique ID 简称UID.
tsdb_uid是 tsdb 的辅助表,用于查找uid对应的指标或者指标对应的uid
新增一个指标,会在这个表中增加两行记录,一行rowkey是指标名,id是uid,另外一行rowkey是uid,name是指标名.
在tsdb_uid表中列族 id 和name 都分别包含三列
metrics 指标名或者指标uid
tagk 标签名或者标签名uid
tagv 标签值或者标签值uid
id 列族:用来将字符串映射到UID
name 列族:用来将一个UID映射到一个字符串
例如 我要存储 host=web、user=admin、project=uc 的web访问量(web.pv)
tsdb表中其实 仅用到和rowkey 列族T是没有用到的,知识因为HBase规定必须至少有一个列族。tsdb表的rowkey设计很值得我们去了解。
监控指标UID(3byte) | 部分时间戳(4byte) | 标签1名UID(3byte) | 标签1值UID(3byte) | .... |
在OpenTSDB中,所有数据存储在tsdb的表中,这是为了充分利用hbase有序和region分布式的特点。所有的值都保存在列族t中。
rowkey为<metric_uid><timestamp><tagk1><tagv1>[...<tagkN><tagvN>],
这样设计的目的是:OpenTsdb以监控指标为中心的查询进行优化,所以监控指标UID排在最前面,在同一个监控指标中,按时间戳进行排序,行健里时间戳存储四舍五入到60分钟,所以1个小时内统一监控指标的数据会存储在一起。
OpenTSDB的tsdb启动之后,会监控指定的socket端口(默认为4242),接收到监控数据,包括指标、时间戳、数据、tag标签,tag标签包括tag名称ID和tag值ID。例如:
继续用上面的例子
web.pv 1292148123 42 host=web user=admin project=uc
对于指标web.pv的ID为:[0, 0, 1],host标签名称的ID为:[0, 0, 2], web标签值的ID为:[0, 0, 3], 标签名称user的ID为:[0, 0, 4] admin标签值的ID为:[0, 0, 5],标签名称project的ID为:[0, 0, 6] uc标签值的ID为:[0, 0, 7],他们组成rowkey:
[0,0,1, 77,4,-99,32, 0,0,2, 0,0,3, 0,0,4, 0,0,5, 0,0,6, 0,0,7]
metricid timestamp tagk1 tagv1 tagk2 tagv2 tagk3 tagv3
可以看到,对于metric + tags相同的数据都会连续存放,且metic相同的数据也会连续存放,这样对于scan以及做aggregation都非常有帮助
对于时间戳为1292148123的数据点来说,其转换为以小时为单位的基准时间(去掉小时后的秒)为129214800,偏移为123,转换为二进制为1111011,因为该值为整数且长度为8位(对应为2byte,故最后3bit为100),故其对应的列族名为:0000011110110100,将其转换为十六进制为07B4
OPENTSDB两种查询数据下hbase表设计优势的体现:
1:自动补全功能
了解此功能前首先了解一下hbase 的block cache 功能
HBase上Regionserver的内存分为两个部分,一部分作为Memstore,主要用来写;另外一部分作为BlockCache,主要用于读。
写请求会先写入Memstore,Regionserver会给每个region提供一个Memstore,当Memstore满64MB以后,会启动 flush刷新到磁盘。当Memstore的总大小超过限制时(heapsize * hbase.regionserver.global.memstore.upperLimit * 0.9),会强行启动flush进程,从最大的Memstore开始flush直到低于限制。
读请求先到Memstore中查数据,查不到就到BlockCache中查,再查不到就会到磁盘上读,并把读的结果放入BlockCache。由于BlockCache采用的是LRU策略,因此BlockCache达到上限(heapsize * hfile.block.cache.size * 0.85)后,会启动淘汰机制,淘汰掉最老的一批数据。
hbase 在每张表的rowkey上建立索引,定位起始位置会非常快,然后通过block cache 查询数据,连续的数据例如 mysql_xxx 会连续存放在一起,scan tsdb_uid 表的 name_uid映射记录即可快速查询。
2:读取时间序列数据
使用一种建立在行健上的正则表达式
可以试着读一下源代码。
/** * Sets the server-side regexp filter on the scanner. * In order to find the rows with the relevant tags, we use a * server-side filter that matches a regular expression on the row key. * @param scanner The scanner on which to add the filter. */ private void createAndSetFilter(final Scanner scanner) { if (group_bys != null) { Collections.sort(group_bys, Bytes.MEMCMP); } final short name_width = tsdb.tag_names.width(); final short value_width = tsdb.tag_values.width(); final short tagsize = (short) (name_width + value_width); // Generate a regexp for our tags. Say we have 2 tags: { 0 0 1 0 0 2 } // and { 4 5 6 9 8 7 }, the regexp will be: // "^.{7}(?:.{6})*\\Q\000\000\001\000\000\002\\E(?:.{6})*\\Q\004\005\006\011\010\007\\E(?:.{6})*$" final StringBuilder buf = new StringBuilder( 15 // "^.{N}" + "(?:.{M})*" + "$" + ((13 + tagsize) // "(?:.{M})*\\Q" + tagsize bytes + "\\E" * (tags.size() + (group_bys == null ? 0 : group_bys.size() * 3)))); // In order to avoid re-allocations, reserve a bit more w/ groups ^^^ // Alright, let's build this regexp. From the beginning... buf.append("(?s)" // Ensure we use the DOTALL flag. + "^.{") // ... start by skipping the metric ID and timestamp. .append(tsdb.metrics.width() + Const.TIMESTAMP_BYTES) .append("}"); final Iterator<byte[]> tags = this.tags.iterator(); final Iterator<byte[]> group_bys = (this.group_bys == null ? new ArrayList<byte[]>(0).iterator() : this.group_bys.iterator()); byte[] tag = tags.hasNext() ? tags.next() : null; byte[] group_by = group_bys.hasNext() ? group_bys.next() : null; // Tags and group_bys are already sorted. We need to put them in the // regexp in order by ID, which means we just merge two sorted lists. do { // Skip any number of tags. buf.append("(?:.{").append(tagsize).append("})*\\Q"); if (isTagNext(name_width, tag, group_by)) { addId(buf, tag); tag = tags.hasNext() ? tags.next() : null; } else { // Add a group_by. addId(buf, group_by); final byte[][] value_ids = (group_by_values == null ? null : group_by_values.get(group_by)); if (value_ids == null) { // We don't want any specific ID... buf.append(".{").append(value_width).append('}'); // Any value ID. } else { // We want specific IDs. List them: /(AAA|BBB|CCC|..)/ buf.append("(?:"); for (final byte[] value_id : value_ids) { buf.append("\\Q"); addId(buf, value_id); buf.append('|'); } // Replace the pipe of the last iteration. buf.setCharAt(buf.length() - 1, ')'); } group_by = group_bys.hasNext() ? group_bys.next() : null; } } while (tag != group_by); // Stop when they both become null. // Skip any number of tags before the end. buf.append("(?:.{").append(tagsize).append("})*$"); scanner.setKeyRegexp(buf.toString(), CHARSET); }
发表评论
-
Sort-based Shuffle的设计与实现
2016-03-15 08:49 811原文 http://www.cnblogs.com/hsea ... -
spark的几个重要概念
2015-12-04 14:09 0本节主要记录以下几个概念 一:RDD的五大特点 二:RDD 窄 ... -
spark部署安装调试
2015-12-02 11:28 737本节记录spark下载-->编译-->安装--&g ... -
spark基本概念
2015-11-12 10:45 787记录一下课堂笔记: ... -
hadoop计算能力调度器配置
2015-10-29 10:39 1017问题出现 hadoop默认调度器是FIFO,其原理就是先按照作 ... -
HBase在各大应用中的优化和改进
2015-10-28 14:59 695Facebook之前曾经透露过Facebook的hbase架构 ... -
一篇很好的解决系统问题过程描述文章
2015-09-23 08:40 498在网上看到的一篇解决h ... -
通过GeoHash核心原理来分析hbase rowkey设计
2015-09-08 15:49 3518注:本文是结合hbase ... -
HBase中asynchbase的使用方式
2015-08-25 10:32 8194Hbase的原生java 客户端是完全同步的,当你使用原生AP ... -
Mapreduce优化的点滴
2015-07-16 15:18 827注:转载 1. 使用自定义Writable 自带的Text ... -
hadoop 如何自定义类型
2015-07-15 09:37 1236记录一下hadoop 数据类型章节的笔记,以便后期使用,本文是 ... -
napreduce shuffle 过程记录
2015-07-10 11:23 758在我看来 hadoop的核心是mapre ... -
ZooKeeper伪分布式集群安装及使用
2015-02-13 08:29 9201. zookeeper介绍 ZooKeeper是一个为分 ... -
hadoop-mahout 核心算法总结
2015-02-07 10:08 1554其实大家都知道hadoop为我们提供了一个大的框架,真正的 ... -
推荐引擎内部原理--mahout
2015-01-22 11:11 569转载自:https://www.ibm.com/devel ... -
hadoop 动态添加删除节点
2015-01-20 13:39 662转自:http://www.cnblogs.com/rill ... -
hbase hadoop zookeeper
2015-01-19 14:47 0hadoop 部署手册 http://www.iteblo ... -
mapreduce 开发以及部署
2015-01-16 13:56 836前面几篇文章的梳理让我对hadoop新yarn 框架有了一 ... -
hadoop yarn几个问题的记录
2015-01-13 11:48 657本文主要介绍以下几 ... -
hadoop集群部署时候的几个问题记录
2015-01-13 10:24 738本章部署一个hadoop 集群 ...
相关推荐
例如,在OpenTSDB中,RowKey的设计通常结合时间戳和度量指标,以实现高效的时间序列数据存储和查询;JanusGraph作为图数据库,它的RowKey设计则更多考虑图结构的连通性;而GeoMesa支持空间数据存储,其RowKey的设计...
OpenTSDB是一种分布式、可扩展的时间序列数据库,其设计目的是为了处理大量来自各种监控系统的数据,例如网络设备、操作系统或应用程序的监控数据。它基于HBase构建,能够高效地存储和检索大量时间序列数据,并且...
- **Rowkey设计**:优化rowkey的长度,分配固定长度的字节给metric、tagk和tagv,以节省存储空间,提升查询效率,并降低内存占用。 - **减少Key-Value数**:通过合并多行单列和单行多列,减少数据存储和网络传输的...
比如OpenTSDB用于监控系统的时序数据存储,其RowKey设计兼顾了时间顺序和数据分布;JanusGraph利用HBase存储图数据,通过索引优化图遍历效率;GeoMesa则展示了如何处理地理空间数据,利用特定的RowKey和索引策略来...
- **存储优化**:OpenTSDB通过为每个`metric`、`tagkey`和`tagvalue`设计固定长度的UID,缩短Rowkey长度;支持数据聚合存储,如将一段时间内的数据合并存储,减少存储占用。 #### 数据处理流程 - **Flume**:负责...
在存储机制上,OpenTSDB将一小时内数据整合为一个键值对,利用HBase的Rowkey设计实现数据压缩。通过相同的metric和tag值,多个秒级数据被合并,极大地提高了存储效率。 在实时存储架构中,OpenTSDB与Apache Flume、...
此外,专刊还涉及了HBase的基本知识、RowKey设计指南、MOB使用指南、实战案例分析等。HBase的RowKey设计对于优化查询性能至关重要,而MOB(Mobile Object Base)是HBase的一个功能,用于存储和管理大型二进制对象,...
同时,HBase的RowKey设计和MOB使用指南为开发人员提供了实用的实战指导。 在HBase的生态建设方面,文中提到了构建HBase平台的实践和应用,强调了HBase平台化后的新特性,如HBase2.0&阿里云HBase的解读,进一步说明...
HBase是一种分布式、版本化、列族式的NoSQL数据库,主要设计用于处理大规模数据集。在大数据领域,HBase以其高效的数据存储和查询能力,尤其是在实时在线服务、监控和数据分析等方面展现出强大的优势。它与Hadoop...
在设计时,需要合理规划RowKey,以便优化查询性能。此外,HBase不适合涉及复杂关系和多表操作的业务,以及需要强事务一致性的情况。在实际应用中,要注意根据业务需求调整HBase的配置,比如关闭Split以保持Region...
由于其设计主要用于批处理任务,因此当涉及到随机访问或快速查询大量数据时,效率相对较低。 - **HBase 与 NoSQL**:HBase 是一个基于Hadoop之上的分布式、可扩展的NoSQL数据库。它受到了Google BigTable的启发,...
时序数据库HiTSDB,作为一款专为处理时间序列数据设计的存储系统,尤其在阿里巴巴集团内部广泛应用并不断优化。然而,随着公有云服务的发展,HiTSDB面临了一系列挑战,尤其是在处理聚合查询时,如数据点过多导致的栈...