VictoriaMetrics的索引文件,保留在{storagePath}/indexdb目录下:

# tree ./indexdb/ -L 1./indexdb/├── 1759A6CAD53ABA2E├── 1759A6CAD53ABA2F└── snapshots

一. 基本概念

indexdb目录下,通常有2个索引目录:

  • 一个为pre,即上个retentionPeriod应用的索引;
  • 一个为cur,即以后retentionPeriod应用的索引;

当retentionPeriod到期后,将删掉pre,将cur配置为pre,同时创立新的索引目录作为cur;
保留2个目录后,能够避免retentionPeriod到期后,没有index可用的景象。

pre与cur的索引目录,在代码中被称为table

1. part

table目录下,蕴含若干个子目录:

  • 每个子目录,被称为一个part
# ls indexdb/1759A6CAD53ABA2F -alh总用量 4.0Kdrwxr-xr-x 28 root root 4.0K 5月   5 09:01 .drwxr-xr-x  5 root root   71 4月  27 09:35 ..drwxr-xr-x  2 root root   98 5月   5 08:00 1141_2_1759A6CADA292254drwxr-xr-x  2 root root   98 5月   5 07:16 13868_28_1759A6CADA29209Fdrwxr-xr-x  2 root root   98 5月   5 07:53 1574_3_1759A6CADA29220Fdrwxr-xr-x  2 root root   98 5月   5 07:52 16467_45_1759A6CADA292207

2. 索引文件

每个part目录下,蕴含若干个索引文件:

# ls indexdb/1759A6CAD53ABA2F/1141_2_1759A6CADA292254/ -alh总用量 36Kdrwxr-xr-x  2 root root   98 5月   5 08:00 .drwxr-xr-x 28 root root 4.0K 5月   5 09:01 ..-rw-r--r--  1 root root  149 5月   5 08:00 index.bin-rw-r--r--  1 root root  15K 5月   5 08:00 items.bin-rw-r--r--  1 root root 2.3K 5月   5 08:00 lens.bin-rw-r--r--  1 root root  335 5月   5 08:00 metadata.json-rw-r--r--  1 root root   48 5月   5 08:00 metaindex.bin

其中:

  • metaindex.bin:

    • 保留[]metaindexRow,用于索引index.bin;
    • 文件内容会被加载到内存,以减速查问;
  • index.bin:

    • 保留[]indexBlock,用于索引items.bin和lens.bin;
  • items.bin:

    • 保留index items的内容;
    • index item即各种索引项及其索引内容;
  • lens.bin:

    • 保留items的len信息;

二. 索引文件中的Item

items.bin文件保留索引的item内容,item用以形容索引数据,以KV构造存储,共有7种item类型:

