InfluxDB数据压缩实践

在上述InfluxDB压缩算法的文章中,描述了具体使用的压缩算法,但单就这些算法并不能指导实践,为此本文针对不同的数据类型总结了压缩率,并给出了能提高压缩率的实际方法,写点姿势,希望能在实际生产中指导实践。

influx_inspect dumptsm xxx.tsm

上述命令是InfluxDB提供的一个inspect tsm文件的工具,运行上述命令会产生如下输出

Summary:
  File: /tmp/tsm1-test412750211/tsm1test039799558
  Time Range: 2017-04-12T02:57:16.000352Z - 2017-05-05T06:30:35.900035Z
  Duration: 555h33m19.899683s   Series: 1   File Size: 217812294

Statistics
  Blocks:
    Total: 1 Size: 217812245 Min: 217812241 Max: 217812241 Avg: 217812245
  Index:
    Total: 1 Size: 36
  Points:
    Total: 20000000
  Encoding:
    Timestamp:  none: 0 (0%)    s8b: 1 (100%) 
    Float:  none: 0 (0%)    gor: 1 (100%) 
  Compression:
    Per block: 10.89 bytes/point
    Total: 10.89 bytes/point

其中statistics包含重要信息,首先总共的点数是2000w,其次timestamp的压缩方法是simple8b(100%),float使用的gorilla(100%)的压缩方法,最后,平均每个点占用的空间是10.89bytes。

下面我们从不同的数据类型入手,探究多种写点的姿势对压缩率的影响。

bool类型的数据

策略一,时间戳乱序写入

values := make([]tsm1.Value, 0, 20000000)
    now := time.Now().Add(-time.Minute * 60 * 24 * 365).UnixNano()
    for i := 0; i < 20000000; i++ {
        values = append(values, tsm1.NewValue(now+rand.Int63n(10000000000), true))
    }
    if err := w.Write([]byte("cpu"), values); err != nil {
        fmt.Printf("unexpected error writing: %v", err)
        return
    }

inspect结果

  Encoding:
    Timestamp:  none: 1 (100%) 
    Bool:   none: 0 (0%)    bp: 1 (100%) 

磁盘文件大小:155MB

策略二, 时间戳等差数列写入

values := make([]tsm1.Value, 0, 20000000)
    now := time.Now().Add(-time.Minute * 60 * 24 * 365).UnixNano()
    for i := 0; i < 20000000; i++ {
        values = append(values, tsm1.NewValue(now+int64(i), true))
    }
    if err := w.Write([]byte("cpu"), values); err != nil {
        fmt.Printf("unexpected error writing: %v", err)
        return
    }

inspect结果

 Encoding:
    Timestamp:  none: 0 (0%)    s8b: 0 (0%)     rle: 1 (100%) 
    Bool:   none: 0 (0%)    bp: 1 (100%) 

磁盘文件大小:2.4MB

策略三,时间戳升序写入

values := make([]tsm1.Value, 0, 20000000)
    now := time.Now().Add(-time.Minute * 60 * 24 * 365).UnixNano()
    for i := 0; i < 20000000; i++ {
        values = append(values, tsm1.NewValue(now+int64(i)*1000000000000+rand.Int63n(1000000000), true))
    }
    if err := w.Write([]byte("cpu"), values); err != nil {
        fmt.Printf("unexpected error writing: %v", err)
        return
    }

inspect结果

  Encoding:
    Timestamp:  none: 0 (0%)    s8b: 1 (100%) 
    Bool:   none: 0 (0%)    bp: 1 (100%) 

磁盘上文件大小:105MB

结论

2000w个bool类型的数据实际上是2000w个bool类型的数据+2000w个int64的时间戳,bool类型数据压缩很固定,只有2.4MB,而且对压缩率基本没有影响;

而int64时间戳的才是真正的影响压缩率的大头,从上述策略中得出,时间戳等比数列的压缩率最高,其次是升序时间戳,最后是完全无序随机。

InfluxDB的主要使用场景是IoT时序数据,这些数据绝大部分都遵从时间升序的规律,甚至对于一些定时采样的数据,时间戳甚至是等比数列的,也就可以使这些数据有相当大的压缩率。

int类型的数据

上述分析,已经得出时间戳对数据的影响了,2000w个int类型的数据实际上是4000w个int64的数据

时间戳等比数列 +随机int数据

values := make([]tsm1.Value, 0, 20000000)
    now := time.Now().Add(-time.Minute * 60 * 24 * 365).UnixNano()
    for i := 0; i < 20000000; i++ {
        values = append(values, tsm1.NewValue(now+int64(i)*1000000000000, rand.Int63n(1000000000)))
    }
    if err := w.Write([]byte("cpu"), values); err != nil {
        fmt.Printf("unexpected error writing: %v", err)
        return
    }

inspect 结果

 Encoding:
    Timestamp:  none: 0 (0%)    s8b: 0 (0%)     rle: 1 (100%) 
    Int:    none: 0 (0%)    s8b: 1 (100%) 

磁盘文件大小:99MB

时间戳等比数列 +升序int数据

    values := make([]tsm1.Value, 0, 20000000)
    now := time.Now().Add(-time.Minute * 60 * 24 * 365).UnixNano()
    for i := 0; i < 20000000; i++ {
        values = append(values, tsm1.NewValue(now+int64(i)*1000000000000, int64(i)*1000000+rand.Int63n(1000000)))
    }
    if err := w.Write([]byte("cpu"), values); err != nil {
        fmt.Printf("unexpected error writing: %v", err)
        return
    }

inspect 结果

  Encoding:
    Timestamp:  none: 0 (0%)    s8b: 0 (0%)     rle: 1 (100%) 
    Int:    none: 0 (0%)    s8b: 1 (100%) 

磁盘文件大小:77MB

时间戳等比数列 + 整体升序,局部随机数据

values := make([]tsm1.Value, 0, 20000000)
    now := time.Now().Add(-time.Minute * 60 * 24 * 365).UnixNano()
    pos := 0
    for i := 0; i < 20000000; i++ {
        values = append(values, tsm1.NewValue(now+int64(i)*1000000000000, int64(pos/100000)*1000000+rand.Int63n(1000000)))
        pos++
    }
    if err := w.Write([]byte("cpu"), values); err != nil {
        fmt.Printf("unexpected error writing: %v", err)
        return
    }

inspect结果

  Encoding:
    Timestamp:  none: 0 (0%)    s8b: 0 (0%)     rle: 1 (100%) 
    Int:    none: 0 (0%)    s8b: 1 (100%) 

磁盘文件大小:63MB

结论

在上述策略中,我们排除了时间戳对数据压缩的影响,所有的时间戳都是递增的,在这种情况下,压缩的效率就完全取决于int数据了。

从三个策略看,int的压缩全部是s8b的压缩,而且数据是否有序这个因素影响并不是很显著。

float类型的数据

为了排除时间戳对压缩率的影响,我们同样采取int类型数据的三种策略

时间戳等比数列 +随机float数据

    values := make([]tsm1.Value, 0, 20000000)
    now := time.Now().Add(-time.Minute * 60 * 24 * 365).UnixNano()
    for i := 0; i < 20000000; i++ {
        values = append(values, tsm1.NewValue(now+int64(i)*1000000000000, rand.Float64()*100000))
    }
    if err := w.Write([]byte("cpu"), values); err != nil {
        fmt.Printf("unexpected error writing: %v", err)
        return
    }

inspect 结果:

  Encoding:
    Timestamp:  none: 0 (0%)    s8b: 0 (0%)     rle: 1 (100%) 
    Float:  none: 0 (0%)    gor: 1 (100%) 

磁盘文件大小: 157MB

时间戳等比数列 +升序float数据

    values := make([]tsm1.Value, 0, 20000000)
    now := time.Now().Add(-time.Minute * 60 * 24 * 365).UnixNano()
    for i := 0; i < 20000000; i++ {
        values = append(values, tsm1.NewValue(now+int64(i)*1000000000000, float64(i*1000000)))
    }
    if err := w.Write([]byte("cpu"), values); err != nil {
        fmt.Printf("unexpected error writing: %v", err)
        return
    }

inspect结果:和上述结果一样
磁盘文件大小:119MB

时间戳等比数列 + 整体升序,局部随机数据

    values := make([]tsm1.Value, 0, 20000000)
    now := time.Now().Add(-time.Minute * 60 * 24 * 365).UnixNano()
    pos := 0
    for i := 0; i < 20000000; i++ {
        values = append(values, tsm1.NewValue(now+int64(i)*1000000000000, float64(int64(pos/100000)*1000000)+rand.Float64()*1000000))
        pos++
    }
    if err := w.Write([]byte("cpu"), values); err != nil {
        fmt.Printf("unexpected error writing: %v", err)
        return
    }

inspect结果:和上面一样
磁盘文件大小:157MB

结论

float的压缩和int的压缩类似,所有的float压缩也全是用的gorrila的压缩方法,且和是否有序没有显著关系。

总结

首先,InfluxDB中需要压缩的数据由两部分构成,实际的数据(int,float,boo,string)和timestamp(int),且一个实际的数值对应一个实际的时间戳,时间戳的个数占比是高达50%的,所以时间戳的压缩比的高低直接关系着整体数据的压缩比的高低;timestamp的压缩方法是s8b的压缩方法,所以在时间戳有序的情况下,能有效提高压缩比。

其次,bool数据的压缩是恒定的,一个bool值占一个bit,无论什么情况下都是这样的;float使用的gorrila的压缩方法,也没有对数据顺序的要求。

最后可以看出InfluxDB对数据的压缩率很大情况下取决于数据的时间戳是否有序,越有序(升序)那么压缩率越高。

欢迎留言讨论~