250 lines
5.9 KiB
Go
250 lines
5.9 KiB
Go
// findSeriesByTags 根据标签查找序列
|
||
func (e *FileEngine) findSeriesByTags(tags map[string]string) ([]string, error) {
|
||
e.tagIndex.mu.RLock()
|
||
defer e.tagIndex.mu.RUnlock()
|
||
|
||
if len(tags) == 0 {
|
||
// 如果没有指定标签,返回所有序列
|
||
allSeries := make(map[string]struct{})
|
||
for _, valueMap := range e.tagIndex.index {
|
||
for _, seriesIDs := range valueMap {
|
||
for _, seriesID := range seriesIDs {
|
||
allSeries[seriesID] = struct{}{}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 转换为切片
|
||
result := make([]string, 0, len(allSeries))
|
||
for seriesID := range allSeries {
|
||
result = append(result, seriesID)
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// 对于每个标签,找到匹配的序列
|
||
var matchedSeries map[string]struct{}
|
||
first := true
|
||
|
||
for key, value := range tags {
|
||
// 获取标签值映射
|
||
valueMap, ok := e.tagIndex.index[key]
|
||
if !ok {
|
||
// 如果标签键不存在,返回空结果
|
||
return []string{}, nil
|
||
}
|
||
|
||
// 获取匹配标签值的序列
|
||
seriesIDs, ok := valueMap[value]
|
||
if !ok {
|
||
// 如果标签值不存在,返回空结果
|
||
return []string{}, nil
|
||
}
|
||
|
||
// 初始化或取交集
|
||
if first {
|
||
matchedSeries = make(map[string]struct{})
|
||
for _, id := range seriesIDs {
|
||
matchedSeries[id] = struct{}{}
|
||
}
|
||
first = false
|
||
} else {
|
||
// 取交集
|
||
newMatched := make(map[string]struct{})
|
||
for _, id := range seriesIDs {
|
||
if _, ok := matchedSeries[id]; ok {
|
||
newMatched[id] = struct{}{}
|
||
}
|
||
}
|
||
matchedSeries = newMatched
|
||
}
|
||
|
||
// 如果没有匹配的序列,提前返回
|
||
if len(matchedSeries) == 0 {
|
||
return []string{}, nil
|
||
}
|
||
}
|
||
|
||
// 转换为切片
|
||
result := make([]string, 0, len(matchedSeries))
|
||
for seriesID := range matchedSeries {
|
||
result = append(result, seriesID)
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// readPointsFromSegment 从段文件中读取数据点
|
||
func (e *FileEngine) readPointsFromSegment(segment *Segment, offset int64, count int) ([]engine.DataPoint, error) {
|
||
segment.mu.RLock()
|
||
defer segment.mu.RUnlock()
|
||
|
||
// 如果文件为空,直接返回
|
||
if segment.size == 0 {
|
||
return []engine.DataPoint{}, nil
|
||
}
|
||
|
||
// 移动文件指针到指定位置
|
||
_, err := segment.file.Seek(offset, 0)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to seek segment file: %v", err)
|
||
}
|
||
|
||
// 读取指定数量的数据点
|
||
points := make([]engine.DataPoint, 0, count)
|
||
for i := 0; i < count; i++ {
|
||
// 读取时间戳(8字节)
|
||
var timestamp int64
|
||
err := binary.Read(segment.file, binary.LittleEndian, ×tamp)
|
||
if err != nil {
|
||
if err == io.EOF {
|
||
break
|
||
}
|
||
return nil, fmt.Errorf("failed to read timestamp: %v", err)
|
||
}
|
||
|
||
// 读取值(8字节)
|
||
var value float64
|
||
err = binary.Read(segment.file, binary.LittleEndian, &value)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("failed to read value: %v", err)
|
||
}
|
||
|
||
// 创建数据点
|
||
point := engine.DataPoint{
|
||
Timestamp: timestamp,
|
||
Value: value,
|
||
}
|
||
points = append(points, point)
|
||
}
|
||
|
||
return points, nil
|
||
}
|
||
|
||
// findTimeWindows 查找指定时间范围内的时间窗口
|
||
func (e *FileEngine) findTimeWindows(seriesID string, startTime, endTime int64) ([]timeWindow, error) {
|
||
e.timeIndex.mu.RLock()
|
||
defer e.timeIndex.mu.RUnlock()
|
||
|
||
// 获取序列的所有时间窗口
|
||
windows, ok := e.timeIndex.windows[seriesID]
|
||
if !ok {
|
||
return nil, nil // 序列不存在
|
||
}
|
||
|
||
// 找到所有与时间范围重叠的窗口
|
||
var matchedWindows []timeWindow
|
||
for _, window := range windows {
|
||
// 检查窗口是否与查询时间范围重叠
|
||
if window.endTime >= startTime && window.startTime <= endTime {
|
||
matchedWindows = append(matchedWindows, window)
|
||
}
|
||
}
|
||
|
||
// 按时间排序
|
||
sort.Slice(matchedWindows, func(i, j int) bool {
|
||
return matchedWindows[i].startTime < matchedWindows[j].startTime
|
||
})
|
||
|
||
return matchedWindows, nil
|
||
}
|
||
|
||
// getLabelsForSeries 获取序列的标签
|
||
func (e *FileEngine) getLabelsForSeries(seriesID string) map[string]string {
|
||
e.tagIndex.mu.RLock()
|
||
defer e.tagIndex.mu.RUnlock()
|
||
|
||
labels := make(map[string]string)
|
||
|
||
// 遍历所有标签
|
||
for key, valueMap := range e.tagIndex.index {
|
||
// 遍历每个标签值
|
||
for value, seriesIDs := range valueMap {
|
||
// 检查序列ID是否在列表中
|
||
for _, id := range seriesIDs {
|
||
if id == seriesID {
|
||
labels[key] = value
|
||
break
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return labels
|
||
}
|
||
|
||
// newTimeIndex 创建新的时间索引
|
||
func newTimeIndex() *TimeIndex {
|
||
return &TimeIndex{
|
||
windows: make(map[string][]timeWindow),
|
||
}
|
||
}
|
||
|
||
// newTagIndex 创建新的标签索引
|
||
func newTagIndex() *TagIndex {
|
||
return &TagIndex{
|
||
index: make(map[string]map[string][]string),
|
||
}
|
||
}
|
||
|
||
// WritePoint 向段文件写入数据点
|
||
func (s *Segment) WritePoint(point engine.DataPoint) (int64, error) {
|
||
s.mu.Lock()
|
||
defer s.mu.Unlock()
|
||
|
||
// 获取当前偏移量
|
||
offset, err := s.file.Seek(0, io.SeekCurrent)
|
||
if err != nil {
|
||
return 0, fmt.Errorf("failed to get file offset: %v", err)
|
||
}
|
||
|
||
// 写入时间戳(8字节)
|
||
err = binary.Write(s.file, binary.LittleEndian, point.Timestamp)
|
||
if err != nil {
|
||
return 0, fmt.Errorf("failed to write timestamp: %v", err)
|
||
}
|
||
|
||
// 写入值(8字节)
|
||
err = binary.Write(s.file, binary.LittleEndian, point.Value)
|
||
if err != nil {
|
||
return 0, fmt.Errorf("failed to write value: %v", err)
|
||
}
|
||
|
||
// 更新段文件信息
|
||
s.size += 16 // 每个数据点16字节
|
||
s.pointCount++
|
||
if s.startTime == 0 || point.Timestamp < s.startTime {
|
||
s.startTime = point.Timestamp
|
||
}
|
||
if point.Timestamp > s.endTime {
|
||
s.endTime = point.Timestamp
|
||
}
|
||
|
||
return offset, nil
|
||
}
|
||
|
||
// Close 关闭段文件
|
||
func (s *Segment) Close() error {
|
||
s.mu.Lock()
|
||
defer s.mu.Unlock()
|
||
|
||
if s.file != nil {
|
||
if err := s.file.Close(); err != nil {
|
||
return fmt.Errorf("failed to close segment file: %v", err)
|
||
}
|
||
s.file = nil
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// Sync 将段文件同步到磁盘
|
||
func (s *Segment) Sync() error {
|
||
s.mu.Lock()
|
||
defer s.mu.Unlock()
|
||
|
||
if s.file != nil {
|
||
if err := s.file.Sync(); err != nil {
|
||
return fmt.Errorf("failed to sync segment file: %v", err)
|
||
}
|
||
}
|
||
return nil
|
||
} |