c# - C#/SQL Server 2016-更新大约1亿条具有哈希值的记录

标签 c# .net sql-server hash

我需要能够从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语句的执行计划是串行的,那么我将使用批处理:

一个连接将处理IDs1之间的所有带有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


另一个连接将处理IDs1.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/

相关文章:

c# - Python 的 hash.digest() 使用什么 c# 等效编码?

c# - 在没有事件源注册的情况下写入 Windows 应用程序事件日志

.net - WinForm中如何清除用户控件的内存?

sql-server - SQL Server不同设计查询性能

c# - 更改 IE 用户代理

c# - 如何在单击按钮时获取 ListView 行的索引

c# - NaoQi 和 Leap 问题 : An unhandled exception of type 'System.BadImageFormatException' . .. 并且无法加载文件或程序集

c# - 如何使 C# 类的 Object 属性可选?

SQL 服务器 : joining a table by multiple parameters

mysql - 通过链接的 MySQL 数据库更新 MySQL 数据库的 MSSQL