const (    // Prefix for MetricName->TSID entries.    nsPrefixMetricNameToTSID = 0    // Prefix for Tag->MetricID entries.    nsPrefixTagToMetricIDs = 1    // Prefix for MetricID->TSID entries.    nsPrefixMetricIDToTSID = 2    // Prefix for MetricID->MetricName entries.    nsPrefixMetricIDToMetricName = 3    // Prefix for deleted MetricID entries.    nsPrefixDeletedMetricID = 4    // Prefix for Date->MetricID entries.    nsPrefixDateToMetricID = 5    // Prefix for (Date,Tag)->MetricID entries.    nsPrefixDateTagToMetricIDs = 6)

items的创立代码:

func (is *indexSearch) createGlobalIndexes(tsid *TSID, mn *MetricName) error {    // The order of index items is important.    // It guarantees index consistency.    ii := getIndexItems()            // type indexItems    defer putIndexItems(ii)    // Create MetricName -> TSID index.    ii.B = append(ii.B, nsPrefixMetricNameToTSID)    ii.B = mn.Marshal(ii.B)    ii.B = append(ii.B, kvSeparatorChar)    ii.B = tsid.Marshal(ii.B)    ii.Next()    // Create MetricID -> MetricName index.    ii.B = marshalCommonPrefix(ii.B, nsPrefixMetricIDToMetricName, mn.AccountID, mn.ProjectID)    ii.B = encoding.MarshalUint64(ii.B, tsid.MetricID)    ii.B = mn.Marshal(ii.B)    ii.Next()    // Create MetricID -> TSID index.    ii.B = marshalCommonPrefix(ii.B, nsPrefixMetricIDToTSID, mn.AccountID, mn.ProjectID)    ii.B = encoding.MarshalUint64(ii.B, tsid.MetricID)    ii.B = tsid.Marshal(ii.B)    ii.Next()    prefix := kbPool.Get()    prefix.B = marshalCommonPrefix(prefix.B[:0], nsPrefixTagToMetricIDs, mn.AccountID, mn.ProjectID)    ii.registerTagIndexes(prefix.B, mn, tsid.MetricID)    kbPool.Put(prefix)    return is.db.tb.AddItems(ii.Items)}

三. table目录的轮转

indexdb目录下有2个table目录,一个是pre,一个是cur。

这两个目录在VictoriaMetrics启动时,被主动创立进去。

// lib/storage/storage.gofunc (s *Storage) openIndexDBTables(path string) (curr, prev *indexDB, err error) {    ...    var tableNames []string    for _, fi := range fis {        if !fs.IsDirOrSymlink(fi) {            continue        }        tableName := fi.Name()        if !indexDBTableNameRegexp.MatchString(tableName) {            // Skip invalid directories.            continue        }        tableNames = append(tableNames, tableName)    }    sort.Slice(tableNames, func(i, j int) bool {        return tableNames[i] < tableNames[j]    })    // 若目录数量 < 2    if len(tableNames) < 2 {        // 没有目录,创立1个        if len(tableNames) == 0 {            prevName := nextIndexDBTableName()            tableNames = append(tableNames, prevName)        }        // 再创立1个        currName := nextIndexDBTableName()        tableNames = append(tableNames, currName)    }    ...    currPath := path + "/" + tableNames[len(tableNames)-1]    curr, err = openIndexDB(currPath, s, 0, &s.isReadOnly)    ...    prevPath := path + "/" + tableNames[len(tableNames)-2]    prev, err = openIndexDB(prevPath, s, 0, &s.isReadOnly)    ...    return curr, prev, nil}

当达到retentionPeriod时,产生目录的轮转,cur变成pre,创立新目录变成cur。

VictoriaMetrics的代码中,应用一个定时器实现:

// lib/storage/storage.gofunc (s *Storage) retentionWatcher() {    for {        d := nextRetentionDuration(s.retentionMsecs)    // retentionPeriod        select {        case <-s.stop:            return        case <-time.After(d):            s.mustRotateIndexDB()        }    }}

轮转的逻辑:

  • 首先,创立新的indexdb;
  • 而后,标记删掉老的indexdb;
  • 最初,配置cur=新的indexdb,同时exDB=老的indexdb;
// lib/storage/storage.gofunc (s *Storage) mustRotateIndexDB() {    // 1.创立新的    newTableName := nextIndexDBTableName()    idbNewPath := s.path + "/indexdb/" + newTableName    rotationTimestamp := fasttime.UnixTimestamp()    idbNew, err := openIndexDB(idbNewPath, s, rotationTimestamp, &s.isReadOnly)        // 2.删掉老的    idbCurr := s.idb()    idbCurr.doExtDB(func(extDB *indexDB) {        extDB.scheduleToDrop()    })    idbCurr.SetExtDB(nil)    // 3.应用新的    idbNew.SetExtDB(idbCurr)    s.idbCurr.Store(idbNew)    // Persist changes on the file system.    fs.MustSyncPath(s.path)    ...}

参考:

1.https://zhuanlan.zhihu.com/p/368912946
2.https://mp.weixin.qq.com/s?__biz=MzU4MjQ0MTU4Ng==&mid=2247499...
3.https://mp.weixin.qq.com/s/fa_3TJuZ-p2uzjUvqAwung