什么是Hi / Lo算法?

我已经在NHibernate文档中找到了它(这是一种生成唯一密钥的方法,第5.1.4.2节),但是我还没有找到关于它的很好的解释。

我知道Nhibernate可以处理它,我不需要了解内部,但是我很好奇。

#1 楼

基本思想是,您有两个数字组成主键-“高”数字和“低”数字。客户基本上可以增加“高”序列,知道它随后可以安全地从先前的“高”值的整个范围生成具有各种“低”值的密钥。

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

可以在客户端设置主键,而不是在没有主键的情况下插入值然后将其取回客户端,这非常有用(尤其是对于ORM)。除了其他功能,这意味着您可以轻松建立父子关系并在进行任何插入操作之前将所有键都放置到位,这使批处理更为简单。

评论


您是说客户端内部协调了“低范围”,而“高顺序”则对应于一个数据库顺序?

–克里斯·诺(Chris Noe)
2009年6月30日13:15

那么,hi&lo值通常是由单个整数值组成,还是由两部分组成的业务键?

–克里斯·诺(Chris Noe)
2009年6月30日15:48

就像IP地址一样-ICANN为您提供一个较高的“网络”编号,然后,在给定的CIDR范围内,您将拥有尽可能低的“主机”编号。

– gbjbaanb
09年8月7日在14:19

@Adam:从根本上讲,什么都没有-增加一个值(“高”部分)可能比生成一堆密钥便宜得多。 (就数据传输而言,它可能便宜得多-您可以以最小的带宽“保留”大量密钥。)

–乔恩·斯基特(Jon Skeet)
2010年8月9日,11:15



@亚当:如果键只是数字,那是真的。对于GUID而言,不是很多:)但是,是的,在简单数字的情况下,任何原子的“固定数量的增量”都可以。如果您将它看作一个数字分为两部分,那实际上就是hi-lo所做的事情。

–乔恩·斯基特(Jon Skeet)
2010年8月9日,11:36

#2 楼

除了Jon的答案:

它还可以用来断开连接。然后,客户端可以向服务器请求一个hi编号,并创建增加lo编号本身的对象。在lo范围用完之前,无需联系服务器。

评论


为了简洁起见,我更喜欢这样做。

–开发人员MariusŽilėnas
19年6月21日在6:59

#3 楼


因为这是一个非常常见的问题,所以我写了这篇文章,并以此为依据。


hi / lo算法将序列域拆分为“ hi”组。同步分配一个“ hi”值。每个“ hi”组都有最大数量的“ lo”条目,可以通过离线分配它们,而不必担心并发重复的条目。


“ hi”令牌由数据库,并确保两个并发调用可以看到唯一的连续值
一旦检索到“ hi”令牌,我们只需要“ incrementSize”(“ lo”条目的数量)

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

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


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

[0, incrementSize)


从以下起始值开始应用:

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


使用所有“ lo”值时,都会获取新的“ hi”值,并且循环继续

您可以在本文中找到更详细的解释:

并且此直观演示也很容易遵循:



虽然hi / lo优化器可以优化标识符生成,但是它不能与其他插入行的系统配合使用

Hibernate提供了pool-lo优化器,它提供了hi / lo生成器策略的优点,同时还提供了与其他第三方客户端的互操作性不知道这种序列分配策略的人。

Pool-lo优化器既高效又可与其他系统互操作,比传统的hi / lo标识符策略要好得多。

评论


我有时真的不明白你是不是哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈一个?)在我们的数据库中插入行(标识符生成器也不会用来插入行吗?),而对我们的标识符策略一无所知。

–阿德林
17-2-6在21:53



其他系统,例如试图运行INSERT语句的DBA。如果她读取当前序列数据,您是否知道我们在此特定数据库表中使用hilo,就很容易找出下一个标识符值?

– Vlad Mihalcea
17年2月7日在1:46

如果评论不适合您的回答,我深表歉意,但我想知道默认情况下使用了哪种优化程序?还是取决于DB(我使用的是PostgreSQL)?因为我无法弄清楚当前序列值和生成的ID之间的关系。我使用@GeneratedValue(strategy = GenerationType.SEQUENCE,生成器=“名称”)@SequenceGenerator(name =“名称”,sequenceName =“ name_seq”,allocationSize = 100)作为我的ID。

– StefanGolubović
19-10-29在18:33



@VladMihalcea,我相信您在第三个项目符号中有错别字,第一个代码段位于(hi(incrementSize)+ 1)...应该是,hi(incrementSize),对吗?

–惠甘
4月6日17:29

#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的想法不同,它将键空间视为连续的线性数字线。

这样避免了复合键的推动力(从来没有确实是个好主意),并避免在服务器重新启动时浪费整个lo-word。相比之下,它会生成“友好的”,人类规模的键值。

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之类的持久性框架提供了更简单,更好的分配器作为其默认值。

评论


帖子不错,但您没有回答这个问题。

–orbfish
2014年1月16日23:09

+1是一个有趣的答案。我同意绝大多数应用程序都无法从Hi-Lo获得比简单方法更多的优势。但是,我认为Hi-Lo更适合于高并发应用程序中多个分配器的特殊情况。

–richj
2014年5月10日18:08



谢谢@richj!我的观点是,您可以在“线性块分配”中使用多个分配器或较大的块,但与Hi / Lo不同,它保持分配器NEXT_VAL与表中键的线性对应关系,并且是可调整的。与HiLo不同,它不需要乘法-只是没有必要! NEXT_HI的乘数和存储使HiLo更加复杂并破坏了可调谐性,因为更改块大小将任意更改要发行的下一个密钥。请参阅:literatejava.com/hibernate/…

–托马斯W
2014年5月11日在1:37

我对多个独立的分配器感兴趣。使用Hi-Lo,显然可以将高值划分为分配器ID /块ID。 (对我而言)尚不明显可以将相同的方法应用于线性块,但是在分配器之间划分总范围基本上是相同的问题。我知道了谢谢。

–richj
2014年5月11日17:21

哦,考虑一下之后,我认为SEQ列映射到一个表名。例如,有一个分配器“客户”表,一个分配器“订单”表,依此类推。原谅我,有时候我很慢。

– Rock Anthony Johnson
18-09-21在20:28

#5 楼

根据我的经验,我发现Hi / Lo算法非常适合具有复制方案的多个数据库。想象一下。您在纽约有一个服务器(别名01),在洛杉矶有另一个服务器(别名02),那么您有一个PERSON表...
在纽约,当创建一个人时也是如此...您始终使用01因为HI值和LO值是下一个秘密。例如。


010000010 Jason
010000011 David
010000012 Theo

在洛杉矶,您始终使用HI02。例如:


020000045 Rupert
020000046 Oswald
020000047 Mario

因此,当您使用数据库复制(无论什么品牌)时,主键和数据轻松自然地结合在一起,而不必担心重复的主键,冲突等。

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

评论


它在Hibernate中不起作用。 HiLo algrotirm在每次交易中都会获得新的序列值,因此HI计数器会相应增加。但是在您的示例中,HI计数器对于一个DB始终是恒定的。

–Dmitry1405
16年11月8日13:35