【问题标题】:Storing C# GetHashCode() in DB is Unreliable [duplicate]在数据库中存储 C#GetHashCode() 是不可靠的 [重复]
【发布时间】:2011-03-18 16:31:14
【问题描述】:

可能重复:
How do I create a HashCode in .net (c#) for a string that is safe to store in a database?

我计划在我的数据库中存储数十万个 URL。我的 UrlInfo 表中的每一行都是不可变的,而 URL 本身就是逻辑主键。由于 URL 可能相当长,因此我决定对 URL 进行哈希处理,以便在添加新行时快速找到可能的匹配项。哈希不是我真正的关键,只是一种快速找到可能匹配项的方法。此外,我使用每个域的 RegEx 模式,将 URL 的本质提炼成可以与其他 URL 进行比较的东西。我将 RegEx 的结果也存储为哈希,我不担心它是否会产生重复。

一切都很顺利,直到我了解到 C# 的 string.GetHashCode() 方法(我一直用来散列事物)不能保证在 .Net 的实现中是唯一的。当我尝试将哈希函数从 ASP.Net 移动到 SQLServer CLR 代码时,我注意到了这一点。 Web 应用程序使用 .Net 4.0,我了解到,SQLServer 2008 R2 使用 .Net 3.5。他们为相同的字符串产生了单独的哈希结果,所以现在我需要摆脱使用 string.GetHashCode() 因为当我将应用程序升级到未来版本的 .Net 时我不想担心这种变化。

所以,问题:

  1. 因为我在我的数据库中存储了一个哈希值,所以我的架构有异味吗?还有更好的方法吗?显然微软不希望我存储哈希结果!

  2. 有人可以推荐一个好的 C# 替换算法来散列字符串吗?我看到了Jon's here,但不确定如何修改以适用于字符串(使用 ascii 代码循环遍历每个字符?)。

  3. 有没有比使用哈希算法更好的字符串压缩算法?

谢谢

很多人的反应都很棒。非常感谢!!!

【问题讨论】:

  • 我知道这并不能回答您的问题,但是对于长期使用 URL 作为密钥会非常谨慎。我的经验是,它们会因多种原因而改变。
  • 他们可以,尽管 URI 本质上是分布式全局系统的关键。只要您使用它可以处理不断变化的事情,它在许多情况下都是一个好方法。遗憾的是,它在拥有自己的哈希索引的数据库中效果更好。
  • 嘿@n8wrl,对于我的应用程序来说,它确实是我感兴趣的 URI 本身,不一定代表它的含义。我正在使用我的辅助 RegEx/hash 策略来缓解 URL 可能变化的问题。因此,例如,我可以匹配这两个字符串,因为域 abc 的 RegEx 仅使用参数 a:http://www.abc.com?/a=5 www.abc.com?a=b&pagecount=2

标签: c# sql-server regex hash gethashcode


【解决方案1】:

您总是可以使用 MD5 哈希值,这相对较快:

public string GetUrlHash(string url) {

    byte[] hash = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(url));

    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < hash.Length; i++) {
        sb.Append(hash[i].ToString("X2"));
    }

    return sb.ToString();
}

这样称呼它:

Console.WriteLine(this.GetUrlHash("http://stackoverflow.com/questions/5355003/storing-c-gethashcode-in-db-is-unreliable"));

得到:

> 777BED7F83C66DAC111977067B4B4385

从唯一性的角度来看,这应该是相当可靠的。 MD5 现在对于密码应用程序来说是不安全的,但你在这里没有这个问题。

唯一的问题是使用这样的字符串作为表上的主键可能会出现问题,性能方面。

您可以做的另一件事是使用 URL 缩短器方法:使用数据库的序列生成功能,并将值(确保使用 LONG 或 BIGINT 的等价物!)转换为类似 Base36 的值,这会给您一个很好的, 简洁的字符串。

【讨论】:

  • 感谢您的回答。今天经过深思熟虑,我最终得到了您建议的 MD5。我考虑过 (1) 一个扁平的 VARBINARY 字段和 (2) 一个 BASE36 压缩,但 MD5 是众所周知的,SQLServer 通过其 HASHBYTES 函数对其进行原生支持。我很高兴知道我的哈希值将在我的不可变对象的生命周期内保持一致。
  • 应该使用 Encoding.UTF8 而不是 Encoding.ASCII,即给定非 ASCII 字符的“不完整”和“不可逆”,您应该使用 Encoding.UTF8
  • @xanatos true,尤其是当 URL 包含扩展字符时。我更新了 sn-p。
【解决方案2】:

这里有人问过类似的问题:

How do I create a HashCode in .net (c#) for a string that is safe to store in a database?

它可能证明可以解决您的问题。

【讨论】:

  • 谢谢@Adam,不知怎的,我在stackoverflow搜索中错过了那个。是的,它证实了我的想法。我想我的架构仍然是合理的,但我需要找到一个好的替代 string.GetHashCode()。
  • 在那个问题中,Jon Skeet 提供了一个很好的字符串散列算法。
【解决方案3】:

请注意,2008 年的 SQL Server 支持(具有)函数 HASHBYTES,给定一些数据(例如字符串)可以生成 MD2、MD4、MD5、SHA 或 SHA1 哈希。

【讨论】:

    【解决方案4】:

    我会说你可能不需要存储哈希。

    只需确保您正确索引表中的 URL 列(唯一索引)并且搜索应该很快。

    【讨论】:

    • 我不知道我是否可以或想要做那个傻瓜。这些 URL 可能会变长。一些 URL 的参数挂在它们上,代表文本,例如,来自已发布的表单。 Sqlserver 不允许索引大于 900 的 varchar,我不打算将自己限制为 900。
    【解决方案5】:

    您是否考虑过压缩字符串并存储 VARBINARY?它可以更小,你可以直接在上面建立一个索引。

    【讨论】:

    • 我认为@Alex 可以,但随着我的字符串变大,它仍然会变大。我选择 MD5 是因为它的冲突率较低,并且可以让我的数据更小。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-29
    • 2021-05-17
    • 2012-06-19
    • 2010-11-07
    • 2018-07-22
    相关资源
    最近更新 更多