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

我无法使用C#webapi将大量数据插入Azure SQL Server数据库

考虑

我想在SQL中插入60K>数据。 在我的本地SQL服务器中没有问题,但在Azure SQL中它的连接超时

我的方法:(所有都在本地sql server中工作,但不在Azure sql server中)

1)尝试使用EF逐个插入记录(10000次约10分钟,大部分超时)

2)尝试使用批量插入扩展和EF 3)在SqlBulkCopy中尝试

4)尝试增加连接字符串中的连接超时

5)尝试在Dbcontext中增加命令时间。

exceptionStackTrace

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) 

是否有任何解决方案或Azure中的任何配置要更改?

更新

用于批量插入的代码

  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) { } } } 

我建议你将sqlBulkCopy.BatchSize设置为合理的数量,而不是一次性插入所有内容。 根据您要插入的数据,尝试从10.000开始,向上或向下工作,直到您对性能满意为止。

编辑以获得一些额外的说明:当您考虑批量大小时,您需要考虑到SqlBulkCopy不仅需要插入数据,还需要读取并发送它 – 最后一部分可能是它在本地工作的原因SQL服务器,但不在Azure上 – 它还意味着,如果您正在使用大型数据集,则需要使用较低的批量大小或相当高的BulkCopyTimeout设置,以允许每个批处理之前完成达到超时限制。

您可以在本文中阅读有关批量大小的更多信息。 SqlBulkCopy的推荐批量大小是多少?

其他选择:
我正在读这篇文章,这可能只是因为你的插入到达了一个关键的DTU(数据库事务单元,基本上是服务器组合资源的衡量标准)使用点。

性能级别经过校准和管理,以提供运行数据库工作负载所需的资源,达到所选服务层/性能级别允许的最大限制。 如果您的工作负载达到了CPU / Data IO / Log IO限制之一的限制,您将继续以最大允许级别接收资源,但您可能会看到查询的延迟增加 。 这些限制不会导致任何错误,只会导致工作负载减慢 ,除非减速变得非常严重以至于查询开始超时

取自以下链接: https : //azure.microsoft.com/da-dk/blog/azure-sql-database-introduces-new-near-real-time-performance-metrics/
尝试在监视DTU使用情况时再次启动副本,并查看它是否在100(长期)时间内处于100%状态。 如果是这种情况,您可能希望提高数据库的定价层级。

我在过去两天一直处理批量插入,这里是一个通用批量插入类,允许你排除一些列,如果找到DbGeography属性,它将它转换为SqlGeography:

  public class BulkInsert where T : class { #region Fields private readonly LoggingService _logger = new LoggingService(typeof(BulkInsert)); private string _connectionString; private string _tableName; private IEnumerable _excludedPropertyNames; private int _batchSize; private IEnumerable _data; private DataTable _dataTable; #endregion #region Constructor public BulkInsert( string connectionString, string tableName, IEnumerable data, IEnumerable 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() : 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 } 

好吧,它必须像以下一样使用:

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

我希望它有所帮助,我很确定它会……我昨天在几分钟内就用它和M实体进行了充实。

首先,如果您只是将记录插入数据库,请确保是否可以连接到Azure SQL数据库并执行没有超时错误的操作。

其次,请检查是否通过使用ObjectContext上的CommandTimeout属性覆盖默认超时值,并且可以尝试为BulkCopyTimeout属性设置0(表示无限制)。

此外,在批量插入时,请确保它是否达到了Azure SQL数据库的限制,您可以尝试更改数据库的服务层和性能级别 。

使用SqlBulkCopy时,取决于Web服务器和Azure SQL服务器之间的Route-Trip-Time(距离),您需要注意两个参数:

  1. BatchSize(对我来说,我将批量大小设置为500)

  2. BulkCopyTimeOut:默认为30秒(我设置为60秒)

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

通过这种配置,我从EventStore到我的Projection MicroService获取20K +事件的整个过程,然后microService更新/将这些20K事件写入数据库花了大约15分钟(取决于事件存储和MicroService之间的RTT路由行程时间) ,以及Microservice和数据库之间)