100台设备采集数据,并写入数据库

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yenange/article/details/83662675

需求见: https://bbs.csdn.net/topics/392471595

有 100 台左右的设备, 每秒采集一条数据,再向 SQL Server 2008 数据库写入数据。

一天的数据量: 100*3600*24=8640000

难点:

  1. 并发量大,数据库、硬盘压力大;
  2. 每天的数据量大,必须要分历史表,做好归档

第 2 点属于数据库的操作,可以用 SQL Server 的代理作业来完成。

主要是第 1 点。

如果每台设备都单独写入数据库,也是可以,但数据库的效率就比较低了。

我的思路是将这一百台设备的数据全部采集,在内存中缓存,然后用一个专门的线程定时写入到数据库。

这样这个表上没有任何的并发插入,数据库的压力将大大降低。


数据库表的创建脚本:

--个人测试采用临时库 tempdb。 
--正式环境千万不能用 tempdb,数据在重启后会消失!
USE tempdb
GO
IF OBJECT_ID('dbo.device_log_data') IS NOT NULL 
	DROP TABLE dbo.device_log_data
GO
CREATE TABLE dbo.device_log_data(
	[logId] INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
	[deviceId] INT NOT NULL,
	[value] INT NOT NULL,
	[deviceTime] DATETIME NOT NULL, 
	[insertTime] DATETIME NOT NULL DEFAULT(GETDATE())
)
GO

程序代码:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Timers;

namespace ConsoleApp3
{
    class Program
    {
        static readonly string CONN_STRING = @"Data Source=.\sqlserver2014;Initial Catalog=tempdb;Integrated Security=True";
        static readonly string TABLE_NAME = "device_log_data";
        static List<MyData> myList = new List<MyData>();

        static void Main(string[] args)
        {
            for (int i = 1; i <= 100; i++)
            {
                MyTimer timer = new MyTimer(1000);
                timer.DeiviceId = i;
                timer.Elapsed += Timer_Elapsed;
            }
            MyTimer timerInsert = new MyTimer(10000);
            timerInsert.Elapsed += TimerInsert_Elapsed;

            Console.Read();
        }

        private static void Timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            MyTimer myTimer = (MyTimer)sender;
            lock (myList)
            {
                myList.Add(new MyData() {
                    DeviceId = myTimer.DeiviceId,
                    Value = new Random().Next(1, 1000),
                    DeviceTime = DateTime.Now
                });
            }
        }

        private static void TimerInsert_Elapsed(object sender, ElapsedEventArgs e)
        {
            List<MyData> list = new List<MyData>();
            lock (myList)
            {
                list.AddRange(myList);
                myList.Clear();
            }
            Insert(list);
        }

        private static void Insert(List<MyData> list)
        {
            DataTable dt = new DataTable();
            dt.Columns.Add(new DataColumn("deviceId",typeof(Int32)));
            dt.Columns.Add(new DataColumn("value", typeof(Int32)));
            dt.Columns.Add(new DataColumn("deviceTime", typeof(string)));

            foreach(var item in list)
            {
                DataRow dr = dt.NewRow();
                dr["deviceId"] = item.DeviceId;
                dr["value"] = item.Value;
                dr["deviceTime"] = item.DeviceTime;
                dt.Rows.Add(dr);
            }

            try
            {
                using (SqlBulkCopy bulkcopy = new SqlBulkCopy(CONN_STRING))
                {
                    bulkcopy.DestinationTableName = TABLE_NAME;
                    bulkcopy.ColumnMappings.Add("deviceId", "deviceId");
                    bulkcopy.ColumnMappings.Add("value", "value");
                    bulkcopy.ColumnMappings.Add("deviceTime", "deviceTime");
                    bulkcopy.WriteToServer(dt);
                }
                Console.WriteLine("inserted {0} rows", list.Count);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
    class MyTimer: Timer
    {
        public MyTimer(double interval)
        {
            this.Interval = interval;
            this.AutoReset = true;
            this.Enabled = true;
        }
        public int DeiviceId { get; set; }
    }
    class MyData
    {
        public int DeviceId { get; set; }
        public int Value { get; set; }
        public DateTime DeviceTime { get; set; }
    }
}

以 Release 来编译,再运行 .exe 文件, 查看系统资源占用情况:

可见, 效果还是非常不错的。

需要注意的是,  查询数据还是应该用 WITH(NOLOCK):

SELECT TOP 10 * FROM dbo.device_log_data WITH(NOLOCK)

有空再写一个线程池的代码。

猜你喜欢

转载自blog.csdn.net/yenange/article/details/83662675