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

问题描述 投票:0回答:1

我需要能够从 SQL Server 2016 数据库表中读取大约 1 亿条记录,然后为一个或多个列生成哈希值并将这些记录写回到表中。

到目前为止,我已经尝试了几种解决方案,但它们对于我们的需求来说太慢了。我正在配备 i7-7700HQ 处理器和 32GB RAM 的 Dell XPS 15 上对此进行测试。首先,我尝试使用带有 SHA1 哈希的 T-SQL HASHBYTES() 函数,但这在 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);
    }
}

我已经尝试过使用批量复制批量大小,并且正在使用非常快的哈希算法。

c# .net sql-server hash
1个回答
0
投票

这个问题很老了,没有人回答。检查一下您在代码中做错的一些事情可能会有所帮助。

1。当您只需要其中一列或几列时,请勿从表中提取所有列。

而不是

SELECT *  FROM

SELECT <recordId>, <columnToHash> FROM YourTable

2。分页。

而不是一次性读取 1 亿条记录,这真的会杀死你的 sql server 数据库。尝试分页:

SELECT recordId, yourColumn
FROM YourTable
ORDER BY recordId
OFFSET 0 ROWS
FETCH NEXT 100 ROWS ONLY;

要检索下一页,您可以将 OFFSET 值增加 100,如下所示:

SELECT recordId, yourColumn
FROM YourTable
ORDER BY recordId
OFFSET 100 ROWS
FETCH NEXT 100 ROWS ONLY;

3.提取块中的记录(通过分页),对列进行哈希处理,按 recordId 批量更新并提交事务。

在每次迭代中提交事务非常重要。这将避免导致 sql server 保持事务打开时间过长,从而阻止其他进程从该表读取和写入。 您可以为此创建一个 SP。与此类似的东西:

-- Create the User-Defined Table Type
CREATE TYPE MyHashedValueType AS TABLE
(
    RecordId INT,
    HashedValue NVARCHAR(20)
);

CREATE PROCEDURE UpdateRecords
(
    @RecordsToUpdate MyHashedValueType READONLY
)
AS
BEGIN
    BEGIN TRY
        -- Start a transaction
        BEGIN TRANSACTION;
        
        UPDATE yt
        SET yt.HashedValue = tt.HashedValue 
        FROM YourTable yt
        JOIN @RecordsToUpdate tt ON yt.RecordId = tt.RecordId;
       
        -- Commit the transaction if all updates were successful
        COMMIT TRANSACTION;
    END TRY
    BEGIN CATCH
        -- If an error occurs, rollback the transaction
        ROLLBACK TRANSACTION;

        -- Handle or log the error here
        -- Example
    DECLARE @ErrorMessage NVARCHAR(4000);
    DECLARE @ErrorNumber INT;

    SET @ErrorMessage = 'An error occurred while updating records.';
    SET @ErrorNumber = 50001; -- You can choose an appropriate error number

    THROW @ErrorNumber, @ErrorMessage, 0;
    END CATCH;
END;

4。并行。

一旦成功实现上述算法,您就可以通过偏移量并行化并将每个任务分配给不同的线程。 这是一种伪代码:

pageSize = 100;
offsets = Math.Ceiling ( (SELECT count(*) from YourTable) / pageSize );

并行迭代您的偏移量并执行以下操作:

ProcessOffset(offsets[i] * pageSize)

您可以尝试这个库: https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/how-to-write-a-parallel-for-loop-with-thread-local-variables

© www.soinside.com 2019 - 2024. All rights reserved.