前言
- influxdb安装和使用
- influxdb概念详解1
- influxdb概念详解2
- influxdb源码编译
- influxdb启动分析
- influxdb源码分析-meta部分
- infludb源码分析-数据写入
- influxdb数据写入细节
- influxdb源码解析-series
- influxdb源码解析-inmem index
- influxdb源码解析-tsi index
这是一个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结构。