100臺裝置採集資料,並寫入資料庫
阿新 • • 發佈:2018-11-09
需求見: 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<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)
有空再寫一個執行緒池的程式碼。