5

详细说明.. a) 表 (BIGTABLE) 可以容纳一百万行,主键为 ID。(随机且唯一) b) 可以使用什么算法来获得迄今为止尚未使用的 ID。这个数字将用于在表 BIGTABLE 中插入另一行。

用更多细节更新了问题.. C)这个表已经有大约 100 K 行并且主键不是作为标识的集合。d) 目前,生成一个随机数作为主键,并在该表中插入一行,如果插入失败,则生成另一个随机数。问题是有时它会进入一个循环并且生成的随机数非常随机,但不幸的是,它们已经存在于表中。因此,如果我们在一段时间后重新尝试随机数生成数,它就会起作用。e) sybase rand() 函数用于生成随机数。

希望这个问题的补充有助于澄清一些观点。

4

18 回答 18

5

问题当然是:为什么要随机 ID?

我遇到类似要求的一个案例是 webapp 的客户端 ID:客户端用他的客户端 ID(存储在 cookie 中)来标识自己,因此必须很难暴力猜测另一个客户端的 ID(因为这将允许劫持他的数据)。

我采用的解决方案是将顺序 int32 与随机 int32 组合以获得我用作客户端 ID 的 int64。在 PostgreSQL 中:

CREATE FUNCTION lift(integer, integer) returns bigint AS $$
SELECT ($1::bigint << 31) + $2
$$ LANGUAGE SQL;

CREATE FUNCTION random_pos_int() RETURNS integer AS $$
select floor((lift(1,0) - 1)*random())::integer
$$ LANGUAGE sql;

ALTER TABLE client ALTER COLUMN id SET DEFAULT
lift((nextval('client_id_seq'::regclass))::integer, random_pos_int());

生成的 ID 是“一半”随机的,而另一“半”保证您不能两次获得相同的 ID:

select lift(1, random_pos_int());  => 3108167398
select lift(2, random_pos_int());  => 4673906795
select lift(3, random_pos_int());  => 7414644984
...
于 2008-09-18T18:05:32.250 回答
3

为什么唯一 ID 是随机的?为什么不使用 IDENTITY?如何为现有行选择 ID。

最简单的做法可能是(从 BIGTABLE 中选择 Max(ID)),然后确保您的新“随机”ID 大于...

编辑:根据添加的信息,我建议您搞砸了。

如果它是一个选项:复制表,然后重新定义它并使用标识列。

如果像另一个答案推测的那样,您确实需要一个真正随机的标识符:使您的 PK 成为两个字段。一个身份字段,然后是一个随机数。

如果您根本无法更改表结构,则在尝试插入之前检查 id 是否存在可能是您唯一的办法。

于 2008-09-18T17:24:34.847 回答
2

有点在盒子外面。

为什么不提前预先生成随机数?这样,当您将新行插入 bigtable 时,就已经进行了检查。这将使插入 bigtable 成为一个恒定时间操作。

您最终将不得不执行检查,但这可能会被卸载到第二个进程,该进程不涉及插入 bigtable 的敏感过程。

或者去生成几十亿个随机数,删除重复的,那么你就不用担心很长一段时间了。

于 2008-09-18T21:11:27.250 回答
2

您最好的选择是使您的密钥空间足够大,以使冲突的概率极低,然后不用担心。如前所述,GUID 将为您执行此操作。或者,您可以使用纯随机数,只要它有足够的位数即可。

这个页面有计算碰撞概率的公式

于 2008-09-18T17:37:39.807 回答
2

没有一个很好的算法来解决这个问题。您可以使用此基本构造来查找未使用的 id:

int id;
do {
  id = generateRandomId();
} while (doesIdAlreadyExist(id));
doSomethingWithNewId(id); 
于 2008-09-18T17:25:29.477 回答
1

选择一个随机数,检查它是否已经存在,如果存在则继续尝试,直到找到不存在的数字。

编辑:或者更好的是,跳过检查并尝试插入具有不同 ID 的行,直到它起作用。

于 2008-09-18T17:23:23.630 回答
1

将关键字段设为 UNIQUE 和 IDENTITY,您就不必担心了。

于 2008-09-18T17:24:12.990 回答
1

如果这是您经常需要做的事情,您可能需要维护一个实时(非数据库)数据结构来帮助您快速回答这个问题。一棵10路树会很好。当应用程序启动时,它通过从数据库中读取键来填充树,然后使其与数据库中的各种插入和删除保持同步。只要您的应用程序是唯一更新数据库的应用程序,就可以在验证下一个大随机密钥尚未使用时非常快速地查询树。

于 2008-09-18T17:25:59.960 回答
1

