opentsdb探索之路——部分设计与实现 (6)

对上面的描述解释一下:
tsd.storage.salt.width:rowkey加多少个byte前缀(默认0(即不开启),如果启用的话 建议1)
tsd.storage.salt.buckets:分桶数(默认20,建议根据region servers数确定)

写入的时候如果启用了salt,则根据metric_uid+所有[tagK+tagV]uid组成的byte数组,计算hashcode值,对分桶数取模,得出salt位

RowKey#prefixKeyWithSalt(注意:取的是关键代码,去除了干扰信息)

public static void prefixKeyWithSalt(final byte[] row_key) { // tsd.storage.salt.width if (Const.SALT_WIDTH() > 0) { final int tags_start = Const.SALT_WIDTH() + TSDB.metrics_width() + Const.TIMESTAMP_BYTES; // we want the metric and tags, not the timestamp final byte[] salt_base = new byte[row_key.length - Const.SALT_WIDTH() - Const.TIMESTAMP_BYTES]; System.arraycopy(row_key, Const.SALT_WIDTH(), salt_base, 0, TSDB.metrics_width()); System.arraycopy(row_key, tags_start,salt_base, TSDB.metrics_width(), row_key.length - tags_start); // 这里通过对salt_buckets取模得出salt位的数值 int modulo = Arrays.hashCode(salt_base) % Const.SALT_BUCKETS();// tsd.storage.salt.buckets final byte[] salt = getSaltBytes(modulo); // 填充salt位的byte System.arraycopy(salt, 0, row_key, 0, Const.SALT_WIDTH()); } }

这个时候大多数人就会疑惑了,在rowkey前加了salt位,那么查询的时候怎么搞?
客户端查询OpenTSDB一条数据,OpenTSDB将这个请求拆成分桶数个查询到HBase,然后返回桶数个结果集到OpenTSDB层做合并。对HBase并发请求相应的也会桶数倍的扩大。见TsdbQuery#findSpans()

if (Const.SALT_WIDTH() > 0) { final List<Scanner> scanners = new ArrayList<Scanner>(Const.SALT_BUCKETS()); for (int i = 0; i < Const.SALT_BUCKETS(); i++) { // 构建出等于分桶数大小个scanner scanners.add(getScanner(i)); } scan_start_time = DateTime.nanoTime(); return new SaltScanner(tsdb, metric, scanners, spans, scanner_filters, delete, rollup_query, query_stats, query_index, null, max_bytes, max_data_points).scan(); }

在每一个scanner的rowkey前面填充bucket index作为salt位,这样才能去hbase中scan到完整的结果,见QueryUtil#getMetricScanner()

public static Scanner getMetricScanner(final TSDB tsdb, final int salt_bucket, final byte[] metric, final int start, final int stop, final byte[] table, final byte[] family) { if (Const.SALT_WIDTH() > 0) { final byte[] salt = RowKey.getSaltBytes(salt_bucket); // 这里把salt_bucket填充到rowkey中 System.arraycopy(salt, 0, start_row, 0, Const.SALT_WIDTH()); System.arraycopy(salt, 0, end_row, 0, Const.SALT_WIDTH()); } return scanner; } 其他配置(Configuration)

相关文档:

opentsdb的配置:

AsyncHBase client的配置:

opentsdb使用的hbase client是

public TSDB(final HBaseClient client, final Config config) { this.config = config; if (client == null) { final org.hbase.async.Config async_config; if (config.configLocation() != null && !config.configLocation().isEmpty()) { try { // AsyncHBase client读取和opentsdb一样的文件 // 所以 有一些需要设置AsyncHBase client的地方直接写在opentsdb的配置文件就能生效 async_config = new org.hbase.async.Config(config.configLocation()); } catch (final IOException e) { throw new RuntimeException("Failed to read the config file: " + config.configLocation(), e); } } else { async_config = new org.hbase.async.Config(); } async_config.overrideConfig("hbase.zookeeper.znode.parent", config.getString("tsd.storage.hbase.zk_basedir")); async_config.overrideConfig("hbase.zookeeper.quorum", config.getString("tsd.storage.hbase.zk_quorum")); this.client = new HBaseClient(async_config); } else { this.client = client; } }

性能优化的一方面可能与参数调优有关,有些与启动参数,操作系统设置等有关,有些参数就是写在配置文件的(比如说最大连接数、超时时间等等)

这里提一下前面没有讲到的与opentsdb相关的两个配置。

tsd.query.skip_unresolved_tagvs:默认为false,查询的时候遇到不存在的tagv时候是否跳过,true则跳过,false则抛出异常,个人感觉这个默认false极不友好。TagVFilter#resolveTags()#TagVErrback

/** * Allows the filter to avoid killing the entire query when we can't resolve * a tag value to a UID. */ class TagVErrback implements Callback<byte[], Exception> { @Override public byte[] call(final Exception e) throws Exception { if (config.getBoolean("tsd.query.skip_unresolved_tagvs")) { LOG.warn("Query tag value not found: " + e.getMessage()); return null; } else { // 默认情况下直接抛出异常 throw e; } } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsfffd.html