Influxdb源码解析-Store

前言

这是一个influxdb源码分析系列的文章,上一章分析了tsi index。tsi index是influxdb 实现的一种倒排索引,为了加速查询。对于index模块本身也做了一下持久化,包括Cache,WAL,SSTable等。这篇文章分析存储的核心结构:Store

Store的定位

Store结构在influxdb中是一个存储层的抽象。这是一个全局的概念,所有的database,retention policy,shard都会被一个store管理。具体的结构定义在了tsdb/store.go中。

// Store manages shards and indexes for databases.
type Store struct {
    mu                sync.RWMutex
    shards            map[uint64]*Shard
    databases         map[string]*databaseState
    sfiles            map[string]*SeriesFile
    SeriesFileMaxSize int64 // Determines size of series file mmap. Can be altered in tests.
    path              string
    indexes map[string]interface{}
    EngineOptions EngineOptions
    baseLogger *zap.Logger
    Logger     *zap.Logger
    opened  bool
}

有些字段有所简化。从这个结构可以看到一个store包含:

  • Shard的信息,map的key是shardId
  • database state
  • SeriesFile。map的key是database,value是这个database对应的seriesfile
  • index。map的key是database,value是index。这里默认是inmem的index
  • 其他

从这里可以看出来几个重要的信息

  • 在store中,database的概念被弱化了,所有的shard是平铺开的。
  • series file和index都是database粒度的,一个database中的所有结构会共享这些结构。

核心功能

Store由于是一个比较上层的抽象,是各个核心组件的组装。所以大部分功能会委托给底层的结构来实现。比如写入和查询,会委托给Shard的相关函数。这里看一下Store在启动的时候,加载所有数据的逻辑。具体的写入流程,在之前有分析过,不是很清楚的可以回去看一下。

New Store

func NewStore(path string) *Store {
	logger := zap.NewNop()
	return &Store{
		databases:           make(map[string]*databaseState),
		path:                path,
		sfiles:              make(map[string]*SeriesFile),
		indexes:             make(map[string]interface{}),
		pendingShardDeletes: make(map[uint64]struct{}),
		epochs:              make(map[uint64]*epochTracker),
		EngineOptions:       NewEngineOptions(),
		Logger:              logger,
		baseLogger:          logger,
	}
}

New 函数的逻辑很简单,初始化一些结构,主要是path。表示当前的根目录。

Open

func (s *Store) Open() error {
    s.mu.Lock()
    defer s.mu.Unlock()
    if s.opened {
        // Already open
        return nil
    }
    s.closing = make(chan struct{})
    s.shards = map[uint64]*Shard{}
    s.Logger.Info("Using data dir", zap.String("path", s.Path()))
    // Create directory.
    if err := os.MkdirAll(s.path, 0777); err != nil {
        return err
    }

    if err := s.loadShards(); err != nil {
        return err
    }
    s.opened = true

    return nil
}

Open函数的逻辑也不复杂,主要逻辑是LoadShards。

LoadShards

这个函数时Store的一个核心逻辑,负责在启动时初始化上述的结构。例如shard,series,index等。由于逻辑很长,这里做一下简化。

func (s *Store) loadShards() error {
    //  1.参数校验和并行度确认

    // 2 遍历所有数据
    // 2.1 遍历所有db 目录
    for _, db := range dbDirs {
        dbPath := filepath.Join(s.path, db.Name())
        // 2.2.1 open series file
        sfile, err := s.openSeriesFile(db.Name())
        // 2.2.2 create index
        idx, err := s.createIndexIfNotExists(db.Name())
         // 2.3 遍历所有rp
        rpDirs, err := ioutil.ReadDir(dbPath)
        for _, rp := range rpDirs {
            rpPath := filepath.Join(s.path, db.Name(), rp.Name())
            // The .series directory is not a retention policy.
            if rp.Name() == SeriesFileDirectory {
                continue
            }
            shardDirs, err := ioutil.ReadDir(rpPath)
            if err != nil {
                return err
            }
            // 2.4 遍历所有shard
            for _, sh := range shardDirs {
                // Series file should not be in a retention policy but skip just in case.
                if sh.Name() == SeriesFileDirectory {
                    log.Warn("Skipping series file in retention policy dir", zap.String("path", filepath.Join(s.path, db.Name(), rp.Name())))
                    continue
                }
                n++
                go func(db, rp, sh string) {
                    t.Take()
                    defer t.Release()

                    start := time.Now()
                    path := filepath.Join(s.path, db, rp, sh)
                    // Shard file names are numeric shardIDs
                    shardID, err := strconv.ParseUint(sh, 10, 64)

                    // Copy options and assign shared index.
                    opt := s.EngineOptions
                    opt.InmemIndex = idx
                    opt.SeriesIDSets = shardSet{store: s, db: db}
                    // 2.5 new shard
                    shard := NewShard(shardID, path, walPath, sfile, opt)
                }(db.Name(), rp.Name(), sh.Name())
            }
        }
    }
}

简化之后的逻辑如上,概括一下就是创建所有的相关的数据。逻辑并不复杂。

总结

这篇分析相对来说比较简单,因为Store结构有些核心的函数,大部分都是委托给它的依赖完成的,相当于是上层的代理。所以阅读完这篇文章,知道以下几个点就好:

  • store是存储和查询的总抽象,代理了所有shard,series,index等信息。
  • shard结构虽然从属于database+retention policy的,但是在store中是打平的。
  • series,index是database级别的概念,在整个database里面是共享的。

这就是本篇文章的主要内容,下一篇会分析一下Shard结构。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值