我需要能够从 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);
}
}
我已经尝试过使用批量复制批量大小,并且正在使用非常快的哈希算法。
这个问题很老了,没有人回答。检查一下您在代码中做错的一些事情可能会有所帮助。
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)