C# LiteDB 时间序列数据处理完整指南

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

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

主要特征:

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

应用场景举例:

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

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# 中处理时间序列数据。通过合理的数据模型和查询策略,可以高效地存储和分析时间序列信息。

责任编辑:武晓燕 来源: 技术老小子
相关推荐

2022-11-16 08:41:43

2022-06-03 00:42:15

数据安全数据量

2024-05-15 15:27:39

2024-01-03 16:01:23

2024-05-08 14:05:03

时间序列数据

2019-06-12 16:21:52

时间序列PythonPandas

2013-08-26 09:36:27

大数据NoSQLMongoDB

2016-09-13 16:51:09

JavaScriptJava数据处理

2021-07-08 09:51:18

MaxCompute SQL数据处理

2024-04-28 11:25:02

C#JSON

2019-02-22 08:25:19

数据清洗预处理机器学习

2024-12-17 18:17:13

Python开发

2024-11-29 07:45:38

C#离线语音文字

2025-02-07 00:14:03

2025-02-04 10:23:56

C#视频版权

2023-07-07 09:04:18

JavaScript时间操作

2022-01-03 22:59:30

开发SDK数据

2009-09-07 13:29:30

C#计算素数序列

2024-01-31 23:22:35

vaexPython

2009-09-01 16:12:41

C#命名指南
点赞
收藏

51CTO技术栈公众号