`

从OpenTsdb来分析rowkey设计

 
阅读更多
讨论此问题前,先理解一个问题。

关于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);
   }
  • 大小: 42 KB
  • 大小: 6.7 KB
分享到:
评论

相关推荐

    HBase-RowKey与索引设计(高清)

    例如,在OpenTSDB中,RowKey的设计通常结合时间戳和度量指标,以实现高效的时间序列数据存储和查询;JanusGraph作为图数据库,它的RowKey设计则更多考虑图结构的连通性;而GeoMesa支持空间数据存储,其RowKey的设计...

    OpenTSDB 文档

    OpenTSDB是一种分布式、可扩展的时间序列数据库,其设计目的是为了处理大量来自各种监控系统的数据,例如网络设备、操作系统或应用程序的监控数据。它基于HBase构建,能够高效地存储和检索大量时间序列数据,并且...

    基于opentsdb的分布式实时监控方案.pdf

    - **Rowkey设计**:优化rowkey的长度,分配固定长度的字节给metric、tagk和tagv,以节省存储空间,提升查询效率,并降低内存占用。 - **减少Key-Value数**:通过合并多行单列和单行多列,减少数据存储和网络传输的...

    HBase 实战经验分享.pdf

    比如OpenTSDB用于监控系统的时序数据存储,其RowKey设计兼顾了时间顺序和数据分布;JanusGraph利用HBase存储图数据,通过索引优化图遍历效率;GeoMesa则展示了如何处理地理空间数据,利用特定的RowKey和索引策略来...

    模拟风电数据日志分析设计

    - **存储优化**:OpenTSDB通过为每个`metric`、`tagkey`和`tagvalue`设计固定长度的UID,缩短Rowkey长度;支持数据聚合存储,如将一段时间内的数据合并存储,减少存储占用。 #### 数据处理流程 - **Flume**:负责...

    时序数据库OpenTSDB构建工业大数据存储平台

    在存储机制上,OpenTSDB将一小时内数据整合为一个键值对,利用HBase的Rowkey设计实现数据压缩。通过相同的metric和tag值,多个秒级数据被合并,极大地提高了存储效率。 在实时存储架构中,OpenTSDB与Apache Flume、...

    2018 Apache HBase 技术实战专刊

    此外,专刊还涉及了HBase的基本知识、RowKey设计指南、MOB使用指南、实战案例分析等。HBase的RowKey设计对于优化查询性能至关重要,而MOB(Mobile Object Base)是HBase的一个功能,用于存储和管理大型二进制对象,...

    2018HBase技术总结

    同时,HBase的RowKey设计和MOB使用指南为开发人员提供了实用的实战指导。 在HBase的生态建设方面,文中提到了构建HBase平台的实践和应用,强调了HBase平台化后的新特性,如HBase2.0&阿里云HBase的解读,进一步说明...

    1-5+HBase+在网易的探索实践.pdf

    HBase是一种分布式、版本化、列族式的NoSQL数据库,主要设计用于处理大规模数据集。在大数据领域,HBase以其高效的数据存储和查询能力,尤其是在实时在线服务、监控和数据分析等方面展现出强大的优势。它与Hadoop...

    Hbase 使用场景及案例

    在设计时,需要合理规划RowKey,以便优化查询性能。此外,HBase不适合涉及复杂关系和多表操作的业务,以及需要强事务一致性的情况。在实际应用中,要注意根据业务需求调整HBase的配置,比如关闭Split以保持Region...

    第1章 HBase入门_v1.3.docx

    由于其设计主要用于批处理任务,因此当涉及到随机访问或快速查询大量数据时,效率相对较低。 - **HBase 与 NoSQL**:HBase 是一个基于Hadoop之上的分布式、可扩展的NoSQL数据库。它受到了Google BigTable的启发,...

    深度解读!时序数据库HiTSDB:分布式流式聚合引擎

    时序数据库HiTSDB,作为一款专为处理时间序列数据设计的存储系统,尤其在阿里巴巴集团内部广泛应用并不断优化。然而,随着公有云服务的发展,HiTSDB面临了一系列挑战,尤其是在处理聚合查询时,如数据点过多导致的栈...

Global site tag (gtag.js) - Google Analytics