哈希键列的隐式转换导致插入速度非常慢

Posted

技术标签:

【中文标题】哈希键列的隐式转换导致插入速度非常慢【英文标题】:Implicit conversion on hash key column is causes very slow insert 【发布时间】:2021-09-13 09:53:59 【问题描述】:

我正在尝试将一些数据插入到一些插入时间过长的阶段表中。例如,一个包含多达 600000 条记录的表需要将近一个小时才能完成。在查询的选择部分,我们正在创建列的哈希,稍后用于更改检测。由于我们使用的数据仓库方法 DataVault,我们无法删除更改哈希。当我查看执行计划时,我看到表达式中的类型转换警告。示例如下所示。

<Warnings>
              <PlanAffectingConvert ConvertIssue="Cardinality Estimate" Expression="CONVERT(varchar(max),[load].[load_acbs_loan_product_dimension].[product_month],0)" />

我还包含了我们正在使用的插入语句的示例:

      INSERT INTO [stage_acbs_balance_category_dimension]
      ( hk_h_balance_category_dimension
      , balance_category_key
      , balance_category_code
      , balance_category_description
      , balance_class_code
      , balance_class_description
      , user_define_code_1
      , user_define_code_2
      , user_define_code_3
      , user_define_code_4
      , user_define_code_5
      , bal_cat_short_name
      , include_bal_in_tax_reporting
      , include_in_billings_statements
      , include_in_past_due_reporting
      , dss_change_hash_acbs_balance_category_dimension_lroc
      , dss_record_source
      , dss_load_date
      , dss_create_time)
      SELECT  CAST(HASHBYTES('sha2_256',
               COALESCE(CAST(load_acbs_balance_category_dimension.balance_category_key AS VARCHAR(MAX)),'null')
               ) AS BINARY(32)) AS hk_h_balance_category_dimension 
           , load_acbs_balance_category_dimension.balance_category_key AS balance_category_key 
           , load_acbs_balance_category_dimension.balance_category_code AS balance_category_code 
           , load_acbs_balance_category_dimension.balance_category_description AS balance_category_description 
           , load_acbs_balance_category_dimension.balance_class_code AS balance_class_code 
           , load_acbs_balance_category_dimension.balance_class_description AS balance_class_description 
           , load_acbs_balance_category_dimension.user_define_code_1 AS user_define_code_1 
           , load_acbs_balance_category_dimension.user_define_code_2 AS user_define_code_2 
           , load_acbs_balance_category_dimension.user_define_code_3 AS user_define_code_3 
           , load_acbs_balance_category_dimension.user_define_code_4 AS user_define_code_4 
           , load_acbs_balance_category_dimension.user_define_code_5 AS user_define_code_5 
           , load_acbs_balance_category_dimension.bal_cat_short_name AS bal_cat_short_name 
           , load_acbs_balance_category_dimension.include_bal_in_tax_reporting AS include_bal_in_tax_reporting 
           , load_acbs_balance_category_dimension.include_in_billings_statements AS include_in_billings_statements 
           , load_acbs_balance_category_dimension.include_in_past_due_reporting AS include_in_past_due_reporting 
           , CAST(HASHBYTES('SHA2_256',
               COALESCE(CAST(load_acbs_balance_category_dimension.balance_category_code AS VARCHAR(MAX)),'null') +'||'+
               COALESCE(CAST(load_acbs_balance_category_dimension.balance_category_description AS VARCHAR(MAX)),'null') +'||'+
               COALESCE(CAST(load_acbs_balance_category_dimension.balance_class_code AS VARCHAR(MAX)),'null') +'||'+
               COALESCE(CAST(load_acbs_balance_category_dimension.balance_class_description AS VARCHAR(MAX)),'null') +'||'+
               COALESCE(CAST(load_acbs_balance_category_dimension.user_define_code_1 AS VARCHAR(MAX)),'null') +'||'+
               COALESCE(CAST(load_acbs_balance_category_dimension.user_define_code_2 AS VARCHAR(MAX)),'null') +'||'+
               COALESCE(CAST(load_acbs_balance_category_dimension.user_define_code_3 AS VARCHAR(MAX)),'null') +'||'+
               COALESCE(CAST(load_acbs_balance_category_dimension.user_define_code_4 AS VARCHAR(MAX)),'null') +'||'+
               COALESCE(CAST(load_acbs_balance_category_dimension.user_define_code_5 AS VARCHAR(MAX)),'null') +'||'+
               COALESCE(CAST(load_acbs_balance_category_dimension.bal_cat_short_name AS VARCHAR(MAX)),'null') +'||'+
               COALESCE(CAST(load_acbs_balance_category_dimension.include_bal_in_tax_reporting AS VARCHAR(MAX)),'null') +'||'+
               COALESCE(CAST(load_acbs_balance_category_dimension.include_in_billings_statements AS VARCHAR(MAX)),'null') +'||'+
               COALESCE(CAST(load_acbs_balance_category_dimension.include_in_past_due_reporting AS VARCHAR(MAX)),'null')
               ) AS BINARY(32)) AS dss_change_hash_acbs_balance_category_dimension_lroc 
           , load_acbs_balance_category_dimension.dss_record_source AS dss_record_source 
           , load_acbs_balance_category_dimension.dss_load_date AS dss_load_date 
           , getdate() AS dss_create_time 
      FROM [load_acbs_balance_category_dimension] load_acbs_balance_category_dimension

我正在寻找一种方法来摆脱隐式转换并让插入执行。我最初想让列成为计算的持久散列,但散列是不确定的。任何想法将不胜感激。

【问题讨论】:

product_month 甚至没有在您的示例查询中提及,因此您的时间可能不会浪费在隐式转换上。 SHA2-256 并不便宜,通常在生成结果哈希之前对输入数据执行 64 轮位混洗。您是否考虑过预先计算 SHA2-256 哈希并将它们作为列包含在您的导入数据文件中? 您好,我刚刚使用 product_month 作为执行计划的警告示例。我们总共进行了 7 次插入操作。包含产品月份的插入内容太大而无法放入论坛,因此我选择了一个较小的查询作为示例。数据来自 Oracle 数据库。您的意思是预先计算 sha2 -256 哈希并将它们存储在 staging 中吗? 为什么不直接使用rowversion 进行变更检测?或者至少切换到MD5 以获得更好的性能 Wherescape 用于生成数据 Vault,因此默认情况下必须使用散列。我将 varchar(max) 切换为 varbinary(max) 并获得了显着的性能提升。但是确实使用另一种 Hash 方法可能会获得更好的性能 【参考方案1】:

我选择将 VARCHAR(MAX) 更改为 VARBINARY(MAX,这显着提升了性能,从 1 小时缩短到 40 秒。我还希望更改散列算法。

【讨论】:

连接多个 MAX 字段可能会导致隐式转换问题。将这些转换更改为更合适的长度也可能会提高性能。您也可以尝试 CONCAT_WS 将所有字段作为数组传递并转换为字符值,但这会忽略空值。

以上是关于哈希键列的隐式转换导致插入速度非常慢的主要内容,如果未能解决你的问题,请参考以下文章

为啥首先允许指针从非常量到常量的隐式转换?

Mysql中的隐式转换

为啥 Java 和 C# 没有到布尔值的隐式转换?

无法在 varbinary(max) 中插入空值并出现错误:不允许从数据类型 nvarchar 到 varbinary(max) 的隐式转换

从非常量到常量模板参数的隐式转换在 boost::optional 中不起作用

scala学习笔记-隐式转换与隐式参数(18)