如何在Postgres 9.6+中生成长度为N的随机,唯一的字母数字ID?

伊恩·斯托姆·泰勒(Ian Storm Taylor)

我已经在StackOverflow上看到了许多不同的解决方案这些解决方案跨越了许多年和许多Postgres版本,但是具有一些新功能,例如gen_random_bytes我想再次询问是否在新版本中有更简单的解决方案。

给定的ID包含a-zA-Z0-9大小取决于使用位置,例如...

bTFTxFDPPq
tcgHAdW3BD
IIo11r9J0D
FUW5I8iCiS

uXolWvg49Co5EfCo
LOscuAZu37yV84Sa
YyrbwLTRDb01TmyE
HoQk3a6atGWRMCSA

HwHSZgGRStDMwnNXHk3FmLDEbWAHE1Q9
qgpDcrNSMg87ngwcXTaZ9iImoUmXhSAv
RVZjqdKvtoafLi1O5HlvlpJoKzGeKJYS
3Rls4DjWxJaLfIJyXIEpcjWuh51aHHtK

(就像Stripe使用ID一样。)

在Postgres 9.6+中,如何通过一种简便的方法为不同的用例指定不同的长度,如何随机,安全地生成它们(以减少冲突并降低可预测性)?

我认为理想情况下,该解决方案应具有类似于以下内容的签名:

generate_uid(size integer) returns text

size可根据您自己的权衡来定制where,以减少冲突的机会,并减少字符串的可用性。

据我所知,它必须用于gen_random_bytes()代替random()真正的随机性,以减少被猜测的机会。

谢谢!


我知道有gen_random_uuid()UUID,但在这种情况下我不想使用它们。我正在寻找能使我获得与Stripe(或其他)使用的ID类似的ID,看起来像:ID"id": "ch_19iRv22eZvKYlo2CAxkjuHxZ"尽可能短,但仍仅包含字母数字字符。

这个要求也是为什么encode(gen_random_bytes(), 'hex')在这种情况下不太合适的原因,因为它会减少字符集,从而迫使我增加字符串的长度以避免冲突。

我目前正在应用程序层中进行此操作,但我希望将其移至数据库层以减少相互依赖性。在应用程序层中执行此操作的Node.js代码如下所示:

var crypto = require('crypto');
var set = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

function generate(length) {
  var bytes = crypto.randomBytes(length);
  var chars = [];

  for (var i = 0; i < bytes.length; i++) {
    chars.push(set[bytes[i] % set.length]);
  }

  return chars.join('');
}
伊恩·斯托姆·泰勒(Ian Storm Taylor)

弄清楚了,下面是执行此操作的函数:

CREATE OR REPLACE FUNCTION generate_uid(size INT) RETURNS TEXT AS $$
DECLARE
  characters TEXT := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  bytes BYTEA := gen_random_bytes(size);
  l INT := length(characters);
  i INT := 0;
  output TEXT := '';
BEGIN
  WHILE i < size LOOP
    output := output || substr(characters, get_byte(bytes, i) % l + 1, 1);
    i := i + 1;
  END LOOP;
  RETURN output;
END;
$$ LANGUAGE plpgsql VOLATILE;

然后只需执行以下操作即可:

generate_uid(10)
-- '3Rls4DjWxJ'

警告

这样做时,您需要确保创建的ID的长度足以避免随着时间的推移随着创建的对象数量的增加而发生冲突由于Birthday Paradox,这可能是违反直觉的因此,您可能想要的长度大于(或大于)10任何通常合理创建的对象的长度,我只是10作为一个简单的示例。


用法

定义函数后,可以在表定义中使用它,如下所示:

CREATE TABLE collections (
  id TEXT PRIMARY KEY DEFAULT generate_uid(10),
  name TEXT NOT NULL,
  ...
);

然后在插入数据时,如下所示:

INSERT INTO collections (name) VALUES ('One');
INSERT INTO collections (name) VALUES ('Two');
INSERT INTO collections (name) VALUES ('Three');
SELECT * FROM collections;

它将自动生成id值:

    id     |  name  | ...
-----------+--------+-----
owmCAx552Q | ian    |
ZIofD6l3X9 | victor |

带前缀的用法

或者,也许您想在查看日志或调试器中的单个ID时添加一个前缀以便于使用(类似于Stripe的用法),如下所示:

CREATE TABLE collections (
  id TEXT PRIMARY KEY DEFAULT ('col_' || generate_uid(10)),
  name TEXT NOT NULL,
  ...
);

INSERT INTO collections (name) VALUES ('One');
INSERT INTO collections (name) VALUES ('Two');
INSERT INTO collections (name) VALUES ('Three');
SELECT * FROM collections;

      id       |  name  | ...
---------------+--------+-----
col_wABNZRD5Zk | ian    |
col_ISzGcTVj8f | victor |

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何从带有条件的字母表中生成长度为N的所有单词

生成长度为6的字母和数字的所有可能组合时出现MemoryError

如何在python中生成随机但唯一的数字?

如何在C#中生成具有3个字母和6个数字的随机字母数字数组?

如何理解'sort -uk 4n -uk 6n -uk 9n','sort -uk 4 -uk 6 -uk 9'和'sort -uk 4,4 -uk 6,6 -uk 9,9'之间的区别?

如何在Django中生成YouTube风格的唯一字母数字ID模型ID

如何添加存储在模型中的唯一随机生成的6位数字密钥

如何在rails中生成固定长度(最多10个字符)的唯一ID数字

如何生成数字序列1,2,5,6,9,10,13,14,

如何在Python中生成唯一的回送IPv6地址?

如何在MATLAB中创建模式为[1 2 5 6 9 10 13 14 17 18 ....]的向量?

如何生成具有第一个字符串的递增字母数字应该是数字,但其余字符串长度为 6 的字母数字序列

JavaScript,生成一个长度为9个数字的随机数

在Ruby中使用SecureRandom生成长度为6的随机数

如何在Python中生成唯一的随机浮点列表

如何在PHP中生成唯一的随机数?

如何在 MySQL 的触发器中通过此代码生成超过 6 个字符的随机字母数字?

如何在Postgres 9.x中插入now()+ INTERVAL

如何在PHP中生成唯一范围的数字

PHP:如何生成用于随机链接的随机,唯一的字母数字字符串?

如何在上午9:00至下午6:00(星期一至星期五)的每个小时内设置Cron作业

如何在Java中生成5个字符的唯一字母数字值?

“ expect(9).be> 6”如何是有效表达式?

如何在Symfony 4中生成唯一的ID?

如何在Java(整数)中生成唯一ID?

如何在mongodb中生成唯一的对象ID

Xcode 6与iOS 9?

如何在同时运行的 Jest 测试中生成从 postgres sql 获取的唯一编号

如何在ES6中生成不重复数字的数组?