第一个问题:这是一个计划中的数据库还是一个已经运行的数据库。如果里面已经有数据,那么 bmdhacks 的答案是正确的。如果是计划数据库,这里是第二个问题:
你的主键真的需要随机吗?如果答案是肯定的,那么使用一个函数从一个已知的种子和一个计数器创建一个随机 id,以了解已经创建了多少个 Id。创建的每个 Id 都会增加计数器。
如果您对种子保密(即,调用种子并将其声明为私有),那么其他人将无法预测下一个 ID。

于 2008-09-18T17:34:25.103 回答
0

在这种情况下,最好的算法是生成一个随机数并进行选择以查看它是否存在,或者如果您的数据库正常出错,则尝试添加它。根据您的键的范围,与有多少记录,这可能是一小段时间。它还具有飙升的能力并且根本不一致。

是否可以在 BigTable 上运行一些查询并查看是否有任何范围可以被利用?IE。100,000 到 234,000 之间还没有 ID,所以我们可以在那里添加 ID?

于 2008-09-18T18:15:40.213 回答
0

如果 ID 是纯随机的,则没有算法可以在没有暴力破解的情况下以类似的随机方式找到未使用的 ID。但是,只要您的随机唯一 ID 的位深度相当大(例如 64 位),您就可以避免只有一百万行的冲突。如果它在插入时发生冲突,请重试。

于 2008-09-18T17:22:52.010 回答
0

为什么不将您的随机数创建者附加到当前日期(以秒为单位)。这样,拥有相同 ID 的唯一方法是,如果两个用户是在同一秒创建的,并且由您的生成器提供相同的随机数。

于 2009-12-08T21:26:03.580 回答
0

是否要求新 ID 也是随机的?如果是这样,最好的答案就是循环(随机化,测试是否存在),直到找到一个不存在的。

如果数据恰好是随机的,但这不是一个强约束,您可以使用 SELECT MAX(idcolumn),以适合数据的方式递增,并将其用作下一条记录的主键。

您需要以原子方式执行此操作,因此要么锁定表,要么使用适合您的数据库配置和模式的其他并发控制。存储过程、表锁、行锁、SELECT...FOR UPDATE 等等。

请注意,在任何一种方法中,您都可能需要处理失败的事务。理论上,您可能会在第一次遇到重复的密钥问题(尽管如果您的密钥空间人口稀少,这不太可能),并且您可能会使用 SELECT...FOR UPDATE 等方法在某些数据库上遇到死锁。因此,请务必检查并在错误时重新启动事务。

于 2008-09-18T17:34:45.880 回答
0

首先检查是否没有使用 Max(ID) + 1 并使用它。

如果 Max(ID) + 1 超过最大值,则在顶部选择一个有序块并开始向后循环寻找一个洞。重复这些块,直到你用完数字(在这种情况下会抛出一个大错误)。

如果找到“洞”,则将 ID 保存在另一个表中,您可以将其用作下一个案例的起点以保存循环。

于 2008-09-18T17:38:21.800 回答
0

跳过任务本身的推理,唯一的算法

  • 会给你一个不在表中的 ID
  • 将用于在表中插入新行
  • 将导致表仍然具有随机唯一 ID

正在生成一个随机数,然后检查它是否已被使用

于 2008-09-18T17:42:17.737 回答
0

根据您的数据库,您可以选择使用序列器(oracle)或自动增量(mysql、ms sql 等)。或者最后的手段做一个 select max(id) + 1 as new id - 小心并发请求,这样你就不会两次得到相同的 max-id - 用即将到来的插入语句将它包装在一个锁中

于 2008-09-18T17:23:30.450 回答
0

我之前已经通过蛮力使用随机数生成器多次看到过这种情况,但这总是一个坏主意。在数据库之外生成一个随机数并尝试查看它是否存在会给您的应用程序和数据库带来很大压力。它可能导致 2 个进程选择相同的 id。

您最好的选择是使用 MySQL 的自动增量功能。其他数据库也有类似的功能。保证您拥有唯一的 ID,并且不会遇到并发问题。

于 2008-09-18T17:26:45.637 回答
0

每次寻找唯一值时扫描该表中的每个值可能是个坏主意。我认为这样做的方法是在另一个表中有一个值,锁定该表,读取该值,计算下一个 id 的值,写入下一个 id 的值,释放锁。然后,您可以自信地使用您读取的 id,您当前的流程是唯一拥有该独特价值的流程。不确定它的扩展性如何。

或者,为您的 id 使用 GUID,因为每个新生成的 GUID 都应该是唯一的。

于 2008-09-18T17:28:00.670 回答