在客户端生成ID并不像看起来那么简单。在JPA和Hibernate中,我们可以使用UUID、自定义策略和专用ID生成服务器。
在客户端而不是数据库中生成ID是分布式应用程序的唯一选择。但是在这样的应用程序中生成唯一的ID是很困难的。正确地生成它们非常重要,因为JPA将使用ID来定义实体状态。最安全的选择是使用UUID和Hibernate的生成器,但是从自定义生成器到专用ID生成服务器有更多的选项。
在前一篇文章我们讨论了JPA实体的服务器生成ID。本文中描述的所有ID生成策略都基于一个基本原则:只有一个点负责生成ID:数据库。这个原则可能会成为一个挑战:我们依赖于一个特定的存储系统,因此切换到另一个存储系统(例如,从PostgreSQL到Cassandra)可能是一个问题。此外,这种方法不适用于分布式应用程序,因为我们可以在多个时区的多个数据中心上部署多个DB实例。
在这种情况下,基于客户端的ID生成(或者说,非基于DB的ID生成)进入了一个阶段。这种策略在ID生成算法和格式方面给了我们更大的灵活性,并允许按其性质进行批处理操作:ID值在存储在DB中之前就已经知道了。在本文中,我们将讨论客户端生成的ID策略的两个基本主题:如何生成唯一的ID值以及何时分配它。
生成算法
当涉及到分布式应用中的ID生成时,我们需要决定使用哪种算法来保证唯一性和声音生成性能。让我们看看这里的一些选择。
随机ID和时间戳-糟糕的想法
对于分散ID生成来说,这是一个简单而天真的实现。让每个应用程序实例使用随机数生成器生成唯一的ID,就这样!为了使它更好,我们可能会考虑使用一个复合结构--让我们在随机数的开头附加时间戳(以毫秒为单位),以使我们的ID可以排序。例如,要创建64位ID,我们可以使用时间戳的前32位和随机数的最后32位。
这种方法的问题在于它不能保证唯一性。我们只能希望生成的ID不会冲突。对于大型分布式数据密集型系统,这种方法是不可接受的.除非我们是赌场,否则我们不能依赖概率法则。
结语
我们不应该为全球唯一的ID生成算法重新发明轮子。这需要花费大量的时间、精力和几个博士学位。一些现有的解决方案可以解决这一问题,并可在我们的应用中使用。
UUID:全球唯一
UUID生成-是一种在分布式应用程序中生成ID的广为人知和广泛使用的方法.标准库在几乎所有编程语言中都支持这种数据类型。我们可以在应用程序代码中生成ID值,这个值将是全局唯一的(通过生成算法的设计)。与“传统”数字ID相比,UUID有一些优点:
- 唯一性不取决于数据表。我们可以使用UUID类型的主键在表或数据库之间移动数据,不会出现问题。
- 数据隐藏。让我们假设我们开发了一个Web应用程序,用户在登录时会在浏览器的地址中看到以下片段:
userId=100
。这意味着可能存在一个ID为99或101的用户。知道这些信息可能会导致安全漏洞。
UUID是不可排序的,但是通常不需要通过代理ID值对数据进行排序;我们应该为此使用一个业务密钥。但是如果我们绝对需要排序,我们可以使用UUID子类型-ULID,它代表“普遍唯一的字典排序标识符”。
在大多数情况下,Java中的随机UUID生成器的性能也是足够的。在我的电脑上(AppleM1max),每次操作大约需要500 ns,这就给了我们大约200万UUID每秒。
UUID:缺点
UUID几乎是ID值的最佳选择,但是有一些事情可能会阻止您使用它。
首先,UUID值比64位长ID占用更多的存储空间.如果我们需要精确的话,是空间的两倍。额外