Influxdb的Meta data分析

piny發表於2021-09-09

Meta分析

一圖抵千言

圖片描述

metadata.png

按上圖,我們從右到從逐一簡單介紹一下
ShardInfo
  1. 定義了一個Shard的id和它位於哪個data node上;

ShardGroupInfo
  1. 封裝了ShardGroup的相關資訊

  2. Influxdb是按時間寫入資料的,每個DB都有自己的Retention Policy(這個我們後面會介紹),這個Retention Policy規定了每兩個ShardGroup之間的時間跨度ShardGroup Duration, 即每過一個ShardGrup Duration就會生產切換到下一個新的ShardGroup;

  3. 這個ShardGroupInfo就記錄了當前這個ShardGroup的相關資訊,比較主要的資訊有:
    3.1 StartTime: 這個Group裡最早的時間
    3.2 EndTime: 這個Group裡最晚的時間
    3.3 根據上面的兩個時間,我們就可以按時間和時間範圍來查詢到相應的ShardGroup;
    3.4 Shards      []ShardInfo: 這個ShardGroup包含的所有Shard,對於同一個ShardGroup,按Series key(Point key)不同雜湊寫到不同的Shard中;

RetentionPolicyInfo
  1. 封裝了Retention Policy: 包括了複本個數,資料保留時長,ShardGroup切分時長和當前節點的所有ShardGroup資訊

  2. 定義了按時間和時間範圍查詢相應SahrdGroup的方法

DatabaseInfo
  1. 管理 RetentionPoliciesContinuousQueries

UserInfo
  1. 封裝了使用者資訊:使用者名稱,密碼,對db的操作許可權

總結
  1. 上面介紹的每個物件基礎都提供了對其管理的下層metadata資訊的增,刪,查的方法;

Meta Client

定義
  1. 定義在services/meta/client.go中,負責所有和meta data有關的操作和請求處理

type Client struct {
    logger *zap.Logger

    mu        sync.RWMutex
    closing   chan struct{}
    changed   chan struct{}
    cacheData *Data    // Authentication cache.
    authCache map[string]authUser

    path string

    retentionAutoCreate bool}

主要就是操作上面介紹過的cacheData *Data;

  1. 提供了大量的方法,基出上都是對上述Data型別包含的meta資訊的增,刪,查,改操作

主要方法介紹
  1. snapshot方法:將meta資料寫入磁碟,所有的meta資訊都有對應的protocol buffer結構,依賴protocol buffer作序列化和反序列化:

func snapshot(path string, data *Data) error {
    filename := filepath.Join(path, metaFile)
    tmpFile := filename + "tmp"

    f, err := os.Create(tmpFile)

    defer f.Close()

    var d []byte    //利用protocol buffer作二進位制的序列化
    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 file.RenameFile(tmpFile, filename)
}
  1. Load方法:meta資料是會儲存到磁碟的,influxdb啟動時也會從磁碟上讀取:

func (c *Client) Load() error {
    file := filepath.Join(c.path, metaFile)

    f, err := os.Open(file)
    defer f.Close()

    data, err := ioutil.ReadAll(f)    //利用protocol buffer作反序列化
    if err := c.cacheData.UnmarshalBinary(data); err != nil {        return err
    }    return nil}
  1. commit方法:influxdb執行時,所有的meta資訊在記憶體裡都快取一分,當meta資訊有改動時,透過此方法立即寫入磁碟,同時更新記憶體裡的快取

func (c *Client) commit(data *Data) error {
    data.Index++    // try to write to disk before updating in memory
    if err := snapshot(c.path, data); err != nil {        return err
    }    // update in memory
    c.cacheData = data    // close channels to signal changes
    close(c.changed)
    c.changed = make(chan struct{})    return nil}
  1. ShardGroupsByTimeRangeShardsByTimeRange:按給定的時間查詢已有的ShardGroup和Shard

func (c *Client) ShardGroupsByTimeRange(database, policy string, min, max time.Time) (a []ShardGroupInfo, err error) {
    ...    // 先找到RetentionPolicyInfo
    rpi, err := c.cacheData.RetentionPolicy(database, policy)    if err != nil {        return nil, err
    } else if rpi == nil {        return nil, influxdb.ErrRetentionPolicyNotFound(policy)
    }
    groups := make([]ShardGroupInfo, 0, len(rpi.ShardGroups))    
    //遍歷RPI中的所有ShardGroup
    for _, g := range rpi.ShardGroups {        if g.Deleted() || !g.Overlaps(min, max) {            continue
        }
        groups = append(groups, g)
    }    return groups, nil}
  1. PrecreateShardGroups: 預先建立ShardGroup, 避免在相應時間段資料到達時才建立ShardGroup

func (c *Client) PrecreateShardGroups(from, to time.Time) error {
    c.mu.Lock()
    defer c.mu.Unlock()
    data := c.cacheData.Clone()
    var changed bool

    // 遍歷所有的DatabaseInfo資訊
    for _, di := range data.Databases {        for _, rp := range di.RetentionPolicies {            if len(rp.ShardGroups) == 0 {                // No data was ever written to this group, or all groups have been deleted.
                continue
            }            
            // ShardGroups中的所有ShardGroup已經是按時間排序好的,最後一個也就是最新的一個ShardGroup
            g := rp.ShardGroups[len(rp.ShardGroups)-1] // Get the last group in time.
            
            // 
            if !g.Deleted() && g.EndTime.Before(to) && g.EndTime.After(from) {                // Group is not deleted, will end before the future time, but is still yet to expire.
                // This last check is important, so the system doesn't create shards groups wholly
                // in the past.

                // Create successive shard group.
                // 計算出需要建立的ShardGroup的開始時間
                nextShardGroupTime := g.EndTime.Add(1 * time.Nanosecond)                // if it already exists, continue
                if sg, _ := data.ShardGroupByTimestamp(di.Name, rp.Name, nextShardGroupTime); sg != nil {                    continue
                }
                newGroup, err := createShardGroup(data, di.Name, rp.Name, nextShardGroupTime)                if err != nil {                    continue
                }
                changed = true
            }
        }
    }    if changed {        if err := c.commit(data); err != nil {            return err
        }
    }    return nil}

Influxdb定義了一個Service:Precreator Serivec(services/precreator/service.go),實現比較簡單,週期性的呼叫PrecreateShardGroups,看是否需要建立ShardGroup

func (s *Service) runPrecreation() {
    defer s.wg.Done()    for {
        select {        case <-time.After(s.checkInterval):            if err := s.precreate(time.Now().UTC()); err != nil {
                s.Logger.Info("Failed to precreate shards", zap.Error(err))
            }        case <-s.done:
            s.Logger.Info("Terminating precreation service")            return
        }
    }
}

         

             




作者:掃帚的影子
連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/3034/viewspace-2817413/,如需轉載,請註明出處,否則將追究法律責任。

相關文章