用随机数替换序列

14

我想用自己的自定义ID生成器替换PostgreSQL数据库中使用的某些序列。该生成器会生成一个带有校验位的随机数。因此,原来的内容如下:

SELECT nextval('customers')

会被替换为类似下面的内容:

SELECT get_new_rand_id('customer')
函数将返回一个类似于[1-9][0-9]{9}的数字值,其中最后一位是校验和。
我的担忧有:
  1. 如何使其原子化
  2. 如何避免重复返回相同的ID(这可以通过尝试将其插入带有唯一约束条件的列来捕获,但那时已经太晚了)
  3. 这真的是个好主意吗?
注意1:我不想使用uuid,因为它将与客户通信,而10位数字比36位字符的uuid要简单得多。 注意2:该函数很少以SELECT get_new_rand_id()的形式调用,而是分配为id列的默认值,而不是nextval()编辑:好的,下面有一些解释为什么
  1. 那么为什么要这样过度复杂化呢?目的是隐藏主键不让客户知道。

      

    我为每个新客户提供唯一的customerId(在数据库中生成的序列号)。由于我向客户通信该编号,因此我的竞争对手相当容易监视我的业务(还有其他类似发票号码和订单号码的编号)。这就是我想让它更加困难的原因(请注意:不是不可能,而是更困难)。

  2. 为什么要进行校验和?

      

    在没有任何隐藏序列号的谈话之前,我向ordernr添加了一个校验位,因为在生产过程中有时会出现笨拙的操作,我的想法是将此作为未来保持的良好做法。

阅读了讨论后,我确实可以看到我的方法不是解决问题的最佳方式,但我没有其他好的想法来解决它,请帮帮我。
  1. 是否应添加额外列以放置向客户公开的ID并保留序列号为主键?
  2. 如何以明智和高效的方式生成要公开的ID?
  3. 校验和是否必要?

我并不是在批评,只是想要理解。为什么要给代理主键添加校验位?而且为什么要与客户共享该主键? - cethegeek
你为什么想要这样做呢?它能给你带来什么好处? - Kuberchaun
6个回答

21

使用密码生成唯一且看起来随机的标识符可能是个好主意,因为它们的输出是双射的(输入和输出值之间存在一对一映射)——与哈希不同,您 将不会有任何冲突。这意味着您的标识符不必像哈希一样长。

大多数加密密码工作在64位或更大块上,但 PostgreSQL Wiki 上有一个针对 (32 位) int 类型的 "非加密" 密码函数的 示例 PL/pgSQL 程序。免责声明:我自己没有试过使用此函数。

要将其用于主键,请从维基页面运行 CREATE FUNCTION 调用,然后在您的表上执行:

ALTER TABLE foo ALTER COLUMN foo_id SET DEFAULT pseudo_encrypt(nextval('foo_foo_id_seq')::int);

神奇的是!

pg=> insert into foo (foo_id) values(default);
pg=> insert into foo (foo_id) values(default);
pg=> insert into foo (foo_id) values(default);
pg=> select * from foo;
  foo_id   
------------
 1241588087
 1500453386
 1755259484
(4 rows)

3
我在您的问题下添加了评论,后来意识到我应该更好地解释一下自己...对此我深感抱歉。
您可以拥有第二个关键字 - 不是主键 - 可以向用户显示。该关键字可以使用您所描述的哈希函数的主键作为种子,并用于查找。该关键字将由插入后的触发器生成(这比尝试确保操作的原子性要简单得多),并且
这就是您与客户共享的关键字,而不是PK。我知道有争议(尽管我不明白为什么)是否将PK对用户应用程序可见。现代数据库设计实践和我的个人经验都似乎表明,PK不应该对用户可见。他们倾向于给它们附加含义,并且随着时间的推移,这是非常糟糕的事情 - 无论该关键字是否具有检查数字。
您的连接仍将使用PK执行。这个生成的其他关键字只是用于客户端查找。他们是面孔,PK是内脏。
希望这可以帮助您。
编辑:对于数据库设计,“正确”或“错误”几乎没有什么可说的。有时候这归结为选择。我认为您面临的选择最好保持PK不变,创建一个次要关键字 - 就是这样。

2
我认为你过于复杂化了这个问题。为什么不让数据库发挥它的优势,让它确保原子性并确保相同的id不会被重复使用呢?为什么不使用postgresql SERIAL类型,并获得自动生成的代理主键,就像SQL Server或DB2中的整数IDENTITY列一样呢?在该列上使用这个代理主键。而且它将比您的用户定义函数更快。
我赞成隐藏此代理主键并使用一个公开的辅助键(带有唯一约束),以在您的接口中查找客户端。
您是否正在使用序列,因为您需要跨多个表使用唯一标识符?这通常表明您需要重新考虑您的表设计,并将那些几个表合并为一个,并使用自动生成的代理主键。
还请参见此处

2
你如何生成随机且唯一的ID是一个有用的问题,但你似乎在做一个关于何时生成它们的逆向假设!我的观点是,你不需要在创建行时生成这些ID,因为它们与插入的数据本质上是独立的。我所做的是预先生成未来使用的随机ID,这样我可以花费自己的时间并绝对保证它们是唯一的,并且在插入时没有任何处理要完成。例如,我有一个订单表,其中包含order_id。当用户输入订单时,该ID会即时生成,以1、2、3等方式增量。用户不需要看到此内部ID。然后我有另一个表——random_ids,其中包含(order_id,random_id)。我有一个例行程序,每晚都会预加载足够的行以覆盖可能在接下来的24小时内插入的订单。(如果我一天内收到10000个订单,那将是一个好问题!)这种方法保证了唯一性,并将任何处理负荷从插入事务中移开,并转移到批处理例程中,在那里不会影响用户。

0
你最好的选择可能是某种哈希函数,然后在末尾添加一个校验和。

0

如果您不经常使用此功能(您不是每秒钟都有新客户,对吧?),那么可以只获取一个随机数,然后尝试插入记录。当唯一约束冲突导致插入失败时,请准备好使用另一个数字重试插入。

我建议使用1000000到999999之间的数字(相同长度的可能数字为900000),并使用UPC或ISBN 10算法进行检查位。使用2个检查位会更好,因为它们可以消除99%的人为错误,而不是9%。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接