什么是 Hi/Lo 算法?

Posted

技术标签:

【中文标题】什么是 Hi/Lo 算法?【英文标题】:What's the Hi/Lo algorithm? 【发布时间】:2010-09-21 21:05:13 【问题描述】:

我在NHibernate 文档中找到了这个(这是生成唯一键的一种方法,第 5.1.4.2 节),但我没有找到关于它如何工作的很好解释。

我知道 Nhibernate 处理它,我不需要知道里面,但我只是好奇。

【问题讨论】:

【参考方案1】:

基本思想是你有两个数字来组成一个主键——一个“高”数字和一个“低”数字。客户端基本上可以递增“高”序列,知道它可以安全地从前一个“高”值的整个范围内生成具有各种“低”值的键。

例如,假设您有一个当前值为 35 的“高”序列,而“低”数在 0-1023 范围内。然后客户端可以将序列增加到 36(以便其他客户端能够在使用 35 时生成密钥)并知道密钥 35/0、35/1、35/2、35/3... 35/1023 是全部可用。

能够在客户端设置主键,而不是在没有主键的情况下插入值然后将它们取回客户端,这可能非常有用(尤其是使用 ORM)。除此之外,这意味着您可以在执行任何插入之前轻松建立父/子关系并准备好所有键,这使得它们的批处理更简单。

【讨论】:

您是说“低范围”在客户端内部协调,而“高序列”对应于DB序列? hi & lo 值是否通常被组合成一个整数值,或者作为一个由两部分组成的业务键? 然后像 IP 地址一样 - ICANN 为您提供一个高“网络”编号,然后在您获得的 CIDR 范围的限制内,您可以拥有任意数量的低“主机”编号。 @Adam:基本上,什么都没有——增加一个值(“高”部分)可能比生成一堆密钥更便宜。 (就数据传输而言,它可能便宜得多 - 您可以以最小的带宽“保留”大量密钥。) @Adam:如果键只是数字,那是真的。对于 GUID 来说不是很多 :) 但是是的,在简单数字的情况下,任何原子的“固定数量的增量”都可以。这就是 hi-lo 正在做的事情,如果你把它想象成一个分成两部分的数字。【参考方案2】:

除了乔恩的回答:

它用于能够断开连接工作。然后,客户端可以向服务器请求一个 hi 号码并创建增加 lo 号码本身的对象。在 lo 范围用完之前,它不需要联系服务器。

【讨论】:

为了简洁起见,我更喜欢这个。【参考方案3】:

hi/lo 算法将序列域分成hi 组。 hi 值是同步分配的。每个hi 组都有最大数量的lo 条目,可以离线分配,而不必担心并发重复条目。

    hi 令牌由数据库分配,保证两个并发调用看到唯一的连续值

    一旦检索到hi 令牌,我们只需要incrementSizelo 条目的数量)

    标识符范围由以下公式给出:

     [(hi -1) * incrementSize) + 1, (hi * incrementSize) + 1)
    

    并且“lo”值将在以下范围内:

     [0, incrementSize)
    

    从以下起始值开始应用:

     [(hi -1) * incrementSize) + 1)
    

    当所有lo 值都用完后,将获取一个新的hi 值并继续循环

而且这种视觉呈现也很容易理解:

虽然hi/lo 优化器可以很好地优化标识符生成,但它不能很好地与其他系统在我们的数据库中插入行,而对我们的标识符策略一无所知。

Hibernate 提供了 pooled-lo 优化器,它提供了 hi/lo 生成器策略的优势,同时还提供了与其他不了解此序列分配策略的第 3 方客户端的互操作性。

pooled-lo 优化器既高效又可与其他系统互操作,是比传统 hi/lo 标识符策略更好的候选者。

【讨论】:

我有时真的不明白你哈哈哈 所以:虽然 hi/lo 优化器可以很好地优化标识符生成(好的很好),但它不能很好地与其他系统配合使用(你的意思是其他系统?,哪些是第一个?)将行插入我们的数据库(标识符生成不是也用于插入行吗?),而对我们的标识符策略一无所知。 其他系统,例如试图运行 INSERT 语句的 DBA。如果她读取当前的序列数据,你认为知道我们在这个特定的 DB 表中使用 hilo 很容易找出下一个标识符值吗? 如果评论不适合您的答案,我深表歉意,但我想知道默认使用什么优化器?还是它依赖于数据库(我使用的是 PostgreSQL)?因为我无法弄清楚当前序列值和生成的 ID 之间的关系。我使用 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "name") @SequenceGenerator(name="name", sequenceName = "name_seq", allocationSize=100) 作为我的 ID。 @VladMihalcea,我相信你在第三条中打错了,第一个 sn-p 在, (hi * incrementSize) + 1)...应该是, hi * incrementSize),对吧?【参考方案4】:

Lo 是一个缓存分配器,它将键空间分成大块,通常基于一些机器字大小,而不是人类可能明智地选择的有意义大小的范围(例如,一次获得 200 个键)。

Hi-Lo 使用往往会在服务器重启时浪费大量密钥,并生成大量对人类不友好的密钥值。

比 Hi-Lo 分配器更好的是“线性块”分配器。这使用了类似的基于表格的原则,但分配了小而方便大小的块并生成了对人类友好的值。

create table KEY_ALLOC (
    SEQ varchar(32) not null,
    NEXT bigint not null,
    primary key (SEQ)
);

要分配下一个,比如说,200 个键(然后作为一个范围保存在服务器中并根据需要使用):

select NEXT from KEY_ALLOC where SEQ=?;
update KEY_ALLOC set NEXT=(old value+200) where SEQ=? and NEXT=(old value);

如果您可以提交此事务(使用重试来处理争用),则您已分配 200 个密钥并可以根据需要分配它们。

该方案的块大小仅为 20,比从 Oracle 序列分配快 10 倍,并且在所有数据库中 100% 可移植。分配性能相当于hi-lo。

与 Ambler 的想法不同,它将键空间视为连续的线性数字线。

这避免了复合键的推动(这从来都不是一个好主意),并避免在服务器重新启动时浪费整个低字。它会生成“友好的”、人性化的关键值。

相比之下,Ambler 先生的想法是分配高 16 位或 32 位,并随着高字数的增加生成对人类不友好的大键值。

分配键的比较:

Linear_Chunk       Hi_Lo
100                65536
101                65537
102                65538
.. server restart
120                131072
121                131073
122                131073
.. server restart
140                196608

在设计方面,他的解决方案在数字行(复合键、大型 hi_word 产品)上比 Linear_Chunk 更复杂,但没有获得比较优势。

Hi-Lo 设计很早就出现在 OO 映射和持久性中。如今,Hibernate 等持久性框架提供了更简单、更好的分配器作为默认设置。

【讨论】:

不错的帖子,但您没有回答问题。 +1 以获得有趣的答案。我同意绝大多数应用程序都没有从 Hi-Lo 获得比更简单方法的优势。但是我认为 Hi-Lo 更适合高并发应用程序中多个分配器的特殊情况。 谢谢@richj!我的观点是,您可以将多个分配器或大块大小与“线性块分配”一起使用,但是 - 与 Hi/Lo 不同 - 它维护分配器 NEXT_VAL 与表中键的 linear 对应关系, 并且是可调的。与 HiLo 不同,它不需要乘法——只是没有必要! NEXT_HI 的乘数和存储使 HiLo 更加复杂并破坏了可调整性,因为更改块大小将任意更改要发出的下一个密钥。请参阅:literatejava.com/hibernate/… 我对多个独立的分配器很感兴趣。使用 Hi-Lo 很明显,高值可以划分为分配器 ID/块 ID。 (对我来说)同样的方法可以应用于线性块并不是很明显,但在分配器之间划分总范围基本上是相同的问题。我现在明白了。谢谢。 哦,想了想,我觉得SEQ列映射到一个表名。例如,Customers 表有一个分配器,Orders 表有一个分配器,以此类推。原谅我,有时我很慢。【参考方案5】:

根据我的经验,我发现 Hi/Lo 算法非常适合具有复制场景的多个数据库。想象一下。您在纽约有一台服务器(别名 01),在洛杉矶有另一台服务器(别名 02),那么您有一个 PERSON 表... 所以在纽约,当一个人被创造出来时......你总是使用 01 作为 HI 值,而 LO 值是下一个连续的。例如。

010000010 杰森 010000011 大卫 010000012 西奥

在洛杉矶,您总是使用 HI 02。例如:

020000045 鲁珀特 020000046 奥斯瓦尔德 020000047 马里奥

因此,当您使用数据库复制时(无论是什么品牌),所有主键和数据都可以轻松自然地组合在一起,而无需担心重复主键、冲突等。

这是在这种情况下最好的方法。

【讨论】:

它在 Hibernate 中不起作用。 HiLo algrotirm 在每个事务中获取一个新的序列值,因此 HI 计数器相应地递增。但在您的示例中,HI 计数器对于一个 DB 始终是恒定的。

以上是关于什么是 Hi/Lo 算法?的主要内容,如果未能解决你的问题,请参考以下文章

Hibernate generator小结

hibernate 主键生成方式

Hibernate的映射文件的generator标签的native属性有哪些值,分别是啥含义

class 2-3 小项目练习

postgresql update returning

Matlab实现小波变换