【问题标题】:Adding unique index to a MySQL text field string using hashing in Ruby在 Ruby 中使用散列向 MySQL 文本字段字符串添加唯一索引
【发布时间】:2018-10-12 20:37:03
【问题描述】:

我有一个 Rails 应用程序,其模型(表)有 1,500,000 条记录,文本字段大小从 50 到 8000 个字符不等。

我需要确保文本字段是唯一的。由于我不能在文本字段上使用 MySQL 唯一索引,我的解决方案是将我的文本字段转换为哈希(使用 Digest::SHA256.hexdigest)并将此哈希保存到名为“body_hash”的 varchar 字段中。然后在该字段上添加一个唯一索引。

问题:

  • 我想知道 Rails 中是否为此提供了内置解决方案? (而不是我重新发明轮子 - 到目前为止我找不到任何东西。)
  • 在此使用另一种哈希算法是否比 Digest::SHA256.hexdigest 更好?

【问题讨论】:

    标签: mysql ruby-on-rails hash unique


    【解决方案1】:

    Rails 解决方案是一个before_save 挂钩。您也可以使用数据库触发器来执行此操作,但这会更加混乱和脆弱。

    SHA256 在这里可能没问题,因为它是基于 SHA2 的。发生碰撞的机会应该非常小。

    对唯一索引使用一致长度的哈希而不是文本实际上是一个好主意,因为 MySQL 的索引对于更长的字符串会变得更大。对于索引引擎来说,短的十六进制字符串更容易处理,并且仍然提供您想要的唯一性约束。

    MySQL 的独特约束实际上也提供了一种排序机制,这就是痛苦的根源,但如果您不关心排序哈希解决方案是一个很好的选择。

    【讨论】:

    • before_save 钩子的一个缺点是它不能保护直接在数据库中进行的插入/更新
    • @oldp 有时这是一项功能,有时这是一项责任。当您需要绕过此行为时,至少您可以选择是否在应用程序层中完成。如果需要,在 DB 层执行此操作可能更难规避。
    【解决方案2】:

    您可能需要考虑在数据库本身中生成此校验和/摘要。这将更快地回填现有数据的值,而不是在 Ruby 中处理。

    结合 MySQL 的 CREATE_DIGEST 来填充 body_digest 列:

    CREATE_DIGEST('SHA512', 'The quick brown fox');
    

    https://dev.mysql.com/doc/refman/8.0/en/enterprise-encryption-functions.html#function_create-digest

    还有一个BEFORE INSERT / BEFORE UPDATE 触发器来设置这个校验和值:

    https://dev.mysql.com/doc/refman/8.0/en/trigger-syntax.html

    值得一提的是,有一个用于在 ActiveRecord 模型上声明数据库触发器的 Ruby 库: https://github.com/jenseng/hair_trigger

    【讨论】:

      猜你喜欢
      • 2018-10-05
      • 1970-01-01
      • 1970-01-01
      • 2017-12-21
      • 1970-01-01
      • 2012-11-13
      • 2013-05-07
      • 2014-01-08
      • 2014-08-30
      相关资源
      最近更新 更多