一旦使用HiLo,如果更改容量(最大Lo)会发生什么?

时间:2010-06-21 11:16:18

标签: algorithm hilo

如果我开始使用HiLo生成器为表分配ID,然后决定增加或减少容量(即最大'lo'值),这是否会导致与已分配ID的冲突?

我只是想知道是否需要在数字上加上一个大红旗,说“不要改变它!”

注意 - 不是特定于NHibernate,我只是对HiLo算法感到好奇。

5 个答案:

答案 0 :(得分:20)

HiLo算法通常基本上将两个整数映射到一个整数ID。它保证每个数据库的数字对是唯一的。通常,下一步是保证一对唯一的数字映射到唯一的整数ID。

this previous SO answer

中给出了HiLo概念上如何运作的一个很好的解释

更改max_lo将保留您的数字对唯一的属性。但是,它是否会确保映射的ID是唯一且无冲突的?

让我们看看Hibernate的HiLo实现。他们似乎使用的算法(从我收集到的算法)是:(我可能不在技术上)

h = high sequence (starting at 0)
l_size = size of low block
l = low sequence (starting at 1)

ID = h*l_size + l

所以,如果您的低位块是100,那么您的预留ID块将会是1-100,101-200,201-300,301-400 ......

你的高序列现在是3.现在如果你突然把你的l_size改为10会怎么样?你的下一个区块,你的高位会增加,你会得到4*10+1 = 41

糟糕。这个新值肯定属于1-100的“保留块”。高序列为0的人会认为,“好吧,我有保留范围1-100仅供我使用,所以我只需在41放一个,因为我知道它是安全的。”< / p>

降低你的l_max时,肯定会有非常非常高的碰撞几率。

相反的情况呢,提高它?

回到我们的示例,让我们将l_size提升到500,将下一个键转换为4*500+1 = 2001,保留范围2001-2501。

在HiLo的这个特定实现中,当提升你的l_max时,看起来会避免碰撞。

当然,您应该自己做一些自己的测试,以确保这是实际的实现,或接近它。一种方法是将l_max设置为100并找到前几个键,然后将其设置为500并找到下一个键。如果这里提到的巨大跳跃,你可能是安全的。

但是,我绝不建议最好在现有数据库上提高l_max。

自行决定; HiLo算法并不是一个考虑到不同l_max的算法,并且根据您的确切实现,您的结果最终可能无法预测。也许有过提高l_max和发现麻烦经验的人可以证明这一点是正确的。

总而言之,即使理论上Hibernate的HiLo实现很可能在l_max被提升时避免冲突,但它可能仍然不是好习惯。您应该编码,好像l_max不会随着时间的推移而改变。

但如果你感到幸运......

答案 1 :(得分:3)

请参阅Linear Chunk表分配器 - 这在逻辑上更简单&amp;正确处理同样的问题。

What's the Hi/Lo algorithm?

通过从数字空间&amp;分配范围直接表示NEXT,而不是用高字或乘数复杂逻辑,您可以直接看到将生成哪些键。

基本上,“线性块分配器”使用添加而不是乘法。如果NEXT是1000&amp;我们配置的范围大小为20,NEXT将提前到1020,我们将按键1000-1019进行分配。

可以随时调整或重新配置范围大小,而不会损失完整性。分配器的NEXT字段,生成的密钥和分配器之间存在直接关系。表中存在MAX(ID)。

(相比之下,“Hi-Lo”使用乘法。如果下一个是50&amp;乘数是20,那么你在1000-1019左右分配密钥。没有直接的相关性在NEXT,表中生成的密钥和MAX(ID)之间,很难安全地调整NEXT,并且在不干扰当前分配点的情况下不能改变乘数。)

使用“Linear Chunk”,您可以配置每个范围/块的大小 - 1的大小相当于传统的基于表的“单个分配器”&amp;命中数据库生成每个键,10的大小快10倍,因为它一次分配10个范围,50或100的大小仍然更快..

65536的大小会生成丑陋的密钥,在服务器重启时浪费大量密钥,相当于Scott Ambler的原始HI-LO算法。

简而言之,Hi-Lo是一个错误的复杂&amp;在概念上应该简单的简单方法 - 沿着数字线分配范围。

答案 2 :(得分:2)

我试图通过一个简单的helloWrold-ish hibernate应用程序来发现HiLo算法。

我用

尝试了一个hibernate示例
<generator class="hilo">
<param name="table">HILO_TABLE</param>
<param name="column">TEST_HILO</param>
<param name="max_lo">40</param>
</generator>

使用单列“TEST_HILO”创建的名为“HILO_TABLE”的表 最初我将TEST_HILO列的值设置为8。

update HILO_TABLE set TEST_HILO=8;

我发现创建ID的模式是

hivalue * lowvalue + hivalue

hivalue是DB中的列值(即从HILO_TABLE中选择TEST_HILO) lowvalue来自config xml(40)

所以在这种情况下ID从8 * 40 + 8 = 328

开始

在我的hibernate示例中,我在一个会话中添加了200行。所以用ID 328到527创建了行 在数据库中,竞争对手增加到13。 增量逻辑似乎是: -

new hivalue in DB = inital value in DB + (rows_inserted/lowvalue + 1 )

= 8 + 200/40 = 8 + 5 = 13

现在,如果我运行相同的hibernate程序来插入行,那么ID应该从中开始 13 * 40 + 13 = 533

当运行程序时,它已被确认。

答案 3 :(得分:1)

根据经验,我会说:是的,减少会导致碰撞。当您具有较低的最大值时,您将获得较低的数字,与数据库中的高值无关(以相同的方式处理,例如,对于NH,每个会话工厂实例的增量)。

增加的可能性不会导致碰撞。但你需要尝试或者问一个比我更了解的人,以确保这一点。

答案 4 :(得分:0)

我知道有个老问题,但是值得回答“是的,可以”

只要您根据表的当前ID号重新计算hibernate_unique_key表,就可以随时增加或减少nex_hi。

在我们的示例中,每个实体hibernate_unique_key表都有一个ID,该表具有两列:

  • next_hi
  • EntityName。

任何给定ID的next_hi的计算方式为

SELECT MAX(Id) FROM TableName/(@max_lo + 1) + 1

下面的脚本使用ID列遍历每个表,并更新我们的nex_hi值

DECLARE @scripts TABLE(Script VARCHAR(MAX))
DECLARE @max_lo VARCHAR(MAX) = '100';
    
INSERT INTO @scripts
SELECT '
INSERT INTO hibernate_unique_key (next_hi, EntityName)
SELECT
(SELECT ISNULL(Max(Id), 0) FROM ' + name + ')/(' + @max_lo + ' + 1) + 1, ''' + name + ''' 
'
FROM sys.tables WHERE type_desc = 'USER_TABLE' 
AND COL_LENGTH(name, 'Id') IS NOT NULL
AND NOT EXISTS (select next_hi from hibernate_unique_key k where name = k.EntityName)


DECLARE curs CURSOR FOR SELECT * FROM @scripts
DECLARE @script VARCHAR(MAX)

OPEN curs 
FETCH NEXT FROM curs INTO @script

WHILE @@FETCH_STATUS = 0
BEGIN
    --PRINT @script 
    EXEC(@script)
    FETCH NEXT FROM curs INTO @script
END
CLOSE curs
DEALLOCATE curs