简体   繁体   English

批量插入在Azure SQL Server中无法正常工作

[英]Bulk insert is not working properly in Azure SQL Server

I'm not able to insert the bulk amount of data into Azure SQL server DB using C# webapi 我无法使用C#webapi将大量数据插入Azure SQL Server数据库

Consider 考虑

I want to insert 60K> data in SQL. 我想在SQL中插入60K>数据。 In my local sql server there is no problem but in Azure SQL its getting connection timed-out 在我的本地SQL服务器中没有问题,但在Azure SQL中它的连接超时

My approach:(All are working in local sql server but not in Azure sql server) 我的方法:(所有都在本地sql服务器上工作,但在Azure sql server中没有)

1) Tried using EF its inserting record one by one (For 10000 approx. 10 min,mostly timeout) 1)尝试使用EF逐个插入记录(10000次约10分钟,大部分超时)

2) Tried using Bulk insert Extension along with EF 3) Tried in SqlBulkCopy 2)尝试使用批量插入扩展和EF 3)在SqlBulkCopy中尝试

4) Tried increasing connection time out in connection string 4)尝试增加连接字符串中的连接超时

5) Tried increasing command time out in Dbcontext. 5)尝试在Dbcontext中增加命令时间。

Exception StackTrace 异常StackTrace

