tsdb-tree: 作用,可按照树形层次结构组织时序,就像浏览文件系统一样浏览时序。相关介绍。这里就不细说了,有兴趣的话看下上面链接中官方介绍的Examples,就能秒懂是干嘛的。
opentsdb 存储细节(Writing)相关文档:
只有一个名为"t"的列族
时序数据的metric、tagk、tagv三部分字符串都会被转成UID,这样再长的字符串在rowkey中也会由UID代替,大大缩短了rowkey的长度
rowkey中的时序数据的timestamp并非实际的时序数据时间,是格式化成以小时为单位的时间戳(所谓的base_time),也就是说该rowkey中的base_time表示的是该时序数据发生在哪个整点(小时)。每个数据写入的时候,会用该时序数据实际时间戳相对base_time的偏移量(offset)作为ColumnQualifier写入。
结合下面的图以及之后的代码,就一目了然。
salt+metric_uid+base_time+tagk1+tagv1+...+tagkN+tagvN 10 9 12 ... 8
rowkey的具体实现
在没有启用salt的情况下,我整理出来生成rowkey的代码如下(注意一下:源码中并没有这段代码哦):
public byte[] generateRowKey(String metricName, long timestamp, Map<String, String> tags) { // 获取metricUid byte[] metricUid = tsdb.getUID(UniqueId.UniqueIdType.METRIC, metricName); // 将时间戳转为秒 if ((timestamp & Const.SECOND_MASK) != 0L) { timestamp /= 1000L; } final long timestamp_offset = timestamp % Const.MAX_TIMESPAN;//3600 // 提取出时间戳所在的整点(小时)时间 final long basetime = timestamp - timestamp_offset; // 用TreeMap存储<tagkUid,tagvUid>, 排序用的是memcmp()方法,下面会有介绍 Map<byte[], byte[]> tagsUidMap = new org.hbase.async.Bytes.ByteMap<>(); tags.forEach((k, v) -> tagsUidMap.put( tsdb.getUID(UniqueId.UniqueIdType.TAGK, k), tsdb.getUID(UniqueId.UniqueIdType.TAGV, v))); // 不加salt的rowkey,metricUid+整点时间戳+所有的tagK、tagV byte[] rowkey = new byte[metricUid.length + Const.TIMESTAMP_BYTES + tags.size() * (TSDB.tagk_width() + TSDB.tagv_width())]; // 下面拷贝相应的数据到rowkey字节数组中的相应位置 System.arraycopy(metricUid, 0, rowkey, 0, metricUid.length); Bytes.setInt(rowkey, (int) basetime, metricUid.length); int startOffset = metricUid.length + Const.TIMESTAMP_BYTES; for (Map.Entry<byte[], byte[]> entry : tagsUidMap.entrySet()) { System.arraycopy(entry.getKey(), 0, rowkey, startOffset, TSDB.tagk_width()); startOffset += TSDB.tagk_width(); System.arraycopy(entry.getValue(), 0, rowkey, startOffset, TSDB.tagv_width()); startOffset += TSDB.tagv_width(); } return rowkey; }其中的ByteMap就是TreeMap,见org.hbase.async.Bytes.ByteMap
/** A convenient map keyed with a byte array. */ public static final class ByteMap<V> extends TreeMap<byte[], V> implements Iterable<Map.Entry<byte[], V>> { public ByteMap() { super(MEMCMP); } }多个tag的排序规则是对tag_id的bytes进行排序,调用的是org.hbase.async.Bytes#memcmp(final byte[] a, final byte[] b)方法,如下
/** * {@code memcmp} in Java, hooray. * @param a First non-{@code null} byte array to compare. * @param b Second non-{@code null} byte array to compare. * @return 0 if the two arrays are identical, otherwise the difference * between the first two different bytes, otherwise the different between * their lengths. */ public static int memcmp(final byte[] a, final byte[] b) { final int length = Math.min(a.length, b.length); if (a == b) { // Do this after accessing a.length and b.length return 0; // in order to NPE if either a or b is null. } for (int i = 0; i < length; i++) { if (a[i] != b[i]) { return (a[i] & 0xFF) - (b[i] & 0xFF); // "promote" to unsigned. } } return a.length - b.length; } 压缩(compaction)相关文档:
#compaction