victoriametrics搜索indexDB时的顺序
整体
1、先搜索cache,
2、然后搜索当前DB
3、最后是搜搜extDB(也就是上一个数据保留周期的db)
为什么要设计extDB(也就是上一个数据保留周期的db),而不是只有DB
extDB是为了防止当数据过期的时候,或者轮转的时候,如果没有extDB作为过渡,那么对于此时对老metrics的新datapoint都会变成新时序,出现大量的index missing,导致性能大降
源码
func (db *indexDB) getTSIDsFromMetricIDs(qt *querytracer.Tracer, accountID, projectID uint32, metricIDs []uint64, deadline uint64) ([]TSID, error) {
qt = qt.NewChild("obtain tsids from %d metricIDs", len(metricIDs))
defer qt.Done()
if len(metricIDs) == 0 {
return nil, nil
}
tsids := make([]TSID, len(metricIDs))
var extMetricIDs []uint64
i := 0
err := func() error {
is := db.getIndexSearch(accountID, projectID, deadline)
defer db.putIndexSearch(is)
for loopsPaceLimiter, metricID := range metricIDs {
if loopsPaceLimiter&paceLimiterSlowIterationsMask == 0 {
if err := checkSearchDeadlineAndPace(is.deadline); err != nil {
return err
}
}
// Try obtaining TSIDs from MetricID->TSID cache. This is much faster
// than scanning the mergeset if it contains a lot of metricIDs.
tsid := &tsids[i]
// 1、优先搜索cache
err := is.db.getFromMetricIDCache(tsid, metricID)
if err == nil {
// Fast path - the tsid for metricID is found in cache.
i++
continue
}
if err != io.EOF {
return err
}
// 2、搜索当前indexDB
if err := is.getTSIDByMetricID(tsid, metricID); err != nil {
if err == io.EOF {
// Postpone searching for the metricID in the extDB.
extMetricIDs = append(extMetricIDs, metricID)
continue
}
return fmt.Errorf("cannot find tsid %d out of %d for metricID %d: %w", i, len(metricIDs), metricID, err)
}
// 填充cache
is.db.putToMetricIDCache(metricID, tsid)
i++
}
return nil
}()
if err != nil {
return nil, fmt.Errorf("error when searching for TISDs by metricIDs in the current indexdb: %w", err)
}
tsidsFound := i
qt.Printf("found %d tsids for %d metricIDs in the current indexdb", tsidsFound, len(metricIDs))
// Search for extMetricIDs in the extDB.
// 3、搜索extDB(也就是上一个数据保留周期的db)
// 之所以有extDB是为了防止当数据过期的时候,出现大量的index missing,导致性能大降
db.doExtDB(func(extDB *indexDB) {
is := extDB.getIndexSearch(accountID, projectID, deadline)
defer extDB.putIndexSearch(is)
for loopsPaceLimiter, metricID := range extMetricIDs {
if loopsPaceLimiter&paceLimiterSlowIterationsMask == 0 {
if err = checkSearchDeadlineAndPace(is.deadline); err != nil {
return
}
}
// There is no need in searching for TSIDs in MetricID->TSID cache, since
// this has been already done in the loop above (the MetricID->TSID cache is global).
tsid := &tsids[i]
if err = is.getTSIDByMetricID(tsid, metricID); err != nil {
if err == io.EOF {
// Cannot find TSID for the given metricID.
// This may be the case on incomplete indexDB
// due to snapshot or due to unflushed entries.
// Just increment errors counter and skip it for now.
atomic.AddUint64(&is.db.missingTSIDsForMetricID, 1)
err = nil
continue
}
err = fmt.Errorf("cannot find tsid for metricID=%d: %w", metricID, err)
return
}
is.db.putToMetricIDCache(metricID, tsid)
i++
}
})
if err != nil {
return nil, fmt.Errorf("error when searching for TSIDs by metricIDs in the previous indexdb: %w", err)
}
qt.Printf("found %d tsids for %d metricIDs in the previous indexdb", i-tsidsFound, len(extMetricIDs))
tsids = tsids[:i]
qt.Printf("load %d tsids for %d metricIDs from both current and previous indexdb", len(tsids), len(metricIDs))
// Sort the found tsids, since they must be passed to TSID search
// in the sorted order.
sort.Slice(tsids, func(i, j int) bool { return tsids[i].Less(&tsids[j]) })
qt.Printf("sort %d tsids", len(tsids))
return tsids, nil
}