在使用SqlBulkCopy

时间:2019-09-26 12:51:36

标签: c# sql-server sqlbulkcopy

我有一个C#网站,该网站记录API调用,它将日志作为一行插入SQL Server数据库中。当站点真正繁忙时,这才是紧要关头,日志表将锁定,并且我们会看到等待数据库连接的超时。我正在尝试使用SqlBulkCopy对此进行补救。原来的日志方法被新的BulkLogger类中的方法所取代,当包含日志的DataTable达到限制(目前为100)时,将调用Flush()方法,并将日志全部写入数据库。

最初的log方法是从代码中调用的,它是没有DI的旧代码,因此我必须在几个地方创建new BulkLogger。我担心某些日志将不会被写入,因为异常处理不当并且BulkLogger可能会丢失,或者只是在页面请求完成时(如果某些日志仍在内存中等待大容量复制)也会丢失。

我的BulkLogger类可以被密封,我没有任何资源可以清除或处置。因此,如果BulkLogger实现了IDisposable,而我实现了Dispose,那么这是否意味着在大多数情况下所有日志都被写入数据库了?

public sealed class BulkLogger : IDisposable
{
    DataSet _logSet;
    DataTable _logTable;

    public BulkLogger() {
        // set up the dataset and datatable 
    }

    public void Log(string message, DateTime logTime) {
        // add message and time to _logTable
        // if _logTable.count > 100 call Flush()
    }

    public void Flush() {
        // Use SqlBulkCopy to insert logs
    }

    public void Dispose()
    {
        Flush();
    }
}

2 个答案:

答案 0 :(得分:1)

使用类似SQLBulkCopy之类的东西,仅允许您根据MSDN文档(https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlbulkcopy?view=netframework-4.8)向数据库/表中插入大量数据。因此,采用这种方法很可能意味着您首先必须将要登录到数据库的调用存储在内存中,一旦达到某个阈值,就将其写入数据库。

我也不认为这是一个好方法,因为您很可能会看到所用资源的增加。

请考虑创建一个单独的线程以将您的日志行写入数据库。 只需要从实例化其自己的连接实例到数据库的线程进行调用,就不需要更改任何代码。

Thread t1 = new Thread(()=>{
SQLConnection con = new SQLConnection("yourConnection");
SqlCommand command = new SqlCommand("INSERT INTO LOG TABLE", con );
command.ExecuteNonQuery();
});
t1.Start();

查看如何从以下位置创建线程:https://docs.microsoft.com/en-us/dotnet/api/system.threading.thread.start?view=netframework-4.8

这是有关如何实现数据库日志记录的示例项目的另一个链接。 https://code.msdn.microsoft.com/How-to-implement-logging-4cbcfc64

答案 1 :(得分:1)

如果太多的线程中断任务使数据库不堪重负,无法插入数据库,则可能需要缩小到数据库的直接管道。不用开始执行线程来进行插入,而是使用1个线程或消耗请求将其插入数据库的线程池。您可以使用ConcurrentQueue<T>在主应用程序线程上将请求排队(生产者),而专用线程则监视此队列并使用请求。这样,插入将被同步处理,而不是全部免费。

以下是生产者/消费者示例的链接:

https://docs.microsoft.com/en-us/dotnet/standard/collections/thread-safe/blockingcollection-overview?redirectedfrom=MSDN

C# producer/consumer