【问题标题】:Replacing sequence with random number用随机数替换序列
【发布时间】:2010-12-13 19:47:54
【问题描述】:

我想用我自己定制的 id 生成器替换我在我的 postgresql 数据库中用于 id 的一些序列。生成器将产生一个随机数,最后带有一个校验位。所以这个:

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(生成的序列号在 分贝)。既然我这么沟通 与客户的号码是 对我的竞争对手来说相当简单的任务 监控我的业务(有 其他编号,例如发票编号和 订购具有相同的 nr 特性)。就是这个监控我 想做一点 更难(注意:并非不可能,但 更难)。

  2. 为什么是校验位?

    在谈论隐藏序列号之前,我在 ordernr 中添加了一个校验位,因为在制作过程中的某些时候手指很笨拙,我的想法是,这将是一个很好的做法,以供将来保留。

阅读讨论后,我当然可以看到我的方法不是解决我的问题的最佳方法,但我对如何解决它没有其他好主意,所以请在这里帮助我。

  1. 我是否应该添加一个额外的列来放置我向客户公开的 id 并将序列号作为主键?
  2. 如何生成要以合理有效的方式公开的 ID?
  3. 校验位是否必要?

【问题讨论】:

  • 我不是在批评,只是想理解。为什么要在代理主键中添加校验位?为什么要与客户共享该主键?
  • 你为什么要这样做呢?它给你带来了什么?

标签: sql postgresql random


【解决方案1】:

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

大多数加密密码适用于 64 位或更大的块,但 PostgreSQL wiki 有一个适用于(32 位)int 类型的 example PL/pgSQL procedure for a "non-cryptographic" cipher 函数。免责声明:我自己没有尝试过使用此功能。

要将其用作主键,请从 wiki 页面运行 CREATE FUNCTION 调用,然后在 empty 表上执行:

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)

【讨论】:

    【解决方案2】:

    我在你的问题中添加了我的评论,然后意识到我应该更好地解释自己......我很抱歉。

    您可以拥有用户可见的第二个键 - 而不是主键。该键可以将主键用作您描述的散列函数的种子,并用作您用来进行查找的键。该密钥将由插入后的触发器生成(这比尝试确保操作的原子性要简单得多)和

    这是您与客户共享的密钥,而不是 PK。我知道 PK 是否对用户应用程序不可见存在争议(尽管我不明白为什么)。现代数据库设计实践以及我的个人经验似乎都表明 PK 不应该对用户可见。他们倾向于赋予他们意义,随着时间的推移,这是一件非常糟糕的事情——不管他们是否在密钥中有一个校验位。

    您的连接仍将使用 PK 完成。这个其他生成的密钥应该只用于客户端查找。他们是脸,PK是胆。

    希望对您有所帮助。

    编辑:FWIW,关于数据库设计中的“正确”或“错误”几乎没有什么可说的。有时它归结为一个选择。我认为您所面临的选择将更好地服务于单独保留 PK 并创建辅助密钥 - 仅此而已。

    【讨论】:

      【解决方案3】:

      我认为你过于复杂了。为什么不让数据库做它最擅长的事情,让它处理原子性并确保相同的 id 不会被使用两次?为什么不使用 postgresql SERIAL 类型并获取自动生成的代理主键,就像 SQL Server 或 DB2 中的整数 IDENTITY 列一样?而是在列上使用它。另外,它会比您的用户定义函数更快。

      我同意隐藏此代理主键并使用公开的辅助键(具有唯一约束)在您的界面中查找客户端。

      您是否因为需要跨多个表的唯一标识符而使用序列?这通常表明您需要重新考虑表设计,并且可能应该将这几个表合并为一个,并使用自动生成的代理主键。

      另见here

      【讨论】:

        【解决方案4】:

        如何生成随机且唯一的 id 是一个有用的问题 - 但您似乎对 何时 生成它们做出了适得其反的假设!

        我的观点是,您不需要在创建行时生成这些 id,因为它们本质上独立于插入的数据。

        我所做的是预先生成随机 id 以供将来使用,这样我就可以度过自己的美好时光并绝对保证它们是独一无二的,并且在插入时无需进行任何处理。

        例如,我有一个带有 order_id 的订单表。此 ID 在用户输入订单时即时生成,以 1、2、3 等方式永远递增。用户不需要看到这个内部 id。

        然后我有另一个表-带有(order_id,random_id)的random_ids。我有一个每天晚上运行的例程,它预先加载这个表有足够的行来覆盖未来 24 小时内可能插入的订单。 (如果我在一天内收到 10000 个订单,我会遇到问题 - 但这是一个很好的问题!)

        这种方法保证唯一性,并将任何处理负载从插入事务转移到批处理例程中,不会影响用户。

        【讨论】:

          【解决方案5】:

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

          【讨论】:

            【解决方案6】:

            如果您不经常使用此功能(您不是每秒都有一个新客户,是吗?),那么只需获取一个随机数然后尝试插入记录是可行的。当它因违反唯一约束而失败时,请准备好用另一个数字重试插入。

            我会使用数字 1000000 到 999999(900000 个可能的相同长度的数字)并使用 UPC or ISBN 10 algorithm 校验数字。 2 个校验位会更好,因为它们可以消除 99% 的人为错误,而不是 9%。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2021-03-26
              • 2018-04-03
              • 1970-01-01
              • 2012-06-24
              • 2020-08-14
              • 2010-11-03
              • 1970-01-01
              相关资源
              最近更新 更多