低冲突邮件哈希算法?

时间:2011-09-05 11:04:45

标签: php sql hash md5 probability

背景

我们正在构建一个邮件工具,目前已将emailaddresses分隔为一个单独的表,因此单个emailaddress只存储一次,而是由id引用。我们发现这是一个好主意,因为每封电子邮件的收件人数量可能很大,并且很可能大多数电子邮件都会收到超过100封电子邮件。

但是,当用户将emailaddresses导入list或类似操作时,我们首先需要批量插入以确保所有电子邮件都有ids,我们只是忽略冲突,这样可行。但是,当我们想要将它们插入list时,我们必须逐个获取emailaddresses或使用emailaddresses进行大量的IN查询(作为list引用{ {1}} emailaddress},不是很诱人!

编辑:用户可能正在导入100.000+个电子邮件地址,对于1000个左右的电子邮件地址来说,当然一个一个地查询并不是一个真正的问题。

问题:

因此,一个想法是对每个id进行哈希处理,并将其用作emailaddress。这意味着,我们可以预测所有id的{​​{1}},而无需查询它们。但是有没有好的算法,因为存储16bytes / 128bit +有点失败的目的...... 64bit 应该足够吗?鉴于这一切都应该被编入索引,我们将不胜感激。

有什么建议吗?如果我们只是从MD5中获取前8个字节怎么办? SHA1的8字节更好吗?也许还有更专业的算法?我并不是所有人都知道碰撞概率,但我很好奇现有算法在裁剪时的效果如何,以及电子邮件是小写的,主要是字母或数字。 (注意数据集可能很大)

PS。我们正在使用PHP,因此它在一定程度上限制了我们实现特殊算法的能力。

3 个答案:

答案 0 :(得分:1)

我不确定我理解您的用例,但在电子邮件地址栏中添加了一个唯一的键约束...

答案 1 :(得分:0)

您当前的方法及其局限性存在很多问题。

其中大部分都可以通过维护一个电子邮件地址表来解决,其中ID为主键(MySQL或SQLite上的自动增量,其他地方的序列)和电子邮件地址上的唯一索引。

为什么你的“用户”正在操纵大量的电子邮件地址列表还远未明朗。听起来您的数据中有很大一部分(即特定列表中的收件人)未在您的数据库中维护。你永远不应该“通过emailaddresses逐个获取电子邮件地址或使用巨大的IN查询”。

减少md5或sha的输出会破坏哈希的唯一性,并且更容易发生冲突。

答案 2 :(得分:0)

在您做任何激烈的事情之前,请检查您的查询计划(如何执行此操作取决于您使用的特定数据库服务器,请查看其文档)。

查看您是否无法获取索引以处理电子邮件地址。这应该可以加快速度,但规划人员可能会跳过它们,因为你要插入大量数据。

当你尝试过这种情况时,你可以查看散列问题。

我不知道任何专门用于散列电子邮件地址的算法,虽然您可以使用MD5,但是当碰撞的概率必须非常小而基本上从未发生时,它被设计用于(我不认为有人有在野外发现了MD5碰撞)。这可以做到,但计算成本很高。如果使用SHA,情况会更糟。

在你的情况下,我会建议一些更简单的事情:首先,因为我们可以假设所有电子邮件都是

形式
<someName>@<someServer>

我会做的是将电子邮件分成两部分,从每个部分中删除所有非字母,非数字字符。

接下来我们可以计算这两个部分中每个部分的数值,我们通过总结每个单独字母的ascii值得到(你剥离了其他所有部分,因此多字节字符不会出现问题)

此时剩下要做的就是合并两个总和,因为我们可以预期会有更少的可能发送者,我们只能花两个字节来保存服务器的名称。

在伪代码中:

function emailHash(namePart, serverPart){
  $someName = asciiStrip(namePart)
  $someServer = asciiStrip(serverPart)
  $someNameSum = 0 
  $someServerSum = 0 
  foreach($letter in $someName){
    $someNameSum += asciiValue($letter)
  }
  foreach($letter in $someServer){
    $someServerSum += asciiValue($letter)
  }
  return ($someNameSum % 2^6)*2^2 + $someServerSum % 2^2
}

根据评论进行修改

你是对的,那个人真的很穷。然而,你可以做另一个有趣的事情,虽然实施起来会有点困难。

在剥离外来字符后,只有36个可能的字符,所以我们只需要6位来存储每个值。用户名部分有48位存储空间,可以从电子邮件地址存储8个字符。碰撞到底有多低?

可以通过某种方式取消数字(比如将它们除以2后存储它们)来改进,这样我们总共只处理32个数字。然后,可以将每个数字仅存储5位,总共9个字符。

如果这不能提供足够低的碰撞率,你可能不得不使用MD5(假设算法给出了完美的分布),只能给你几十亿的碰撞机会。