与自动递增的数字相比,电子邮件地址是一个糟糕的初选候选人吗?

我们的web应用程序需要电子邮件地址在系统中是唯一的。所以,我想到使用电子邮件地址为主键。然而,我的同事认为字符串比较将比整数比较慢。

这是一个有效的理由不使用电子邮件为主键吗?

我们使用的是PostgreSQL。


您可以通过使用整数主键来提高性能。


是的,如果您使用整数来代替会更好。您还可以将电子邮件列设置为唯一约束。

是这样的:

CREATE TABLE myTable(
    id integer primary key,
    email text UNIQUE
);

您应该使用整数主键。如果你需要电子邮件列是唯一的,为什么不简单地在该列上设置一个唯一索引呢?


字符串比较比int比较慢。但是,如果您只是使用电子邮件地址从数据库检索用户,那么这并不重要。如果您有多个连接的复杂查询,那么这很重要。

如果在多个表中存储有关用户的信息,则用户表的外键将是电子邮件地址。这意味着您将多次存储电子邮件地址。


整数主键更好的另一个原因是当您在不同的表中引用电子邮件地址时。如果address本身是一个主键,那么在另一个表中你必须使用它作为一个键。所以你可以多次存储电子邮件地址。


你的同事是对的:使用一个自动递增的整数作为你的主键。

您可以在应用程序级别实现电子邮件唯一性,或者您可以将电子邮件地址列标记为惟一,并在该列上添加索引。

将字段添加为唯一字段只会在插入到该表时花费字符串比较的成本,而不会在执行连接和外键约束检查时花费字符串比较成本。

当然,您必须注意,在数据库级别向应用程序添加任何约束都可能导致应用程序变得不灵活。在仅仅因为应用程序需要某个字段是唯一的或非空的,就将它设置为“唯一的”或“非空的”之前,一定要充分考虑。


我对postgres不太熟悉。主键是一个很大的主题。我在这个网站(stackoverflow.com)上看到了一些很好的问题和答案。

我认为使用数字主键并在电子邮件列上使用UNIQUE INDEX可以获得更好的性能。电子邮件往往在长度上有所不同,可能不适合主键索引。

在这里和这里读点书。


我还要指出,电子邮件是一个糟糕的选择,使一个独特的领域,有些人,甚至是小企业共享一个电子邮件地址。和电话号码一样,电子邮件也可以重复使用。Jsmith@somecompany.com很容易属于一年前的约翰·史密斯,两年后的茱莉亚·史密斯。

电子邮件的另一个问题是它们经常变化。如果你用这个键连接到其他表,那么你也必须更新其他表,当整个客户公司更改他们的电子邮件时,这可能会对性能造成相当大的影响(我曾经见过这种情况)。


这很糟糕。假设某个电子邮件提供商倒闭了。用户会想要更改他们的电子邮件。如果你使用e-mail作为主键,用户的所有外键都将复制该电子邮件,这使得更改它非常困难……

... 我甚至还没有开始谈论性能考虑因素。


主键应该是唯一的常量

电子邮件地址随着季节的变化而变化。作为查找的辅助键很有用,但作为主键不太合适。


如果你有一个非int值作为主键,那么在大数据上插入和检索将会非常慢。


使用电子邮件地址作为主键的缺点:

Slower when doing joins. Any other record with a posted foreign key now has a larger value, taking up more disk space. (Given the cost of disk space today, this is probably a trivial issue, except to the extent that the record now takes longer to read. See #1.) An email address could change, which forces all records using this as a foreign key to be updated. As email address don't change all that often, the performance problem is probably minor. The bigger problem is that you have to make sure to provide for it. If you have to write the code, this is more work and introduces the possibility of bugs. If your database engine supports "on update cascade", it's a minor issue.

使用电邮地址作主键的优点:

You may be able to completely eliminate some joins. If all you need from the "master record" is the email address, then with an abstract integer key you would have to do a join to retrieve it. If the key is the email address, then you already have it and the join is unnecessary. Whether this helps you any depends on how often this situation comes up. When you are doing ad hoc queries, it's easy for a human being to see what master record is being referenced. This can be a big help when trying to track down data problems. You almost certainly will need an index on the email address anyway, so making it the primary key eliminates one index, thus improving the performance of inserts as they now have only one index to update instead of two.

在我看来,这两种情况都不是十拿九稳的。当有实用的键时,我倾向于使用自然键,因为它们更容易使用,而且在大多数情况下,缺点并不太重要。


这取决于桌子。如果表中的行表示电子邮件地址,那么电子邮件是最好的ID。如果不是,那么电子邮件不是一个好的ID。


使用GUID作为主键…这样,当你执行INSERT操作时,你就可以从程序中生成它,而不需要从服务器获取响应来找出主键是什么。它在所有表和数据库中都是唯一的,如果有一天你截断了表,自动增量被重置为1,你不必担心会发生什么。


如果只是要求电子邮件是唯一的,那么你可以用该列创建一个唯一的索引。


就我个人而言,我在设计数据库时不使用任何信息作为主键,因为我很可能在以后需要更改任何信息。我提供主键的唯一原因是,它方便从客户端执行大多数SQL操作,我的选择一直是自动增加整数类型。


我不知道这在您的设置中是否可能是一个问题,但根据您的RDBMS,列的值可能是区分大小写的。PostgreSQL文档说:“如果你声明一个列为UNIQUE或PRIMARY KEY,隐式生成的索引是区分大小写的”。换句话说,如果您在一个以email为主键的表中接受用户输入进行搜索,并且用户提供“John@Doe.com”,那么您将找不到“john@doe.com”。


是的,这是一个糟糕的主键,因为你的用户会想要更新他们的电子邮件地址。


似乎没有人提到一个可能的问题,即电子邮件地址可能被视为隐私。如果电子邮件地址是主键,那么个人资料页面的URL很可能类似于..../Users/my@email.com。如果不想暴露用户的电子邮件地址怎么办?您必须找到其他一些识别用户的方法,可能是通过一个唯一的整数值来生成像..../Users/1这样的url。那么你最终会得到一个唯一的整数值。


Email是一个很好的唯一索引候选,但不是主键,如果它是一个主键,例如,你将无法更改联系人的电子邮件地址。 我认为你的连接查询也会变慢。


在逻辑层面上,电子邮件是天然的关键。 在物理层面上,如果您使用的是关系数据库,那么自然键并不适合作为主键。原因主要是别人提到的性能问题。

出于这个原因,设计可以进行调整。自然键成为替代键(UNIQUE, NOT NULL),您使用代理键/人工键/技术键作为主键,在您的情况下,这可以是一个自动递增键。

systempuntoout问道:

如果有人想更改他的电子邮件地址怎么办?你是否也要更改所有外键?

这就是级联的作用。

使用数字代理键作为主键的另一个原因与索引在平台中的工作方式有关。例如,在MySQL的InnoDB中,表中的所有索引都预先挂起了主键,所以你希望PK尽可能小(为了速度和大小)。同样与此相关的是,当主键按顺序存储时,InnoDB会更快,而字符串在那里没有帮助。

使用字符串作为替代键时要考虑的另一件事是,使用您想要的实际字符串的哈希值可能更快,跳过一些字母的大写和小写。(实际上,我降落在这里是为了寻找证据来证实我刚才说的话;还看……)


我知道这有点晚了,但我想补充的是,人们放弃电子邮件帐户和服务提供商恢复地址,允许其他人使用它。

正如@HLGEM指出的那样,“Jsmith@somecompany.com很容易在一年后属于约翰·史密斯,两年后属于朱莉娅·史密斯。”在这种情况下,如果约翰·史密斯想要你的服务,你要么拒绝使用他的电子邮件地址,要么删除所有与朱莉娅·史密斯有关的记录。

如果你必须根据当地法律删除与企业财务历史有关的记录,你可能会发现自己陷入困境。

所以我永远不会使用电子邮件地址、车牌等数据作为主键,因为无论它们看起来多么独特,它们都不受你的控制,并且可能会提供一些你可能没有时间处理的有趣挑战。


主键应该选择一个静态属性。由于电子邮件地址不是静态的,可以被多个候选人共享,因此使用它们作为主键并不是一个好主意。此外,电子邮件地址通常是一定长度的字符串,可能大于唯一id,我们想使用[len(email_address)>len(unique_id)],所以它将需要更多的空间,甚至最糟糕的是,它们被多次存储为外键。因此会导致性能下降。


不要使用电子邮件地址为主键,保持电子邮件的唯一性,但不要使用它为主键,使用用户id或用户名为主键


您可能需要考虑任何适用的数据法规。电子邮件是个人信息,例如,如果你的用户是欧盟公民,那么根据GDPR,他们可以指示你从你的记录中删除他们的信息(记住,无论你在哪个国家,这都适用)。

如果出于参考完整性或审计等历史原因,需要将记录本身保存在数据库中,则使用代理键将允许您将所有个人数据字段设置为NULL。如果他们的个人数据是主键,这显然不那么容易