InfluxDB meta文件分析

进度的起步和平息

水源执行c程序时,利用exec函数调用一个非正规的开行例程,该启动例程丛内核中获取命令行参数和条件变量值。

操作系统 : CentOS7.3.1611_x64

进度终止的景况

5种正常终止的境况:

(1)从main函数返回;
(2)调用exit;
(3)调用_exit和_Exit函数;
(4)最后一个线程调用pthread_exit;
(5)最后一个线程从其启动例程返回;  

3种非凡终止情状

(1)调用abort;
(2)接到一个信号;
(3)最后一个线程对取消请求做出响应;

go语言版本:1.8.3 linux/amd64

经过启动和平息图

图片 1

InfluxDB版本:1.1.0

atexit函数

一个进度最多可以注册32和函数(例如:signal函数),这几个函数由exit函数自动调用。在程序终止时调用那几个函数,形成终止处理程序,来展开扫尾进度前的收尾工作。而exit函数通过atexit函数的挂号记录来判定调用哪些函数。

influxdb默认配置:

exit函数

此函数由ISO C
定义,其操作包蕴处理终止处理程序,然后倒闭所有标准I/O流。必要专注的是,它不会处理公事描述符、多进度(父子进程)以及作业控制。

/etc/influxdb/influxdb.conf

_e(E)xit函数 ISO C 定义这几个函数的目标是为经过提供一种无需运行终止处理程序或信号处理函数的章程而终止程序。但ISO C 对标准I/O流是还是不是开展冲洗,那取决于操作系统的贯彻。在unix中,是不开展冲洗的。

meta默许配置:

exit和_e(E)ixt函数的状态码

不论进度如何甘休,它都会在基本上实施同一段代码(由进程启动和退出图可见)。那段代码来关闭所有的文书描述符,释放具有的贮存空间。

程序退出后,利用退出码告知该进程的父进度。父进度经过wait或waitpid函数来完结该子进度的善后工作(获取子进度有关音讯释放子进程占用资源)。若父进程没有处理子进程的脱离状态,则子进度变成僵死进度。相反的,若父进度在子进程前停下,则子进度变成孤儿进度。孤儿进度会由1号经过(init进度)接收,大概进度如下:

(1)进程终止时,内核逐个检查所有活动的进程;
(2)分析查找该终止进程的子进程;
(3)将该进程的子进程的父进程ID改为1;
[meta]
  dir = "/var/lib/influxdb/meta"
  retention-autocreate = true
  logging-enabled = true

wait和waitpid函数

程序正常或更加终止时,内核都会向父进度发送SIGNAL信号。子进程终止是异步事件,所以该信号也是异步信号。而该信号一般会被父过程默许忽略。或者提供一个信号处理函数来善后。wait和waitpid函数就是中间的信号处理函数的一片段。

wait和waitpid函数不相同如下:

(1)wait会阻塞调用者进程等待直至第一个终止的子进程到来;
(2)waitpid可以通过参数设置,来实现调用者进程不阻塞,或选择要阻
塞等待的子进程;

此间的调用者指的是父进度

  • dir

环境表和环境变量

meta数据存放目录,默许值:/var/lib/influxdb/meta

环境表结构图

图片 2

  • 各类程序都收到到一张环境表
  • 环境表也是一个字符指针数组
  • enrivon叫做环境指针
  • 指针数组叫做环境表
  • 逐一指针指向的字符串叫做环境字符串

meta数据文件默许路径:/var/lib/influxdb/meta/meta.db

环境变量

  • unix内核并不检查环境字符串,它们的分解完全在于各样应用进度
  • 日常在一个shell启动文件中安装环境变量来支配shell的动作
  • 修改或者增添环境变量时,只可以影响当下进度以及之后(之前的相当)生成和调用的任何子进度的环境,但不可以影响其父进程的条件

和环境变量相关的函数如下:

#include<stdlib.h>
char *getenv(const char *name);
      返回值:指向与name关联的value的指针;若未找到,返回NULL

int putenv(char *str);
                       返回值:若成功,返回0;若出错,返回非0

int setenv(const char *name, const char *value,
            int rewrite);
int unsetenv(const char *name);
                两个函数返回值:若成功,返回0;若出错,返回-1 
  • retention-autocreate

这个函数怎样修改环境表的

