【问题标题】:SHA1 sum as a primary key?SHA1总和作为主键?
【发布时间】:2012-08-15 07:20:56
【问题描述】:

我打算将文件名和其他详细信息存储在一个表中,我打算使用文件名的 sha1 哈希作为 PK。

  • 第一季度。 SHA1 PK 不会是一个顺序增加/减少的数字。 那么,数据库是否会消耗更多资源 维护/search_into 并索引该键?如果我决定将其作为 40 个字符值保存在数据库中。

  • 第二季度。我在这里读到: https://stackoverflow.com/a/614483/986818 将数据存储为 二进制(20)字段。有人可以在这方面给我建议吗:

  • a) 我必须将此列创建为:TYPE=integer, LENGTH=20,
    COLLATION=binary,ATTRIBUTES=binary?
  • b) 如何将 MySQL 或 Perl 中的 sha1 值转换为存储到 桌子?
  • c) 这 20 个字符的值是否存在重复的危险?

**

---------更新-------------

**

要求是在文件名上搜索表。用户提供文件名,我去搜索表,如果文件名不存在,则添加它。因此,要么我在 varchar(100) 文件名字段上编制索引,要么生成一个带有文件名 sha1 的列 - 希望与索引 varchar 字段相比,为 MySql 编制索引会更容易。我也可以使用我的程序中的 sha1 值对 sha1 列进行搜索。说啥?主键或只是索引键:我选择 PK 因为 DBIx 喜欢使用 PK。并且 PK 或 INDEX+UNIQ 对系统来说将是相同数量的开销(所以我认为)

【问题讨论】:

  • 为什么将哈希用作 PK 而不仅仅是具有传统 PK (Guid/int) 的唯一列?
  • 另外,请记住哈希不是唯一的。无限数量的数据输入映射到相同的哈希值(尽管我不知道对于特定的哈希算法,这是否是每个可能的哈希值的情况。可能有一些只有有限数量的数据输入对于某些哈希值)。
  • 我在这里遗漏了什么吗?我假设您的文件名附有位置?由于名称/位置应该是唯一的,只需使用已经自动递增的 pk,并在文件名和位置上使用单独的唯一键。

标签: mysql sha1


【解决方案1】:

这里没有理由使用加密安全哈希。相反,如果您这样做,请使用普通哈希。见这里:https://softwareengineering.stackexchange.com/questions/49550/which-hashing-algorithm-is-best-for-uniqueness-and-speed

哈希值不是 40 字符值!这是一个 160 位的数字,您应该以这种方式存储它(作为 20 字符的二进制字段)。编辑:我看到你在评论 2 中提到了这一点。是的,你绝对应该这样做。但是我不能告诉你怎么做,因为我不知道你使用的是什么编程语言。 Edit2:我看到它是 perl - 抱歉,我不知道如何在 perl 中转换它,但请寻找“打包”函数。

不,不要将其创建为整数类型。最大整数是 128 位,不包含整个内容。尽管您实际上可以将其截断为 128 位而不会造成真正的伤害。

无论如何,最好使用更简单的哈希。你可以冒险并忽略碰撞,但如果你做得好,你就必须处理它们。

【讨论】:

  • 根本没有理由使用哈希。哈希有冲突。您不想在主键中发生冲突。即使碰撞“非常罕见”也不行。
【解决方案2】:

如果我决定将它作为 40 个字符值保存在数据库中。

使用字符序列作为键会降低性能,原因很明显。

PK 也应该是唯一的。尽管可能不太可能最终发生冲突(理论上,将其用于创建 PK 的函数似乎不合适。
此外,任何知道您使用的文件名和哈希的人都会知道您的所有数据库 ID。我不确定这是否值得考虑。

【讨论】:

    【解决方案3】:

    Q1:是的,它需要建立一个 B-Tree 节点,其中不仅包含 1 个整数(4 个字节),还包含一个 CHAR(40)。只要 INDEX 保存在内存中,速度就会大致相同。由于条目大约大 10 倍,因此您需要 10 倍以上的内存才能将其保存在内存中。但是:无论如何,您可能都想通过哈希查找。因此,您需要将其作为主键或索引。

    Q2:只需创建一个表字段,如 CREATE TABLE test (ID BINARY(40), ...);稍后您可以使用 INSERT INTO test (ID, ..) VALUES (UNHEX('4D7953514C'), ...);

    -- 关于:这 20 个字符的值是否存在重复的危险?

    机会是 1 in 2^(8*20)。 1 in 1,46 * 10^48 ... 或 1 的 14615016373309029182036848327163*10^18。所以这个机会是非常非常v..不可能的。

    【讨论】:

    • Ty.Hi,我想知道有关为构建列提供的值的完整详细信息(我正在使用 phpMyAdmin 处理我的数据库)。
    • 您需要创建一个 BINARY(40) 类型的列;您可以选择它作为列类型(可能不可用)...或者您需要编写自己的 SQL 并使用 phpMyAdmin 的 EXECUTE SQL 命令。
    • 在您的源代码中,您可以使用 UNHEX('....SHA1 HASH.....') 将数据从 HEX(仅包含 0123456789abcdef)转换为二进制(更紧凑,大小的 1/2)。
    • 如果您只需要查找“双打”,则使用 md5() 进行散列可能会更容易。如果您使用普通的 INT 作为主键,则只需要慢速哈希查找来进行上传。对于下载,您可以使用链接本身提供的 ID。如果您不希望人们只是将 ID 从 10002 更改为 10003 并获取下一个文件,您也可以将哈希放入链接中并检查哈希是否与文件条目匹配。为简单起见,您可以将哈希原样存储到字符串列中(例如 VARCHAR(40) 或 md5 VARCHAR(32))。
    【解决方案4】:

    我会坚持使用标准的自动递增整数作为主键。如果文件名的唯一性很重要(听起来确实如此),那么您可以在文件名本身或文件名的某些派生的规范版本上添加 UNIQUE 约束。大多数语言/框架都有某种方法来获取路径的规范版本(相对于绝对、标准化大小写等)。

    如果您实施我的建议或追求您的原始计划,那么您应该知道多个字符串可以映射到相同的文件名/路径。两个版本都有不同的哈希值/通过唯一性约束,但实际上都引用同一个文件。这取决于操作系统,对您来说可能是也可能不是问题。只是需要记住的一点。

    【讨论】:

      【解决方案5】:

      好的,然后在文件名上使用非常短的散列并接受冲突。为它使用整数类型(那要快得多!!!)。例如。您可以使用 md5(filename) 然后使用前 8 个字符并将它们转换为整数。 SQL 可能如下所示:

      CREATE TABLES files (
        id INT auto_increment,
        hash INT unsigned,
        filename VARCHAR(100),
      
        PRIMARY KEY(id),
        INDEX(hash)
      );
      

      那么你可以使用:

      SELECT id FROM files WHERE hash=<hash> AND filename='<filename>';
      

      然后将哈希用于排序大多数其他文件(通常是所有其他文件),然后文件名用于从少数哈希冲突中选择正确的条目。

      为了在 perl 中生成整数哈希键,我建议使用 md5() 和 pack()。

      【讨论】:

      • 虽然我同意所有人,但感谢大家的时间和努力。我认为这个建议试图解决真正的问题。我喜欢这个建议,因为它解决了通过文件名在表中查找文件名时最小化资源使用和编程步骤的想法。
      • 请注意,通过哈希匹配文件名是区分大小写的。如果您希望它不区分大小写,请在散列之前将字符串转换为小写(或大写)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-01-27
      • 2012-06-29
      • 1970-01-01
      • 2020-08-29
      • 2018-05-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多