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