2025-11-08
LiteDB
00

目录

时间序列数据简介
主要特征:
应用场景举例:
Nuget 安装LiteDB包
数据模型设计
时间序列数据模型
LiteDB 时间序列数据操作
数据插入
数据查询
数据聚合与分析
使用示例
注意事项
结论

时间序列数据简介

时间序列数据是一种特殊的数据类型,它按照时间顺序记录和组织数据点,每个数据点都与特定的时间戳相关联。这种数据形式在现代数据分析中扮演着极其重要的角色。

主要特征:

  1. 时间维度:每个数据点都有明确的时间标记
  2. 顺序性:数据按时间先后顺序排列
  3. 连续性:通常以固定或变化的时间间隔收集
  4. 趋势性:可能展现出特定的模式、周期或趋势

应用场景举例:

  • 工业领域:设备传感器实时监测数据,如温度、压力、振动等
  • 金融市场:股票价格、汇率、交易量的每日记录
  • 环境监测:气温、湿度、空气质量等定期采样数据
  • 互联网运维:服务器性能指标、网络流量、用户访问量等
  • 物联网设备:智能家居设备的使用数据、能源消耗记录

Nuget 安装LiteDB包

image.png

数据模型设计

时间序列数据模型

C#
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using LiteDB; namespace App07 { /// <summary> /// 时间序列数据点模型 /// </summary> public class TimeSeriesDataPoint { /// <summary> /// 唯一标识符 /// </summary> [BsonId] public ObjectId Id { get; set; } /// <summary> /// 数据记录时间戳 /// </summary> public DateTime Timestamp { get; set; } /// <summary> /// 数据源或传感器标识 /// </summary> public string SourceId { get; set; } /// <summary> /// 数值类型数据 /// </summary> public double Value { get; set; } /// <summary> /// 可选的额外元数据 /// </summary> public Dictionary<string, object> Metadata { get; set; } } }

LiteDB 时间序列数据操作

数据插入

C#
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using LiteDB; namespace App07 { public class TimeSeriesRepository { private readonly LiteDatabase _database; private readonly ILiteCollection<TimeSeriesDataPoint> _collection; public LiteDatabase Database => _database; public TimeSeriesRepository(string databasePath) { _database = new LiteDatabase(databasePath); _collection = _database.GetCollection<TimeSeriesDataPoint>("time_series_data"); // 创建时间戳和源ID的复合索引 _collection.EnsureIndex(x => x.Timestamp); _collection.EnsureIndex(x => x.SourceId); } /// <summary> /// 插入单个数据点 /// </summary> public void InsertDataPoint(TimeSeriesDataPoint dataPoint) { _collection.Insert(dataPoint); } /// <summary> /// 批量插入数据点 /// </summary> public void InsertBatch(IEnumerable<TimeSeriesDataPoint> dataPoints) { _collection.InsertBulk(dataPoints); } } }

数据查询

C#
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using LiteDB; namespace App07 { public class TimeSeriesQuery { private readonly ILiteCollection<TimeSeriesDataPoint> _collection; public TimeSeriesQuery(LiteDatabase database) { _collection = database.GetCollection<TimeSeriesDataPoint>("time_series_data"); } /// <summary> /// 查询特定时间范围内的数据 /// </summary> public IEnumerable<TimeSeriesDataPoint> GetDataInTimeRange( DateTime startTime, DateTime endTime, string sourceId = null) { var query = Query.And( Query.GTE("Timestamp", startTime), Query.LTE("Timestamp", endTime) ); if (!string.IsNullOrEmpty(sourceId)) { query = Query.And(query, Query.EQ("SourceId", sourceId)); } return _collection.Find(query); } /// <summary> /// 获取最近的数据点 /// </summary> public TimeSeriesDataPoint GetLatestDataPoint(string sourceId) { return _collection .Find(x => x.SourceId == sourceId) .OrderByDescending(x => x.Timestamp) .FirstOrDefault(); } } }

数据聚合与分析

C#
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using LiteDB; namespace App07 { public class TimeSeriesAnalytics { private readonly ILiteCollection<TimeSeriesDataPoint> _collection; public TimeSeriesAnalytics(LiteDatabase database) { _collection = database.GetCollection<TimeSeriesDataPoint>("time_series_data"); } /// <summary> /// 计算时间范围内的统计数据 /// </summary> public (double Min, double Max, double Average) CalculateStatistics(DateTime startTime, DateTime endTime, string sourceId) { var data = _collection.Find(x => x.Timestamp >= startTime && x.Timestamp <= endTime && x.SourceId == sourceId); return ( data.Min(x => x.Value), data.Max(x => x.Value), data.Average(x => x.Value) ); } /// <summary> /// 按时间间隔聚合数据 /// </summary> public IEnumerable<(DateTime Interval, double AggregatedValue)> AggregateByInterval( DateTime startTime, DateTime endTime, string sourceId, TimeSpan interval) { return _collection .Find(x => x.Timestamp >= startTime && x.Timestamp <= endTime && x.SourceId == sourceId) .GroupBy(x => Math.Floor((x.Timestamp - startTime).TotalMinutes / interval.TotalMinutes)) .Select(g => ( Interval: startTime.AddMinutes(g.Key * interval.TotalMinutes), AggregatedValue: g.Average(x => x.Value) )); } } }

使用示例

C#
namespace App07 { internal class Program { static void Main(string[] args) { var repository = new TimeSeriesRepository("timeseries.db"); var query = new TimeSeriesQuery(repository.Database); var analytics = new TimeSeriesAnalytics(repository.Database); // 创建一个随机数生成器,用于模拟传感器数据 var random = new Random(); Console.WriteLine("开始模拟数据写入,按 'Q' 键退出..."); // 创建一个取消令牌源 using var cancellationTokenSource = new CancellationTokenSource(); // 启动异步任务来写入数据 Task.Run(async () => { while (!cancellationTokenSource.Token.IsCancellationRequested) { var dataPoint = new TimeSeriesDataPoint { Timestamp = DateTime.Now, SourceId = "sensor_001", Value = 20 + random.NextDouble() * 10, // 生成20-30之间的随机温度 Metadata = new Dictionary<string, object> { { "Location", "Main Room" }, { "Unit", "Celsius" } } }; repository.InsertDataPoint(dataPoint); Console.WriteLine($"写入数据点 - 时间: {dataPoint.Timestamp}, 值: {dataPoint.Value:F2}°C"); // 等待1秒 await Task.Delay(1000, cancellationTokenSource.Token); } }, cancellationTokenSource.Token); // 等待用户按 'Q' 键退出 while (Console.ReadKey(true).Key != ConsoleKey.Q) { // 继续循环直到按下 'Q' 键 } // 取消数据写入任务 cancellationTokenSource.Cancel(); Console.WriteLine("\n数据写入已停止"); // 显示一些统计信息 try { var lastMinute = DateTime.Now.AddMinutes(-1); var stats = analytics.CalculateStatistics( lastMinute, DateTime.Now, "sensor_001" ); Console.WriteLine("\n最近一分钟的统计数据:"); Console.WriteLine($"最小值: {stats.Min:F2}°C"); Console.WriteLine($"最大值: {stats.Max:F2}°C"); Console.WriteLine($"平均值: {stats.Average:F2}°C"); } catch (Exception ex) { Console.WriteLine($"计算统计数据时出错: {ex.Message}"); } var result = query.GetLatestDataPoint("sensor_001"); Console.WriteLine($"\n最新数据点: 时间: {result.Timestamp}, 值: {result.Value:F2}°C"); Console.WriteLine("\n按任意键退出..."); Console.ReadKey(); } } }

image.png

image.png

注意事项

  • LiteDB 适合中小型时间序列数据
  • 对于海量数据,考虑使用专业的时序数据库
  • 定期备份数据库文件
  • 注意内存使用和查询性能
  • 使用复合索引
  • 定期归档和清理旧数据
  • 批量插入数据
  • 选择合适的数据粒度
  • 考虑数据压缩和分区策略

结论

LiteDB 提供了一个轻量、灵活的解决方案,用于在 C# 中处理时间序列数据。通过合理的数据模型和查询策略,可以高效地存储和分析时间序列信息。

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!