时间序列数据是一种特殊的数据类型,它按照时间顺序记录和组织数据点,每个数据点都与特定的时间戳相关联。这种数据形式在现代数据分析中扮演着极其重要的角色。
主要特征:
- 时间维度:每个数据点都有明确的时间标记
- 顺序性:数据按时间先后顺序排列
- 连续性:通常以固定或变化的时间间隔收集
- 趋势性:可能展现出特定的模式、周期或趋势
应用场景举例:
- 工业领域:设备传感器实时监测数据,如温度、压力、振动等
- 金融市场:股票价格、汇率、交易量的每日记录
- 环境监测:气温、湿度、空气质量等定期采样数据
- 互联网运维:服务器性能指标、网络流量、用户访问量等
- 物联网设备:智能家居设备的使用数据、能源消耗记录
Nuget 安装LiteDB包
图片
数据模型设计
时间序列数据模型
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 时间序列数据操作
数据插入
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);
}
}
}
数据查询
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();
}
}
}
数据聚合与分析
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)
));
}
}
}
使用示例
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();
}
}
}
图片
图片
注意事项
- LiteDB 适合中小型时间序列数据
- 对于海量数据,考虑使用专业的时序数据库
- 定期备份数据库文件
- 注意内存使用和查询性能
- 使用复合索引
- 定期归档和清理旧数据
- 批量插入数据
- 选择合适的数据粒度
- 考虑数据压缩和分区策略
结论
LiteDB 提供了一个轻量、灵活的解决方案,用于在 C# 中处理时间序列数据。通过合理的数据模型和查询策略,可以高效地存储和分析时间序列信息。