Execution Timeout Expired.  The timeout period elapsed prior to completion of the operation or the server is not responding.
System.Data.SqlClient.SqlException (0x80131904): Execution Timeout Expired.  The timeout period elapsed prior to completion of the operation or the server is not responding. ---> System.ComponentModel.Win32Exception (0x80004005): The wait operation timed out
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at System.Data.SqlClient.TdsParserStateObject.ReadSniError(TdsParserStateObject stateObj, UInt32 error)
   at System.Data.SqlClient.TdsParserStateObject.ReadSniSyncOverAsync()
   at System.Data.SqlClient.TdsParserStateObject.TryReadNetworkPacket()
   at System.Data.SqlClient.TdsParserStateObject.TryPrepareBuffer()
   at System.Data.SqlClient.TdsParserStateObject.TryReadByte(Byte& value)
   at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
   at System.Data.SqlClient.SqlBulkCopy.RunParser(BulkCopySimpleResultSet bulkCopyHandler)
   at System.Data.SqlClient.SqlBulkCopy.CopyBatchesAsyncContinuedOnSuccess(BulkCopySimpleResultSet internalResults, String updateBulkCommandText, CancellationToken cts, TaskCompletionSource`1 source)
   at System.Data.SqlClient.SqlBulkCopy.CopyBatchesAsyncContinued(BulkCopySimpleResultSet internalResults, String updateBulkCommandText, CancellationToken cts, TaskCompletionSource`1 source)
   at System.Data.SqlClient.SqlBulkCopy.CopyBatchesAsync(BulkCopySimpleResultSet internalResults, String updateBulkCommandText, CancellationToken cts, TaskCompletionSource`1 source)
   at System.Data.SqlClient.SqlBulkCopy.WriteToServerInternalRestContinuedAsync(BulkCopySimpleResultSet internalResults, CancellationToken cts, TaskCompletionSource`1 source)
   at System.Data.SqlClient.SqlBulkCopy.WriteToServerInternalRestAsync(CancellationToken cts, TaskCompletionSource`1 source)
   at System.Data.SqlClient.SqlBulkCopy.WriteToServerInternalAsync(CancellationToken ctoken)
   at System.Data.SqlClient.SqlBulkCopy.WriteRowSourceToServerAsync(Int32 columnCount, CancellationToken ctoken)
   at System.Data.SqlClient.SqlBulkCopy.WriteToServer(DataTable table, DataRowState rowState)

Is there any solution for it or any configuration to be changed in Azure? 是否有任何解决方案或Azure中的任何配置要更改?

Update 更新

Code used for bulk insert 用于批量插入的代码

  using (var dbConnection = new DBModel().Database.Connection as SqlConnection)
                {
                    dbConnection?.Open();
                    using (var sqlBulkCopy = new SqlBulkCopy(dbConnection))
                    {
                        try
                        {
                            /* ColumnMapping
                             * Column is mapped to DB Column to DataTable Column
                             *
                             */
                            sqlBulkCopy.EnableStreaming = true;
                            sqlBulkCopy.BulkCopyTimeout = 500;
                            sqlBulkCopy.DestinationTableName = "LogTable";
                            //dt is object of the Datatable
                            sqlBulkCopy.WriteToServer(dt);
                        }
                        catch (Exception ex)
                        {

                        }
                    }


                }

I'd suggest you set sqlBulkCopy.BatchSize to a reasonable amount, instead of inserting everything in one batch. 我建议你将sqlBulkCopy.BatchSize设置为合理的数量,而不是一次性插入所有内容。 Depending on the data you're inserting, try starting with 10.000 and work your way up or down until you're satisfied with the performance. 根据您要插入的数据,尝试从10.000开始,向上或向下工作,直到您对性能满意为止。

Edit for some extra clarification: When you consider your batch size, you need to take into consideration that SqlBulkCopy will need to not only insert the data, but also read AND send it - the last part is probably the reason why it works on your local SQL server, but not on Azure - it also means that, if you're working with a large dataset, that you will need to work with lower batch size, or a considerably higher BulkCopyTimeout setting, to allow each batch the chance to finish before reaching the timeout limit. 编辑以获得一些额外的说明:当您考虑批量大小时,您需要考虑到SqlBulkCopy不仅需要插入数据,还需要读取和发送它 - 最后一部分可能是它在本地工作的原因SQL服务器,但不在Azure上 - 它还意味着,如果您正在使用大型数据集,则需要使用较低的批处理大小或相当高的BulkCopyTimeout设置,以允许每个批处理完成之前达到超时限制。

You can read more on batch sizes in this post. 您可以在本文中阅读有关批量大小的更多信息。 What is the recommended batch size for SqlBulkCopy? SqlBulkCopy的推荐批量大小是多少?

Other option: 其他选择:
I was reading up on this, and it could simply be because your insert reaches a critical DTU (Database Transaction Unit, basically a measure of the servers combined resources) usage point. 我正在读这篇文章,它可能只是因为你的插入到达了一个关键的DTU(数据库事务处理单元,基本上是服务器组合资源的度量)使用点。

Performance levels are calibrated and governed to provide the needed resources to run your database workload up to the max limits allowed for your selected service tier/performance level. 性能级别经过校准和管理,以提供运行数据库工作负载所需的资源,达到所选服务层/性能级别允许的最大限制。 If your workload is hitting the limits in one of CPU/Data IO/Log IO limits, you will continue to receive the resources at the maximum allowed level, but you are likely to see increased latencies for your queries. 如果您的工作负载达到了CPU / Data IO / Log IO限制之一的限制,您将继续以最大允许级别接收资源,但您可能会看到查询的延迟增加 These limits will not result in any errors, but just a slowdown in your workload, unless the slowdown becomes so severe that queries start timing out . 这些限制不会导致任何错误,只会导致工作负载减慢 ,除非减速变得非常严重以至于查询开始超时

Taken from this link: https://azure.microsoft.com/da-dk/blog/azure-sql-database-introduces-new-near-real-time-performance-metrics/ 取自以下链接: https//azure.microsoft.com/da-dk/blog/azure-sql-database-introduces-new-near-real-time-performance-metrics/
Try starting the copy again while monitoring the DTU usage and see if it's on 100% for long(er) periods. 尝试在监视DTU使用情况时再次启动副本,并查看它是否在100(长期)时间内处于100%状态。 If that is the case, you might want to up your pricing tier scale for the database. 如果是这种情况,您可能需要提高数据库的定价层级别。

I've been dealing with Bulk insert during last two days, here is a Generic Bulk Insert Class that allows you to exclude some columns and, in case of a DbGeography Property is found it cast it to SqlGeography: 我在过去两天一直在处理批量插入,这里是一个通用批量插入类,允许你排除一些列,如果发现DbGeography属性,它将它转换为SqlGeography:

 public class BulkInsert<T> where T : class
{
    #region Fields

    private readonly LoggingService _logger = new LoggingService(typeof(BulkInsert<T>));
    private string _connectionString;
    private string _tableName;
    private IEnumerable<string> _excludedPropertyNames;
    private int _batchSize;
    private IEnumerable<T> _data;
    private DataTable _dataTable;

    #endregion

    #region Constructor

    public BulkInsert(
        string connectionString,
        string tableName,
        IEnumerable<T> data,
        IEnumerable<string> excludedPropertyNames,
        int batchSize = 1000)
    {
        if (string.IsNullOrEmpty(connectionString)) throw new ArgumentNullException(nameof(connectionString));
        if (string.IsNullOrEmpty(tableName)) throw new ArgumentNullException(nameof(tableName));
        if (data == null) throw new ArgumentNullException(nameof(data));
        if (batchSize <= 0) throw new ArgumentOutOfRangeException(nameof(batchSize));

        _connectionString = connectionString;
        _tableName = tableName;
        _batchSize = batchSize;
        _data = data;
        _excludedPropertyNames = excludedPropertyNames == null ? new List<string>() : excludedPropertyNames;
        _dataTable = CreateCustomDataTable();
    }

    #endregion

    #region Public Methods

    public void Insert()
    {
        using (var connection = new SqlConnection(_connectionString))
        {
            connection.Open();
            SqlTransaction transaction = connection.BeginTransaction();

            using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.Default | SqlBulkCopyOptions.KeepIdentity, transaction))
            {
                bulkCopy.BatchSize = _batchSize;
                bulkCopy.DestinationTableName = _tableName;

                // Let's fix tons of mapping issues by
                // Setting the column mapping in SqlBulkCopy instance:
                foreach (DataColumn dataColumn in _dataTable.Columns)
                {
                    bulkCopy.ColumnMappings.Add(dataColumn.ColumnName, dataColumn.ColumnName);
                }

                try
                {
                    bulkCopy.WriteToServer(_dataTable);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex.Message);
                    transaction.Rollback();
                    connection.Close();
                }
            }

            transaction.Commit();
        }
    }

    #endregion

    #region Private Helper Methods

    private DataTable CreateCustomDataTable()
    {
        PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
        var table = new DataTable();
        foreach (PropertyDescriptor prop in properties)
        {
            // Just include the not excluded columns
            if (_excludedPropertyNames.All(epn => epn != prop.Name))
            {                  
                if (prop.PropertyType.Name == "DbGeography")
                {
                    var type = typeof(SqlGeography);
                    table.Columns.Add(prop.Name, type);
                }
                else
                {
                    table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
                }
            }
        }
        foreach (T item in _data)
        {
            DataRow row = table.NewRow();
            foreach (PropertyDescriptor prop in properties)
            {
                // Just include the values in not excluded properties 
                if (_excludedPropertyNames.All(epn => epn != prop.Name))
                {
                    if (prop.PropertyType.Name == "DbGeography")
                    {                           
                        row[prop.Name] = SqlGeography.Parse(((DbGeography)prop.GetValue(item)).AsText()).MakeValid();
                    }
                    else
                    {
                        row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
                    }
                }
            }
            table.Rows.Add(row);
        }
        return table;
    }

    #endregion

}

And well, It has to be used like: 好吧,它必须像以下一样使用:

var myEntityBulk = new BulkInsert<MyEntity>(
    _mYConnectionString,
     "MyEntities", 
      myEntities, 
      new[] { "ObjectState","NavigationPropertyOne", "NavigationPropertyTwo" }
);
myEntityBulk.Insert();

I hope it helps, I'm quite sure it will... Yesterday I bulked with it &M entities in few minutes. 我希望它有所帮助,我很确定它会...我昨天在几分钟内就与它和M实体进行了大量合作。

Firstly, please make sure whether you could connect to Azure SQL database and do operation without timeout error if you just insert a record to database. 首先,如果您只是将记录插入数据库,请确保是否可以连接到Azure SQL数据库并执行没有超时错误的操作。

Secondly, please check whether you override the default timeout value by using the CommandTimeout property on the ObjectContext , and you could try to set 0 (that indicates no limit) for BulkCopyTimeout property. 其次,请检查是否通过使用ObjectContext上的CommandTimeout属性覆盖默认超时值,并且可以尝试为BulkCopyTimeout属性设置0(表示无限制)。

Besides, please make sure whether it hit limits of your Azure SQL database while you do bulk insert, you could try to change the service tier and performance level of your database . 此外,在批量插入时,请确保它是否达到Azure SQL数据库的限制,您可以尝试更改数据库的服务层和性能级别

When using SqlBulkCopy, depend on the Route-Trip-Time (distance) between your web server and the Azure SQL server, there are two parameters you need to be aware of: 使用SqlBulkCopy时,依赖于Web服务器和Azure SQL服务器之间的Route-Trip-Time(距离),您需要注意两个参数:

  1. BatchSize (for me I set batch size to 500) BatchSize(对我来说,我将批量大小设置为500)

  2. BulkCopyTimeOut: default is 30 seconds (I set to 60 secs) BulkCopyTimeOut:默认为30秒(我设置为60秒)

     using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection)) { bulkCopy.BatchSize = array.Length; bulkCopy.DestinationTableName = tempTableName; bulkCopy.BulkCopyTimeout = 60;...} 

With this configuration, my whole process of getting 20K+ events from EventStore to my Projection MicroService, then the microService Update/Write those 20K events to a Database took approximately 15 minutes (depends on the RTT- route trip time between the Event Store and the MicroService, and between Microservice and the database) 通过这种配置,我从EventStore到我的Projection MicroService获取20K +事件的整个过程,然后microService更新/将这些20K事件写入数据库花了大约15分钟(取决于事件存储和MicroService之间的RTT路由行程时间) ,以及Microservice和数据库之间)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM