gotidb/docs/design/engine-design.md

9.6 KiB
Raw Blame History

存储引擎设计文档

1. 概述

GoTiDB存储引擎抽象层旨在提供统一的接口使不同的存储后端可以无缝集成到系统中。本文档描述了存储引擎的设计原则、接口定义和实现建议。

2. 设计目标

  • 抽象统一: 提供一致的API隐藏不同存储引擎的实现细节
  • 可扩展性: 支持添加新的存储引擎而无需修改核心代码
  • 性能优化: 针对时序数据的特点进行优化
  • 可配置性: 允许通过配置调整引擎行为

3. 存储引擎接口

3.1 核心接口

// Engine 是所有存储引擎必须实现的基础接口
type Engine interface {
    // 基本生命周期
    Open() error
    Close() error
    
    // 数据操作
    WritePoint(ctx context.Context, point DataPoint) error
    WriteBatch(ctx context.Context, points []DataPoint) error
    
    // 查询操作
    Query(ctx context.Context, query Query) (QueryResult, error)
    
    // 管理操作
    Flush() error
    Compact() error
    
    // 监控
    Stats() EngineStats
    
    // 能力查询
    Capabilities() EngineCapabilities
}

3.2 扩展接口

特定引擎可以实现额外接口来提供特殊功能:

// PersistentEngine 提供持久化功能
type PersistentEngine interface {
    Engine
    Backup(path string) error
    Restore(path string) error
}

// ReplicatedEngine 提供复制功能
type ReplicatedEngine interface {
    Engine
    AddReplica(addr string) error
    RemoveReplica(addr string) error
}

4. 统一查询接口

所有读操作通过统一的Query接口实现提供灵活性和一致性

// Query 定义查询参数
type Query struct {
    // 查询类型
    Type QueryType
    
    // 时间范围
    StartTime int64
    EndTime   int64
    
    // 序列标识
    SeriesID   string
    DeviceID   string
    MetricCode string
    
    // 标签过滤
    TagFilters []TagFilter
    
    // 聚合选项
    Aggregation     AggregationType
    AggInterval     time.Duration
    IncludeRawData  bool
    
    // 结果限制
    Limit  int
    Offset int
    
    // 其他查询选项
    Options map[string]interface{}
}

// QueryType 定义查询类型
type QueryType int

const (
    // 原始数据查询
    QueryTypeRaw QueryType = iota
    
    // 聚合查询
    QueryTypeAggregate
    
    // 最新值查询
    QueryTypeLatest
    
    // 标签查询
    QueryTypeTags
    
    // 元数据查询
    QueryTypeMetadata
)

// TagFilter 定义标签过滤条件
type TagFilter struct {
    Key      string
    Operator FilterOperator
    Value    string
}

// FilterOperator 定义过滤操作符
type FilterOperator int

const (
    OpEqual FilterOperator = iota
    OpNotEqual
    OpRegex
    OpGreaterThan
    OpLessThan
    // 更多操作符...
)

// AggregationType 定义聚合类型
type AggregationType int

const (
    AggNone AggregationType = iota
    AggSum
    AggAvg
    AggMin
    AggMax
    AggCount
    // 更多聚合类型...
)

4.1 查询结果

// QueryResult 定义查询结果
type QueryResult interface {
    // 结果类型
    Type() QueryType
}

// TimeSeriesResult 定义时间序列查询结果
type TimeSeriesResult struct {
    SeriesID string
    Points   []DataPoint
}

// AggregateResult 定义聚合查询结果
type AggregateResult struct {
    SeriesID string
    Groups   []AggregateGroup
}

type AggregateGroup struct {
    StartTime int64
    EndTime   int64
    Value     float64
    Count     int
}

4.2 查询构建器

为了简化查询构建提供流式API

query := NewQueryBuilder().
    ForMetric("cpu.usage").
    WithTimeRange(startTime, endTime).
    WithTag("host", OpEqual, "server01").
    WithAggregation(AggAvg, 5*time.Minute).
    Build()

5. 配置抽象

type EngineConfig interface {
    // 通用配置方法
    WithMaxRetention(duration time.Duration) EngineConfig
    WithMaxPoints(points int) EngineConfig
    WithFlushInterval(interval time.Duration) EngineConfig
    
    // 获取特定引擎的配置
    MemoryConfig() *MemoryEngineConfig
    FileConfig() *FileEngineConfig
    // 其他引擎...
}

// 内存引擎特定配置
type MemoryEngineConfig struct {
    MaxPointsPerSeries int  // 可配置的保留点数替代硬编码的30
    UseCompression bool
    // 其他内存引擎特定参数...
}

6. 引擎注册机制

// EngineRegistry 管理所有可用的存储引擎
type EngineRegistry struct {
    engines map[string]EngineFactory
}

// EngineFactory 创建存储引擎实例
type EngineFactory func(config EngineConfig) (Engine, error)

// 注册新引擎
func (r *EngineRegistry) Register(name string, factory EngineFactory) {
    r.engines[name] = factory
}

// 创建引擎实例
func (r *EngineRegistry) Create(name string, config EngineConfig) (Engine, error) {
    if factory, ok := r.engines[name]; ok {
        return factory(config)
    }
    return nil, fmt.Errorf("unknown engine: %s", name)
}

7. 性能优化建议

7.1 写入路径优化

实现写入缓冲区合并小批量写入:

type WriteBuffer struct {
    points     map[string][]DataPoint  // 按序列ID分组
    mu         sync.Mutex
    maxSize    int
    flushCh    chan struct{}
    engine     Engine
}

func (wb *WriteBuffer) Add(point DataPoint) {
    wb.mu.Lock()
    seriesID := point.SeriesID()
    wb.points[seriesID] = append(wb.points[seriesID], point)
    size := len(wb.points)
    wb.mu.Unlock()
    
    if size >= wb.maxSize {
        wb.Flush()
    }
}

func (wb *WriteBuffer) Flush() {
    wb.mu.Lock()
    points := wb.points
    wb.points = make(map[string][]DataPoint)
    wb.mu.Unlock()
    
    // 批量写入引擎
    wb.engine.WriteBatch(context.Background(), points)
}

7.2 并发控制优化

实现分片锁减少锁竞争:

type ShardedLock struct {
    locks     []sync.RWMutex
    shardMask uint64
}

func NewShardedLock(shards int) *ShardedLock {
    // 确保分片数是2的幂
    shards = nextPowerOfTwo(shards)
    return &ShardedLock{
        locks:     make([]sync.RWMutex, shards),
        shardMask: uint64(shards - 1),
    }
}

func (sl *ShardedLock) getLockForKey(key string) *sync.RWMutex {
    h := fnv.New64()
    h.Write([]byte(key))
    hashVal := h.Sum64()
    return &sl.locks[hashVal&sl.shardMask]
}

func (sl *ShardedLock) Lock(key string) {
    sl.getLockForKey(key).Lock()
}

func (sl *ShardedLock) Unlock(key string) {
    sl.getLockForKey(key).Unlock()
}

7.3 内存优化

实现时序数据的紧凑存储:

// 紧凑存储时间戳和值
type CompactTimeSeriesBlock struct {
    baseTime    int64
    deltaEncode []byte  // 使用delta编码存储时间戳
    values      []byte  // 压缩存储的值
}

func NewCompactBlock(baseTime int64, capacity int) *CompactTimeSeriesBlock {
    return &CompactTimeSeriesBlock{
        baseTime:    baseTime,
        deltaEncode: make([]byte, 0, capacity*binary.MaxVarintLen64),
        values:      make([]byte, 0, capacity*8), // 假设double值
    }
}

func (b *CompactTimeSeriesBlock) AddPoint(timestamp int64, value float64) {
    // 存储时间增量
    delta := timestamp - b.baseTime
    buf := make([]byte, binary.MaxVarintLen64)
    n := binary.PutVarint(buf, delta)
    b.deltaEncode = append(b.deltaEncode, buf[:n]...)
    
    // 存储值
    bits := math.Float64bits(value)
    buf = make([]byte, 8)
    binary.LittleEndian.PutUint64(buf, bits)
    b.values = append(b.values, buf...)
}

7.4 查询优化

实现时间范围索引:

type TimeRangeIndex struct {
    // 每个时间窗口的起始位置
    windows     []timeWindow
    blockSize   int64  // 时间窗口大小如1小时
}

type timeWindow struct {
    startTime int64
    endTime   int64
    offset    int  // 数据块中的偏移
}

func (idx *TimeRangeIndex) FindBlocks(start, end int64) []int {
    var result []int
    for i, window := range idx.windows {
        if window.endTime >= start && window.startTime <= end {
            result = append(result, i)
        }
    }
    return result
}

8. 实现路线图

  1. 定义核心接口

    • 实现Engine接口
    • 定义Query和QueryResult结构
  2. 重构现有引擎

    • 调整内存引擎以实现新接口
    • 使MaxPointsPerSeries可配置
  3. 实现查询构建器

    • 创建流式API构建查询
  4. 添加性能优化

    • 实现写入缓冲区
    • 添加分片锁
    • 优化内存使用
  5. 实现引擎注册机制

    • 创建EngineRegistry
    • 支持动态引擎选择
  6. 添加监控和统计

    • 实现Stats接口
    • 收集性能指标

9. 使用示例

// 创建引擎
registry := NewEngineRegistry()
registry.Register("memory", NewMemoryEngine)
registry.Register("file", NewFileEngine)

config := NewEngineConfig().
    WithMaxRetention(24 * time.Hour).
    WithMaxPoints(1000)

engine, err := registry.Create("memory", config)
if err != nil {
    log.Fatal(err)
}

// 写入数据
point := DataPoint{
    DeviceID:   "device1",
    MetricCode: "temperature",
    Labels:     map[string]string{"location": "room1"},
    Value:      25.5,
    Timestamp:  time.Now().UnixNano(),
}
err = engine.WritePoint(context.Background(), point)

// 查询数据
query := NewQueryBuilder().
    ForMetric("temperature").
    WithTimeRange(startTime, endTime).
    WithTag("location", OpEqual, "room1").
    Build()

result, err := engine.Query(context.Background(), query)
if err != nil {
    log.Fatal(err)
}

// 处理结果
if tsResult, ok := result.(*TimeSeriesResult); ok {
    for _, point := range tsResult.Points {
        fmt.Printf("Time: %v, Value: %v\n", 
            time.Unix(0, point.Timestamp), point.Value)
    }
}