听说过时序数据库吗?( 五 )

  • 获取指定ip的数据 。因为ip是做为tags保存的 。即便是访问一个ip的数据,也要把所有其他的ip数据读取出来再过滤掉 。如果ip总数有十多万个,那么查询的效率也会非常低 。为了让这样的查询变得更快,需要把ip编码到metric_name里去 。比如ip.10.0.0.1.Proc.load_avg.1m 这样 。
  • 所以结论是,不用索引是不行的 。如果希望支持任意条件的组合查询,只有主存储的排序是无法对所有查询条件进行优化的 。但是如果查询条件是固定的一种,那么可以像Opentsdb这样只有一个主存储,做针对性的优化 。
    DocValues为什么快?
    DocValues是一种按列组织的存储格式,这种存储方式降低了随机读的成本 。传统的按行存储是这样的:
    听说过时序数据库吗?

    文章插图
     
    1和2代表的是docid 。颜色代表的是不同的字段 。
    改成按列存储是这样的:
    听说过时序数据库吗?

    文章插图
     
    按列存储的话会把一个文件分成多个文件,每个列一个 。对于每个文件,都是按照docid排序的 。这样一来,只要知道docid,就可以计算出这个docid在这个文件里的偏移量 。也就是对于每个docid需要一次随机读操作 。
    那么这种排列是如何让随机读更快的呢?秘密在于Lucene底层读取文件的方式是基于memory mApped byte buffer的,也就是mmap 。这种文件访问的方式是由操作系统去缓存这个文件到内存里 。这样在内存足够的情况下,访问文件就相当于访问内存 。那么随机读操作也就不再是磁盘操作了,而是对内存的随机读 。
    那么为什么按行存储不能用mmap的方式呢?因为按行存储的方式一个文件里包含了很多列的数据,这个文件尺寸往往很大,超过了操作系统的文件缓存的大小 。而按列存储的方式把不同列分成了很多文件,可以只缓存用到的那些列,而不让很少使用的列数据浪费内存 。
    按列存储之后,一个列的数据和前面的posting list就差不多了 。很多应用在posting list上的压缩技术也可以应用到DocValues上 。这不但减少了文件尺寸,而且提高数据加载的速度 。因为我们知道从磁盘到内存的带宽是很小的,普通磁盘也就每秒100MB的读速度 。利用压缩,我们可以把数据以压缩的方式读取出来,然后在内存里再进行解压,从而获得比读取原始数据更高的效率 。
    如果内存不够是不是会使得随机读的速度变慢?肯定会的 。但是mmap是操作系统实现的API,其内部有预读取机制 。如果读取offset为100的文件位置,默认会把后面16k的文件内容都预读取出来都缓存在内存里 。因为DocValues是只读,而且顺序排序存储的 。相比b-tree等存储结构,在磁盘上没有空洞和碎片 。而随机读的时候也是按照DocId排序的 。所以如果读取的DocId是紧密相连的,实际上也相当于把随机读变成了顺序读了 。Random_read(100), Random_read(101), Random_read(102)就相当于Scan(100~102)了 。
    分布式计算
    分布式聚合如何做得快?Elasticsearch/Lucene从最底层就支持数据分片,查询的时候可以自动把不同分片的查询结果合并起来 。Elasticsearch的document都有一个uid,默认策略是按照uid 的 hash把文档进行分片 。
    听说过时序数据库吗?

    文章插图
     
    一个Elasticsearch Index相当于一个MySQL里的表,不同Index的数据是物理上隔离开来的 。Elasticsearch的Index会分成多个Shard存储,一部分Shard是Replica备份 。一个Shard是一份本地的存储(一个本地磁盘上的目录),也就是一个Lucene的Index 。不同的Shard可能会被分配到不同的主机节点上 。一个Lucene Index会存储很多的doc,为了好管理,Lucene把Index再拆成了Segment存储(子目录) 。Segment内的doc数量上限是1的31次方,这样doc id就只需要一个int就可以存储 。Segment对应了一些列文件存储索引(倒排表等)和主存储(DocValues等),这些文件内部又分为小的Block进行压缩 。
    听说过时序数据库吗?

    文章插图
     
    时间序列数据一般按照日期分成多个Elasticsearch Index来存储,比如logstash-2014.08.02 。查询的时候可以指定多个Elasticsearch Index作为查找的范围,也可以用logstash-*做模糊匹配 。
    美妙之处在于,虽然数据被拆得七零八落的,在查询聚合的时候甚至需要分为两个阶段完成 。但是对于最终用户来说,使用起来就好像是一个数据库表一样 。所有的合并查询的细节都是隐藏起来的 。


    推荐阅读