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.0K
drwxr-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_1759A6CADA292254
drwxr-xr-x 2 root root 98 5月 5 07:16 13868_28_1759A6CADA29209F
drwxr-xr-x 2 root root 98 5月 5 07:53 1574_3_1759A6CADA29220F
drwxr-xr-x 2 root root 98 5月 5 07:52 16467_45_1759A6CADA292207
2. 索引文件
每个part目录下,包含若干个索引文件:
# ls indexdb/1759A6CAD53ABA2F/1141_2_1759A6CADA292254/ -alh
总用量 36K
drwxr-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.go
func (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.go
func (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.go
func (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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。