需求见: https://bbs.csdn.net/topics/392471595
有 100 台左右的设备, 每秒采集一条数据,再向 SQL Server 2008 数据库写入数据。
一天的数据量: 100*3600*24=8640000
难点:
第 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 myList = new List();
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 list = new List();
lock (myList)
{
list.AddRange(myList);
myList.Clear();
}
Insert(list);
}
private static void Insert(List 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)
有空再写一个线程池的代码。