SHA1摘要作为主键?

3
我将把文件名和其他细节存储在一个表中,计划使用文件名的SHA1哈希作为主键。
Q1. SHA1 PK不会是递增/递减的数字。因此,在维护/搜索并在该键上建立索引时,数据库是否需要更多资源?如果我决定将其保留在数据库中作为40个字符值。
Q2. 我在这里读到:https://dev59.com/hnRB5IYBdhLWcg3weHHx#614483 将数据存储为二进制(20)字段。有人可以就此向我提供建议吗:
a) 我必须将此列创建为:类型=整数,长度=20,COLLATION=二进制,ATTRIBUTES=二进制吗?
b) 如何在MySQL或Perl中转换sha1值以存储到表中?
c) 这20个字符值存在重复的危险吗?
要求是按文件名搜索表格。用户提供文件名,我搜索表格,如果文件名不存在,则添加它。因此,要么在varchar(100)文件名字段上进行索引,要么生成一个带有文件名SHA1的列——希望对于MySql来说,这比对varchar字段进行索引更容易。此外,我可以从我的程序中使用SHA1值针对SHA1列进行搜索。怎么样?我选择PK,因为DBIx喜欢使用PK。对于系统而言,PK或INDEX+UNIQ的开销相同(所以我想)。

1
为什么要使用哈希作为主键而不是具有传统主键(Guid/int)的唯一列? - Jared
另外,请记住哈希值是不唯一的。无限数量的数据输入映射到相同的哈希值(虽然我不知道对于特定的哈希算法,是否每个可能的哈希值都是如此。对于某些哈希值,可能只有有限数量的数据输入)。 - Christian Stieber
我这里有什么遗漏吗?我假设你的文件名已经带有位置信息了?由于名称/位置应该是唯一的,所以只需使用已经自动递增的主键,并在文件名和位置上使用单独的唯一键即可。 - Ben
5个回答

0

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

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

不,不要将其创建为整数类型。最大整数为128位,无法容纳整个内容。尽管您可以将其截断为128位而没有真正的伤害。

使用更简单的哈希更好。您可以冒险忽略冲突,但是如果您正确地处理它们,您必须处理它们。


根本没有理由使用哈希。哈希会发生冲突。你不想在主键中有冲突。即使冲突很罕见,也是不可取的。 - Tomalak

0
如果我决定将其保留在数据库中作为40个字符的值。
使用字符序列作为键会因明显原因而降低性能。
此外,PK应该是唯一的。虽然你最终可能不太可能遇到冲突(理论上使用它来创建PK的函数似乎不合适)。
此外,任何知道文件名和您使用的哈希的人都将知道您所有的数据库ID。我不确定这是否是不考虑的事情。

0

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

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

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

机会是2 ^(8 * 20)中的1。 1 in 1.46 * 10 ^ 48 ...或14615016373309029182036848327163 * 10 ^ 18中的1。因此,那种情况的机会非常非常小。


Ty.您好,我想了解构建列所提供的完整细节(我正在使用phpMyAdmin处理我的数据库)。 - rajeev
你需要创建一个类型为BINARY(40)的列;你可以选择这个作为列类型(也许不可用)...或者你需要编写自己的SQL并使用phpMyAdmin的EXECUTE SQL命令。 - SDwarfs
在您的源代码中,您可以使用UNHEX('....SHA1 HASH.....')将数据从HEX(仅包含0123456789abcdef)转换为二进制(更紧凑,大小为1/2)。 - SDwarfs
如果您只需要查找“重复项”,则使用md5()进行哈希可能更容易。如果您将普通INT用作主键,则仅需要对上传进行缓慢的哈希查找。对于下载,您可以使用链接本身中提供的ID。如果您不希望人们只需将ID从10002更改为10003并获取下一个文件,则可以将哈希放入链接中,并检查哈希是否与文件条目匹配。为简单起见,您可以将哈希存储为字符串列(例如VARCHAR(40)或对于md5 VARCHAR(32))。 - SDwarfs

0

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

如果你采用我的建议或者坚持原计划,那么你应该知道多个字符串可能映射到同一个文件名/路径。这两个版本将具有不同的哈希值/通过唯一性约束,但实际上都指向同一个文件。这取决于操作系统,可能对你造成问题,这只是需要注意的事情。


0

好的,那么在文件名上使用一个非常短的哈希值并接受冲突。对其使用整数类型(这样更快!)。例如,您可以使用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()函数。


虽然我同意并感谢大家的时间和努力,但我认为这个建议试图解决真正的问题。我喜欢这个建议,因为它通过文件名查找表来减少资源使用和编程步骤的想法。 - rajeev
请注意,通过哈希匹配文件名是区分大小写的。如果您想要不区分大小写,请在哈希之前将字符串转换为小写(或大写)。 - SDwarfs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接