UUID与顺序ID作为主键的比较
UUID与顺序ID作为主键的比较
在本教程中,我们将探讨UUID和顺序ID作为主键的区别。
在设计数据库时,选择适当的主键格式对系统的性能、可扩展性和数据完整性至关重要。
数据库中的表必须有一个主键列,该列既唯一又不允许为空。这样,主键值就可以唯一地识别每一行。
在选择主键时的一个主要决策是使用UUID还是顺序ID。虽然两种方法都有其优缺点,但最佳选择取决于特定用例和系统的目标。
根据RFC 4122标准定义,UUID(通用唯一识别码)表示一个128位的值。
如今,大多数关系型数据库支持UUID类型:
- Oracle – RAW(16)类型
- SQL Server – NEWID()函数
- PostgreSQL – UUID类型
- MySQL – BINARY(16)类型或UUID()函数
然而,如果数据库不支持这种类型,我们应该将其定义为BINARY(16)类型。使用不同的类型(例如,CHAR(32)也可以工作,但它会占用额外且不必要的空间来存储值)。
现在,让我们来讨论使用UUID作为主键的优点和缺点。
2.1. 分布式系统
在与共享数据库的分布式系统一起工作时,UUID可以派上用场。
使用UUID作为主键的主要目的是能够在分布式系统中共享数据。
使用UUID作为主键,我们可以保证不会发生冲突,而且我们不需要创建一个中央协调系统来管理主键的唯一性。
此外,它还减少了数据库之间的集成复杂性。
分布式和NoSQL数据库依赖于UUID作为键(例如,MongoDB或CouchDB),而不是数值。
2.2. 唯一性
UUID是全局唯一的。 换句话说,我们可以使用一个在表、数据库和系统之间都唯一的ID来标识数据库中的每条记录。这在动态添加或删除节点的分布式系统中尤为重要,因为它们之间的协调可能具有挑战性。冲突只存在于理论上。
此外,由于下一个值很难预测,它们提供了额外的安全性,因此几乎不可能让恶意用户猜测ID。另一方面,这样的用户可以很容易地猜测顺序ID的下一个值。
此外,它们不暴露有关业务数据的信息,因此我们可以安全地将它们用作URL路径的一部分。
2.3. 生成值
UUID的另一个优点是它可以由应用程序或数据库系统本身生成。
通常,当我们使用顺序ID时,我们依赖数据库系统来生成和递增主键值,而不是我们自己。否则,跟踪我们应该使用的下一个值将变得复杂。
然而,放弃生成顺序主键的责任也有一个缺点。我们只能在执行插入语句后才能得到新记录的实际值。
相反,我们可以在代码中自己生成UUID,而不是告诉数据库代表我们生成它。由于UUID是唯一的且不是顺序的,我们不必担心之前的值。
因此,我们可以立即拥有主键值。我们不需要等到插入查询执行。
2.4. 内存使用
UUID是128位长,是BIGINT大小的两倍,是INTEGER类型的四倍。
在关系型数据库中,我们通常使用BIGINT来存储数值标识符,使用UUID代替可能没有太大区别,因为它只是大了两倍。
然而,拥有一个大的主键可能会导致性能问题,特别是在选择查询和索引方面。
2.5. 可读性
UUID由32个十六进制数字组成,由四个短划线分隔,这使得它很难记住。
因此,UUID的表示可能不被认为是用户友好的,可能很难口头表达。它不容易阅读和记忆。
另一方面,顺序ID易于阅读和记忆。
然而,ID值本身是否适合人类阅读是值得怀疑的。
2.6. 排序
另一个缺点是我们不能按自然顺序对UUID值进行排序。
由于这个限制,我们可能被迫使用另一个列,例如创建时间戳,来获取有序的项目。因此,这可能会增加查询的执行时间。
3. 顺序ID
序列是一个在数据库中标识序列记录的唯一字母数字值。
此外,我们只能在序列中使用数值标识符,因为系统无法为UUID类型确定下一个序列值。
通常更倾向于使用顺序ID而不是UUID,因为它们需要的空间更少。
数据库引擎为顺序ID提供原生支持:
- PostgreSQL – SERIAL
- SQL Server – IDENTITY
- MySQL – AUTO_INCREMENT
- SQLite – AUTOINCREMENT
让我们看看使用顺序ID作为主键的优点和缺点。
3.1. 可读性
与UUID值不同,数值标识符更简单易读易记。
使用数值标识符,我们可以轻松地跟踪记录插入数据库的顺序。
此外,我们可以根据它们的ID快速识别记录之间的关系。
3.2. 索引
我们经常在主键和外键上使用索引来加速选择查询和连接。
一些数据库使用B+树结构进行索引,而其他数据库,如SQL和MySQL,使用聚集索引。
此外,UUID值的索引效果不佳。键越长,索引条目所需的内存就越多。
此外,UUID的索引因子很低,因为它们的值是随机的。每次我们修改表时,索引都需要更新。这可能会影响我们系统的性能并使用不必要的内存。
此外,我们还可以在外键上对连接的表进行索引,这在处理UUID值时可能会对性能产生额外的打击。
3.3. 批量操作
批量操作指的是将多个数据库操作作为一个工作单元执行的过程。
使用顺序ID的批量操作效果更好的一个原因是顺序ID以可预测的顺序生成。多个数据库操作可以作为一个单一的批次执行,优化系统的性能。
主键值以可预测的顺序生成,新记录被添加到序列的末尾。这使得可以使用某些类型的查询,例如需要按主键排序的区间查询。
此外,顺序ID通常比UUID小,这可以通过减少所需的存储量来提高系统的性能。
然而,在使用顺序ID的分布式系统中,需要考虑序列中的间隙潜力。在这种情况下,使用UUID作为主键可能是更好的选择。
3.4. 可预测性
序列标识符遵循特定的结构,这使它们可预测。
这可能允许恶意用户读取他们不应该阅读的信息。
我们可能无意中暴露了一些私人数据和业务逻辑。例如,最后一个ID可能代表用户生成的发票总数,这可以揭示收入信息。
3.5. 并发
正如前面提到的,通过应用程序生成顺序ID会很复杂。要创建ID,我们需要查询数据库以找出下一个可用的值。
在分布式系统中,如果有多个系统正在将数据插入数据库,这通常意味着我们的数据可能会发生冲突。不同的系统可能会产生相同的键值。
为了解决这个问题,我们需要创建单独的服务来产生顺序值。此外,相同的服务将成为单点故障。
3.6. 大小限制
最后,顺序ID限制了它们的大小。 尽管数值标识符的最大数量相当大,但耗尽数字是可能的。
如果我们使用INT类型作为主键,最终可能会达到最大数量(2,147,483,647),这将导致静默溢出错误。因此,我们最终可能会有负值作为主键。
4. UUID和顺序ID之间的区别
总之,下表显示了UUID和顺序ID之间的区别。
| UUID | 顺序ID |
|---|---|
| 占用128位 | 当涉及到BIGINT时占用64位,INT类型占用32位 |
| 冲突只存在于理论上 | 由于大小限制,冲突是可能的 |
| 在分布式系统中表现良好 | 需要有协调组件以避免重复值 |
| 得到NoSQL和分布式数据库的良好支持 | 不建议在NoSQL和分布式数据库中使用 |
| 不可预测 | 可预测 |
| 难以记忆和口头表达 | 易于阅读和记忆 |
5. 结论
在本教程中,我们学习了UUID和顺序ID作为主键的区别。
总之,是否使用UUID或顺序ID作为主键取决于系统的特定用例和目标。
如果我们正在使用分布式系统并且需要全局唯一性,UUID可能是最佳选择。另一方面,如果我们有有限的内存使用量,并且执行SQL查询的性能至关重要,顺序ID可能是最佳选择。
最终,主键的选择应该基于对系统要求和限制的仔细评估。