我需要能够从SQL Server 2016数据库表中读取大约1亿条记录,然后为一个或多个列生成哈希并将这些记录写回到表中。
到目前为止,我已经尝试了几种解决方案,这些解决方案对于我们的需求而言太慢了。我正在具有i7-7700HQ处理器和32GB RAM的Dell XPS 15上对此进行测试。首先,我尝试将T-SQL HASHBYTES()函数与SHA1哈希一起使用,但这在1亿条记录测试数据集上花费了两个多小时。
当然,使用C#OleDbReader进行更新的速度甚至更慢,但瓶颈似乎在于编写记录。现在,我可以使用SqlBulkCopy将更改的记录复制到新的临时表中了,这比更新现有表快得多。但是仍然需要40分钟才能生成所有散列,而将记录写回需要花很多时间。理想情况下,我们希望整个操作在一个小时内完成。有谁知道我可以进一步优化。这是代码:
using (SqlConnection sourceConnection = new SqlConnection(connectionString))
{
sourceConnection.Open();
SqlDataAdapter adapter = new SqlDataAdapter("SELECT * FROM [ContosoRetailDW].[dbo].[FactInventory]", sourceConnection);
var dataTable = new System.Data.DataTable();
adapter.FillSchema(dataTable, System.Data.SchemaType.Source);
dataTable.Columns[2].DataType = typeof(string);
dataTable.Columns[2].MaxLength = 20;
adapter.Fill(dataTable);
for (int i = 0; i < dataTable.Rows.Count; i++)
{
byte[] toHash = Encoding.UTF8.GetBytes((string)dataTable.Rows[i][2]);
dataTable.Rows[i][2] = xxHash.CalculateHash(toHash).ToString("X");
}
using (SqlConnection destinationConnection = new SqlConnection(connectionString))
{
destinationConnection.Open();
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(destinationConnection.ConnectionString))
{
bulkCopy.BatchSize = 100000;
bulkCopy.DestinationTableName = "FactInventory_Hashed";
bulkCopy.WriteToServer(dataTable);
}
}
我已经尝试使用批量复制批处理大小,并且正在使用非常快速的哈希算法。
最佳答案
这或多或少是一个扩展注释。
从我的角度来看,这种方法有以下缺点:
[1]它将大量数据移出SQL Server SQL DB -> C# App -> SQL DB
,从而产生大量的网络I / O(它是网络I / O-是的,但仍然是很多I / O)
[2]它会将大量数据加载到内存中。如果此应用程序与SQL Server数据库引擎在同一台计算机上执行,则可能会在同一台计算机(包括SQL Server)上运行的其他应用程序上产生内存压力。
[3]它会在大表上生成两次扫描。第一个是SELECT ... FROM ...
,第二个是for (...) {...}
语句。
我会尝试什么?
(i)我将xxHash.CalculateHash
方法导入为CLR scalar function
(请参见https://docs.microsoft.com/en-us/sql/relational-databases/clr-integration-database-objects-user-defined-functions/clr-scalar-valued-functions)
CREATE ASSEMBLY CLRxxHash FROM 'D:\....\xxHash.dll';
GO
CREATE FUNCTION dbo.xxHash(@Source VARBINARY(8000)) RETURNS INT
AS EXTERNAL NAME CLRxxHash.......;
GO
然后我将使用以下T-SQL脚本
USE ContosoRetailDW
GO
INSERT dbo.FactInventory_Hashed (..., HashCol)
SELECT ..., dbo.xxHash(CONVERT(VARCHAR(8000), fi.Column2) AS HashCol
FROM dbo.FactInventory AS fi
警告:如果SQL Server将使用并行机制,那么这应该在大表上表现得足够好(至少)。由于具有标量功能,SQL Server可能会生成串行计划。
(ii)如果以上
INSERT ... SELECT
语句的执行计划是串行的,那么我将使用批处理:一个连接将处理
IDs
和1
之间的所有带有1.000.000
的行INSERT dbo.FactInventory_Hashed (..., HashCol)
SELECT ..., dbo.xxHash(CONVERT(VARCHAR(8000), fi.Column2) AS HashCol
FROM dbo.FactInventory AS fi
WHERE fi.ID BETWEEN 1 AND 1000000
另一个连接将处理
IDs
和1.000.001
之间的所有2.000.000
INSERT dbo.FactInventory_Hashed (..., HashCol)
SELECT ..., dbo.xxHash(CONVERT(VARCHAR(8000), fi.Column2) AS HashCol
FROM dbo.FactInventory AS fi
WHERE fi.ID BETWEEN 1000001 AND 2000000
等等。
关于c# - C#/SQL Server 2016-更新大约1亿条具有哈希值的记录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44242235/