环境表和环境字符串平时存放在内存空间的高地址处(顶部)。所以在修改它的值时,内存是不可能继续向高地址延伸;但又因为,它之下是逐一栈帧,所以也不可以向下延长。如何修改它的值的历程如下:

(1)修改环境表

1)新value <= 旧value,直接覆盖旧value的存储空间
2)新value >= 旧value,调用malloc函数,在堆区开辟新的存储空间,
将新value复制到这里,再将这片存储区首地址写到环境表相应的位置处。

(2)新增环境表

1)新增一个环境变量,调用malloc函数开辟新的存储空间,将原来的环
境表复制到该存储区,其次再添加一个环境变量,然后在尾部赋值为NULL,
最后将environ指向该区域;
2)在 1)过程的基础上,调用realloc函数,多次添加环境变量;

注意:以那种办法修改的环境变量只在当下程序运行时有效,当程序截至时,相应的存储区被系统回收,那一个改动就会失灵。

用于控制默认存储策略,数据库创立时,会自动生成autogen的存储策略,默许值:true

内存存储结构补充表明

  • logging-enabled

内存管理结构图

图片 3

  • 未初步化数据段(block started by symbol):在程序初阶执
    行从前,内核将此段中的数据初阶化为0或空指针;
  • 栈:老是函数调用时,其回来地址以及调用者的环境音讯(如某些机器寄存器的值)都存放在栈中;
  • 共享库:只需在拥有进度都可援引的存储区中保留那种库例程的一个副本;

是不是开启meta日志,默许值:true

存储空间分配函数

#include<stdlib.h>
void *malloc(size_t size);
void *calloc(size_t nojy, size_t size);
void *realloc(void *ptr, size_t newsize);
         3个函数返回值:若成功,返回非空指针;若出错,返回NULL
  • malloc函数:初叶值不确定;底层通过调用sbrk函数落成;
  • calloc函数:开头值为0;
  • realloc函数:日增或调减在此从前分配区的尺寸;当伸张长度时,可能将之前分配区的内容移到另一个丰富大的区域,以便在分配区末尾扩大存储区,而新增存储区起先值不确定(例如:可变数组的选拔);

注意:那么些动态分配的函数一般在分配存储空间时,会比须求的大。因为在开发空间的前后部分存储记录管理音讯。因而,在接纳时,千万不要越界访问,以免造成不可预言的结局。

meta文件的dump和load

源码路径: github.com/influxdata/influxdb/services/meta/client.go

meta文件dump

// snapshot will save the current meta data to disk
func snapshot(path string, data *Data) error {
    file := filepath.Join(path, metaFile)
    tmpFile := file + "tmp"

    f, err := os.Create(tmpFile)
    if err != nil {
        return err
    }
    defer f.Close()

    var d []byte
    if b, err := data.MarshalBinary(); err != nil {
        return err
    } else {
        d = b
    }

    if _, err := f.Write(d); err != nil {
        return err
    }

    if err = f.Sync(); err != nil {
        return err
    }

    //close file handle before renaming to support Windows
    if err = f.Close(); err != nil {
        return err
    }

    return renameFile(tmpFile, file)
}

snapshot可以通过以下二种办法触发:

1、当执行 Client.Open 函数时会进行snapshot操作;

2、执行meta文件更新时经过commit函数进行snapshot操作;

在InfluxDB中程序中,通过 NewServer
函数创制MetaClient变量(meta.NewClient),然后实施MetaClient.Open()进行伊始化;

一连会透过Server.Open函数(run/server.go)启动各项服务,固然有meta文件的换代操作,通过commit函数举办snapshot操作;

meta文件load

// Load will save the current meta data from disk
func (c *Client) Load() error {
    file := filepath.Join(c.path, metaFile)

    f, err := os.Open(file)
    if err != nil {
        if os.IsNotExist(err) {
            return nil
        }
        return err
    }
    defer f.Close()

    data, err := ioutil.ReadAll(f)
    if err != nil {
        return err
    }

    if err := c.cacheData.UnmarshalBinary(data); err != nil {
        return err
    }
    return nil
}

Client.Open()中会执行Load操作,NewServer时会自动加载。

函数间跳转策略

在c语言中,goto语句是不可以跨函数跳转的。越发是在函数深层调用时的跳转要求,在阴差阳错处理的情景下越发实惠。

#include<setjmp.h>
int setjmp(jmp_buf env);
          返回值:若直接调用,返回0;若从longjmp返回,返回非0
