追踪整个compaction过程,我们不难发现其中多了不少get、write、delete请求,数据量非常大的情况下无形给HBase带来不小压力。留意一下,这里可能也是我们重点优化的地方。
追加模式(appends)相关文档:
#appends
Also in 2.2, writing to HBase columns via appends is now supported. This can improve both read and write performance in that TSDs will no longer maintain a queue of rows to compact at the end of each hour, thus preventing a massive read and re-write operation in HBase. However due to the way appends operate in HBase, an increase in CPU utilization, store file size and HDFS traffic will occur on the region servers. Make sure to monitor your HBase servers closely.
tsd.storage.enable_appends:默认是false
在追加模式下,opentsdb写入的时候,会将rowkey相同的点的value值写到一个单独的ColumnQualifier(0x050000)中。所以与之前的直接写入模式是兼容的,这就意味着可以随时启用或者禁用追加模式。
显然这就是我们想要的压缩后的效果。少了把已经写入HBase的数据拉过来在opentsdb内存压缩,回写数据,再删除原数据的一系列操作,当然了压力应该是丢给了HBase。
追加模式会消耗更多的HBase集群的资源(官方是这么说的,究竟多大,有待研究),另外本人猜测对于大量高并发的写入可能有锁的同步问题,讲道理单从瞬间写入性能考虑,追加模式下的性能应该是不及之前的直接写入。
opentsdb UID的分配(UID Assignment)相关文档:
#uid
相信到这里应该已经到UID有一定的认识了,使用UID大大节省了存储空间。
Within the storage system there is a counter that is incremented for each metric, tagk and tagv. When you create a new tsdb-uid table, this counter is set to 0 for each type.
很类似mysql中的自增主键。见UniqueId#allocateUid()
private Deferred<Long> allocateUid() { // randomize_id默认是false,两种方式:一种是随机数uid,另外一种是递增uid // tagk和tagv目前无法配置,用的是递增uid(metric倒是可配 tsd.core.uid.random_metrics默认false) if (randomize_id) { return Deferred.fromResult(RandomUniqueId.getRandomUID()); } else { //实际走这里,会去hbase的tsdb-uid表请求递增uid return client.atomicIncrement(new AtomicIncrementRequest(table, MAXID_ROW, ID_FAMILY, kind)); } }tsdb-uid表中rowkey为0x00的cell中存有目前三种类型的最大UID
这里我们看到metric、tagk、tagv三种类型的UID映射是独立的。另外,注意两个与此相关的配置项
tsd.core.auto_create_metrics:默认为false,是否给tsdb-uid表中不存在的metric分配UID,false的情况下,写入新的metric时序数据会抛出异常
tsd.core.preload_uid_cache:默认为false,是否程序启动时就从tsdb-uid表获取UID并缓存在本地,见TSDB#TSDB(final HBaseClient client, final Config config)
if (config.getBoolean("tsd.core.preload_uid_cache")) { final ByteMap<UniqueId> uid_cache_map = new ByteMap<UniqueId>(); uid_cache_map.put(METRICS_QUAL.getBytes(CHARSET), metrics); uid_cache_map.put(TAG_NAME_QUAL.getBytes(CHARSET), tag_names); uid_cache_map.put(TAG_VALUE_QUAL.getBytes(CHARSET), tag_values); UniqueId.preloadUidCache(this, uid_cache_map); }从这里我们也可以看到使用这种递增UID分配方式,先来的tagk必然会分配到数值较小的UID,后来的tagk会分配到数值较大的UID,如此一来结合上文写入的时候rowkey中的tags会按照tagk_uid的byte数组进行排序,就能得出最先写入的tagk是排在rowkey中较为靠前的位置,那么知道了这种规则,在某些情况下对于查询优化有没有帮助呢?
opentsdb 查询细节(Reading)相关文档:
查询放在这个地方讲是因为我们只有弄清楚数据是怎么存的,才会明白如何取。通过前文我们知道写入的时候rowkey中的tags会按照tagk_uid的byte数组进行排序,那么同样从HBase读数据的时候讲道理也应该这样排序是不是。来看QueryUtil#setDataTableScanFilter()
但是,正常情况下的scan(除非查询的时候设置explicit_tags为true),对于tag的过滤并不是直接拼在rowkey中,而是放在scanner.setFilter(regex_filter)