如何选择数据库主键?UUID还是自动递增整数?

“ 为提高可读性,95%的情况下会选择自动递增整数。”

在设计新的数据库结构的时候,首先要做的事情之一就是决定使用何种格式的主键。99%的情况下,开发人员需要二选一:UUID或是自动递增整数/序列号。

开发人员开始可能不会意识到,一旦选择了某种主键格式,会产生一系列后续影响,而且之后几乎无法切换。

“ 要选择合适的主键格式,数据库结构设计者需要对业务需求和底层数据库系统都有很好的理解,这样才能做出有所根据的权衡。”

UUID

现在有5种标准的UUID格式。大多数情况下,人们会选择v4(随机UUID)或v1(时间戳UUID),该格式的特点:

  • 全局唯一性。例如,通过日志找项目不会误找。易于在系统之间迁移数据,因为碰撞只在理论上可能;
  • 无状态性,它可以动态生成;
  • 感觉安全,因为恶意用户无法猜到这个ID。但是,安全团队总会坚称公共可访问的UUID路径是无法满足安全标准的;
  • V1 UUID存储了时间戳信息,有时可能会有用;
  • (对人来说)不可读的
  • 不能根据创建时间自然地排序。虽然V1 UUID格式包含了时间戳,但是它使用了小端模式进行了编码--最不重要的时间出现在最前面,这导致UUID很难去按照创建时间排序。人们设计自己的UUID格式来解决这个问题,也有一个标准化的草案(新UUID格式);
  • 对于像MySQL,Oracle这样的使用集群主键的数据库,v4随机生成UUID如果作为主键会损害插入性能。因为它会要求对行进行重新排序,以便将新插入的行放在集群索引里的正确的位置。另一种,PostgreSQL用堆替代集群主键,因此使用UUID作为主键不会影响它的插入性能。

自动递增整数/序列号

使用自动递增整数/序列号作为主键也很常用,并且每种主流的数据库引擎会提供原生支持。比如:MySQL - AUTO_INCREMENT,PostgreSQL - SERIAL,SQLite - AUTOINCREMENT。该格式的特点:

  • 可读性。如果我们需要向外展示它,这是特别有价值的。想想 问题id,显然,issue-12 比 issue-b1e92c3b-a44a-4856-9fe3-925444ac4c23更具可读性;
  • 更少的空间。UUID一般需要占有16字节。对于自动递增整数,当作为Long格式存储时,它占用8字节。如果表本身只有几列,那么额外的主键空间开销会变得更加明显;
  • 不能被用在分布式系统里,因为不同主机很可能产生完全相同的数字;
  • 不能动态生成。相反,我们必须通过查询数据库去计算出下个可用的主键。在分布式系统里,这通常意味着需要引入一个单独的服务来生成这个序列号。该服务将为成为一个单点故障(SPOF)
  • 一些业务数据会被暴露,因为最新的ID可以体现出库存的总数。攻击者还可以通过扫描整数范围来探究泄漏(如果ACL实现得正确,就不会发生泄漏)。

选择哪个?

如上所述,这两种方式各有利弊。但是基于我们的经验,95%的情况下,默认选择应该总是自动递增整数。为什么呢?

“可读性,可读性带来简单性。数字易于书写,易于记忆,易于交流。主键不仅不要被系统使用,它也需要展示给终端用户(例如订单号),并由运维工程师,客户支持等进行检查。”

99.9%的应用不会达到互联网规模,它们只需要一些支持CRUD操作的模型,包含数千条记录。而且也不需要分布式系统。

拿经典的问题追踪/项目管理工具举例。该工具可能最多包含5个图项目,每个项目包含5个图问题。而 问题id 用 issue/123 肯定比 issue/b1e92c3b-a44a-4856-9fe3-925444ac4c23 更具可读性。事实上,所有主要问题追踪系统都是用整数作为问题id,像Jira,Apple的Radar,Google的issue tracker等,而绝大多数应用程序都还没有这些问题追踪工具那么复杂。

当然也存在使用UUID的合理情况,例如日志的条目。但大多数情况下,使用UUID作为主键是未经成熟优化的标志,而且这个选择之后很难切换。

你会怎么选择呢?

“ 如果你喜欢这篇文章,可能会对Bytebase这个产品感兴趣,这是一个基于网页,开源的数据库结构更改和版本控制工具。它不会告诉你应该选择哪种主键格式,但它会帮助你团队里的开发人员和DBA们进行更好的团队协同。”

原英文版:Choose Primary Key - UUID or Auto Increment Integer?

你可能感兴趣的:(数据库设计,数据库,postgresql,sqlite,database,mysql)