void longjmp(jmp_buf env, int val);

变量值回滚问题:自行变量和寄存器变量会存在回滚现象。利用volatile属性来防止此类情状的暴发。(在给变量赋值时,赋的值回首先存储在内存(存储器变量)中,然后在由cpu取走,存储在cpu的寄存器上(寄存器变量)。在做系统优化时,那个频仍使用的变量,会直接存储到寄存器中而不经过内存。)

meta文件内容编解码

源码路径: github.com/influxdata/influxdb/services/meta/data.go

meta数据encode:

// MarshalBinary encodes the metadata to a binary format.
func (data *Data) MarshalBinary() ([]byte, error) {
    return proto.Marshal(data.marshal())
}

meta数据decode:

// UnmarshalBinary decodes the object from a binary format.
func (data *Data) UnmarshalBinary(buf []byte) error {
    var pb internal.Data
    if err := proto.Unmarshal(buf, &pb); err != nil {
        return err
    }
    data.unmarshal(&pb)
    return nil
}

proto路径 :github.com/gogo/protobuf/proto

寄存器变量会设有回滚现象的商讨

在调用setjmp函数时,内核会把近来的栈顶指针保存在env变量中,所以在调用longjmp函数重临该地方时,全局变量、静态变量、易失变量和自行变量即使在调用setjmp和longjmp函数之间它们的值被改动过,是不会回滚到setjmp函数调用从前的值(当然,编译器将auto变量优化为寄存器变量除外)。因为,那个存储器变量的值是储存在内存相应的段中,回到原先栈顶状态时,同样访问的仍然本来的内存空间。

但是,对于寄存器变量来说,首先要强烈一点:寄存器变量是用动态储存的方法。意思是寄存器变量的值可能存在不一致的寄存器中。借使在调setjmp和longjmp函数之间它们的值被涂改过,那么些值可能不会存到setjmp从前的对其赋值的寄存器中,而在调用longjmp函数后,又赶回了调用setjmp函数时的图景。这一个时候再读取寄存器变量的值时,读到的是原先那多少个寄存器中蕴藏的值而不是修改过的更加寄存器中储存的值,所以现身的回滚现象。

meta文件结构定义

源码路径: github.com/influxdata/influxdb/services/meta/data.go

meta文件存储的就是 meta.Data 的数码,结构定义如下:

// Data represents the top level collection of all metadata.
type Data struct {
    Term      uint64 // associated raft term
    Index     uint64 // associated raft index
    ClusterID uint64
    Databases []DatabaseInfo
    Users     []UserInfo

    MaxShardGroupID uint64
    MaxShardID      uint64
}

Term :暂时不明了为啥用的。

Index
:从源码看那几个相应是近乎版本号的事物,开首化为1,执行commit操作是会大增。假如为1,会立马施行持久化操作(在Open函数中操作)。

ClusterID : 是InfluxDB集群相关内容;

Databases :用于存储数据库音信;

Users :用于存储数据库用户音讯;

DatabaseInfo 定义 :

// DatabaseInfo represents information about a database in the system.
type DatabaseInfo struct {
    Name                   string
    DefaultRetentionPolicy string
    RetentionPolicies      []RetentionPolicyInfo
    ContinuousQueries      []ContinuousQueryInfo
}

RetentionPolicyInfo 定义:

// RetentionPolicyInfo represents metadata about a retention policy.
type RetentionPolicyInfo struct {
    Name               string
    ReplicaN           int
    Duration           time.Duration
    ShardGroupDuration time.Duration
    ShardGroups        []ShardGroupInfo
    Subscriptions      []SubscriptionInfo
}

ShardGroupInfo 定义:

// ShardGroupInfo represents metadata about a shard group. The DeletedAt field is important
// because it makes it clear that a ShardGroup has been marked as deleted, and allow the system
// to be sure that a ShardGroup is not simply missing. If the DeletedAt is set, the system can
// safely delete any associated shards.
type ShardGroupInfo struct {
    ID          uint64
    StartTime   time.Time
    EndTime     time.Time
    DeletedAt   time.Time
    Shards      []ShardInfo
    TruncatedAt time.Time
}

ShardInfo 定义:

// ShardInfo represents metadata about a shard.
type ShardInfo struct {
    ID     uint64
    Owners []ShardOwner
}

ShardOwner 定义:

// ShardOwner represents a node that owns a shard.
type ShardOwner struct {
    NodeID uint64
}

