Contents

HI/LO算法

1. 简介

在本教程中,我们将解释 Hi/Lo 算法。它主要用作 数据库标识符生成策略

我们将从算法概述开始。然后,我们将展示一个基于 Hibernate 框架的实际示例。最后,我们将讨论算法的用例、优点和缺点。

2. Hi/Lo 算法概述

2.1. 定义

Hi/Lo 算法的主要目的是创建可以安全地用作数据库标识符的数字范围。为此,它使用三个数字变量,通常称为highlowincrementSize

incrementSize变量保存一批可以生成的标识符的最大数量。它应该被视为在算法开始时定义的常数值。在多个客户端使用相同的 Hi/Lo 配置来持久化条目的环境中,任何运行时修改都可能导致严重问题。

High变量通常是从数据库序列中分配的。在这种情况下,我们确信没有人会两次获得相同的数字。

low变量在[0, incrementSize)范围内保存当前分配的数字。

给定这些点,Hi/Lo 算法生成范围为 [(hi – 1) * incrementSize + 1 , (hi * incrementSize)) 的值。

2.2. 伪代码

让我们看一下使用 Hi/Lo 算法生成新值的步骤:

  • 如果low大于或等于 incrementSize,则为high分配一个新值并将low重置 为 0
  • 使用公式生成一个新值:(high – 1) * incrementSize + low
  • low加 1
  • 返回生成的值

3. 实例

让我们看看 Hi/Lo 算法的实际应用。为此,我们将使用 Hibernate 框架及其 Hi/Lo 实现。

首先,让我们定义一个要使用的数据库实体:

@Entity
public class RestaurantOrder {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "hilo_sequence_generator")
    @GenericGenerator(
      name = "hilo_sequence_generator",
      strategy = "sequence",
      parameters = {
        @Parameter(name = "sequence_name", value = "hilo_seqeunce"),
        @Parameter(name = "initial_value", value = "1"),
        @Parameter(name = "increment_size", value = "3"),
        @Parameter(name = "optimizer", value = "hilo")
      }
    )
    private Long id;
}

这是一个带有一个id字段的简单餐厅订单。要正确定义Hibernate中的Hi/Lo算法,在id字段的定义中,我们必须选择一个序列策略——hilo优化器——并指定increment_size参数。

为了展示 Hi/Lo 算法的实际效果,我们将在一个循环中保存九个餐厅订单:

public void persist() {
    Transaction transaction = session.beginTransaction();
    for (int i = 0; i < 9; i++) {
        session.persist(new RestaurantOrder());
        session.flush();
    }
    transaction.commit();
}

根据实体中指定的增量大小,对于下一个high值,我们应该只有 3 次调用数据库。假设数据库序列从 1 开始,第一批生成的标识符将在 [1,3] 范围内。

当 Hi/Lo 算法返回 3 并且 Hibernate 请求下一个标识符的值时,low变量的值等于incrementSize常量。在这种情况下,必须对数据库进行下一次调用以获取新的high值。将 2 作为新的high值,该算法生成范围 [4,6] 内的值。

最后,对数据库进行最后一次调用以获得下一个high值,并将 [7, 9] 范围内的值分配给实体。

在*persist()*方法执行期间捕获的休眠日志确认这些值:

Hibernate: call next value for hilo_seqeunce
org.hibernate.id.enhanced.SequenceStructure  - Sequence value obtained: 1
org.hibernate.event.internal.AbstractSaveEventListener  - Generated identifier: 1, using strategy: org.hibernate.id.enhanced.SequenceStyleGenerator
org.hibernate.event.internal.AbstractSaveEventListener  - Generated identifier: 2, using strategy: org.hibernate.id.enhanced.SequenceStyleGenerator
org.hibernate.event.internal.AbstractSaveEventListener  - Generated identifier: 3, using strategy: org.hibernate.id.enhanced.SequenceStyleGenerator
Hibernate: call next value for hilo_seqeunce
org.hibernate.id.enhanced.SequenceStructure  - Sequence value obtained: 2
org.hibernate.event.internal.AbstractSaveEventListener  - Generated identifier: 4, using strategy: org.hibernate.id.enhanced.SequenceStyleGenerator
org.hibernate.event.internal.AbstractSaveEventListener  - Generated identifier: 5, using strategy: org.hibernate.id.enhanced.SequenceStyleGenerator
org.hibernate.event.internal.AbstractSaveEventListener  - Generated identifier: 6, using strategy: org.hibernate.id.enhanced.SequenceStyleGenerator
Hibernate: call next value for hilo_seqeunce
org.hibernate.id.enhanced.SequenceStructure  - Sequence value obtained: 3
org.hibernate.event.internal.AbstractSaveEventListener  - Generated identifier: 7, using strategy: org.hibernate.id.enhanced.SequenceStyleGenerator
org.hibernate.event.internal.AbstractSaveEventListener  - Generated identifier: 8, using strategy: org.hibernate.id.enhanced.SequenceStyleGenerator
org.hibernate.event.internal.AbstractSaveEventListener  - Generated identifier: 9, using strategy: org.hibernate.id.enhanced.SequenceStyleGenerator

4. 算法优缺点

Hi/Lo 算法的主要优点是减少了对下一个序列值的数据库调用次数。增加incrementSize的值会减少到数据库的往返次数。显然,这意味着我们的应用程序的性能提升。除此之外,在互联网连接较弱的环境中,Hi/Lo 算法是首选

另一方面,在多个不同客户端将数据持久保存到数据库中同一个表的环境中,Hi/Lo 算法并不是最佳选择。第三方应用程序可能不知道我们用来生成标识符的 Hi/Lo 策略。因此,他们可能会使用我们应用程序中当前使用的生成数字范围中的实体 ID。在这种情况下,在持久化数据时,我们可能会遇到难以修复的错误。