我已经在StackOverflow上看到了许多不同的解决方案,这些解决方案跨越了许多年和许多Postgres版本,但是具有一些新功能,例如gen_random_bytes我想再次询问是否在新版本中有更简单的解决方案。
gen_random_bytes
给定的ID包含a-zA-Z0-9,其大小取决于使用位置,例如…
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,以降低碰撞的机会,并减小字符串的可用性。
size
据我所知,它必须用于gen_random_bytes()代替random()真正的随机性,以减少被猜测的机会。
gen_random_bytes()
random()
谢谢!
我知道有gen_random_uuid()UUID,但在这种情况下我不想使用它们。我正在寻找能使我获得与Stripe(或其他)使用的ID类似的ID的东西:看起来"id": "ch_19iRv22eZvKYlo2CAxkjuHxZ"尽可能短,同时仍仅包含字母数字字符。
gen_random_uuid()
"id": "ch_19iRv22eZvKYlo2CAxkjuHxZ"
这个要求也是为什么encode(gen_random_bytes(), 'hex')在这种情况下不太合适的原因,因为它会减少字符集,从而迫使我增加字符串的长度以避免冲突。
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(''); }
弄清楚了,下面是执行此操作的函数:
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作为一个简单的示例。
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
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 |