ShardOwner紧要用以集群,其中NodeId用于标识集群的节点ID,在InfluxDB
1.1社区版本中集群已经不帮忙了,该字段无效。

SubscriptionInfo 定义:

// SubscriptionInfo hold the subscription information
type SubscriptionInfo struct {
    Name         string
    Mode         string
    Destinations []string
}

ContinuousQueryInfo 定义:

// ContinuousQueryInfo represents metadata about a continuous query.
type ContinuousQueryInfo struct {
    Name  string
    Query string
}

UserInfo 定义:

// UserInfo represents metadata about a user in the system.
type UserInfo struct {
    Name       string
    Hash       string
    Admin      bool
    Privileges map[string]influxql.Privilege
}

其它

meta文件分析示例代码:

package main

import (
    "os"
    "fmt"
    "io/ioutil"
    "github.com/influxdata/influxdb/services/meta"
)

func Load(metaFile string) error {
    cacheData:= &meta.Data{
            Index: 1,
        }
    //file := filepath.Join(c.path, metaFile)

    f, err := os.Open(metaFile)
    if err != nil {
        if os.IsNotExist(err) {
            return nil
        }
        return err
    }
    defer f.Close()

    data, err := ioutil.ReadAll(f)
    if err != nil {
        return err
    }

    if err := cacheData.UnmarshalBinary(data); err != nil {
        return err
    }
    //fmt.Println(data)
    //fmt.Println("=======================")

    fmt.Println("Term       :",cacheData.Term)
    fmt.Println("Index      :",cacheData.Index)
    fmt.Println("Databases :")
    //fmt.Println(cacheData.Databases)

    for k,dbInfo := range cacheData.Databases {
        //fmt.Println(k,dbInfo)
        fmt.Println("k =",k)
        fmt.Println(dbInfo.Name,dbInfo.DefaultRetentionPolicy)
        for _,rPolicy := range dbInfo.RetentionPolicies {
            //fmt.Println(rPolicy)
            fmt.Println(rPolicy.Name,rPolicy.ReplicaN,rPolicy.Duration,rPolicy.ShardGroupDuration)
            fmt.Println("-------------ShardGroups---------------")
            //fmt.Println(rPolicy.ShardGroups)
            for shardIdx,shardGroup := range rPolicy.ShardGroups {
                //fmt.Println(shardGroup)
                fmt.Println("shardIdx =",shardIdx)
                fmt.Println("ID          :",shardGroup.ID)
                fmt.Println("StartTime   :",shardGroup.StartTime)
                fmt.Println("EndTime     :",shardGroup.EndTime)
                fmt.Println("DeletedAt   :",shardGroup.DeletedAt)
                //fmt.Println("Shards      :",shardGroup.Shards)
                fmt.Printf("Shards      :")
                for _,shard := range shardGroup.Shards {
                    fmt.Println(shard.ID,shard.Owners)
                }

                fmt.Println("TruncatedAt :",shardGroup.TruncatedAt)
                //fmt.Println(shardGroup.ID,shardGroup.StartTime,shardGroup.EndTime)
                // DeletedAt,Shards  ,      TruncatedAt
            }
            //fmt.Println(rPolicy.Subscriptions)
            fmt.Println("--------------Subscriptions----------------")
            for subsIdx,subInfo := range rPolicy.Subscriptions {
                //fmt.Println(subInfo)
                fmt.Println("subsIdx =",subsIdx)
                fmt.Println("Name :",subInfo.Name)
                fmt.Println("Mode :",subInfo.Mode)
                fmt.Println("Destinations :",subInfo.Destinations)
            }

        }
        fmt.Println("=======================")
    }

    fmt.Println("Users :")
    fmt.Println(cacheData.Users)
    fmt.Println(cacheData.MaxShardGroupID)
    fmt.Println(cacheData.MaxShardID)
    return nil
}

func main() {
    argsWithProg := os.Args
    if(len(argsWithProg) < 2) {
        fmt.Println("usage : ",argsWithProg[0]," configFile")
        return
    }
    metaFile := os.Args[1]

    fmt.Println(argsWithProg)
    fmt.Println(metaFile)

    Load(metaFile)
}

好,就那些了,希望对您有赞助。

本文github地址:

https://github.com/mike-zhang/mikeBlogEssays/blob/master/2018/20180112\_InfluxDB\_meta文件解析.rst

迎接补充

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图