如何为gen_random_uuid()设置种子以实现测试环境UUID一致?
问题
我有如下表结构:
CREATE TABLE my_table ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, . . );
出于测试需求,我希望每次测试运行时都能生成相同的id,方便对比测试日志与结果。有没有类似setseed(x)作用于random()的函数,能用来控制gen_random_uuid()的生成?
gen_random_uuid()返回的是不含时间戳的版本4 UUID,理论上应该可以实现种子控制。我虽然能基于random()编写自定义函数my_gen_random_uuid(),但速度会慢很多。
举个例子,执行以下查询时,dice列每次都返回相同值,但uuid列每次都变,这不是我想要的结果:
select setseed(1/7.0); select floor(1+random()*6) dice, gen_random_uuid() from generate_series(1,7) order by 1,2;
查询结果:
dice | gen_random_uuid ------+-------------------------------------- 2 | 27539fb8-da6d-4976-945e-1a84948b6d1f 3 | 3969c800-70a2-48d0-b3e4-13edf02ee7fa 3 | b68949b5-67b2-4786-b1f4-23654439e037 3 | df1061d1-e890-4222-a4ea-d42c90d15435 4 | ef1ce38e-1199-4101-9e40-a0eb930c492b 5 | 81fe87fa-eab5-4229-b591-d83ae528353b 6 | 54c61441-70e5-4d83-a08e-7b3662d20970 (7 rows)
解决方案
PostgreSQL原生的gen_random_uuid()并不支持通过setseed()来控制随机种子,因为它使用的是操作系统提供的加密安全随机数生成器(比如Linux下的/dev/urandom),和random()依赖的伪随机数生成器不属于同一体系。
如果想要在测试中生成可复现的UUID,有几个可行方案:
- 自定义伪随机UUID生成函数:虽然性能不如原生函数,但测试场景下通常可以接受。基于
random()和setseed()实现的示例如下:
CREATE OR REPLACE FUNCTION seeded_gen_random_uuid() RETURNS uuid AS $$ DECLARE bytes bytea; BEGIN bytes := decode(lpad(to_hex(floor(random()*4294967296)::bigint), 8, '0') || lpad(to_hex(floor(random()*4294967296)::bigint), 8, '0') || lpad(to_hex(floor(random()*4294967296)::bigint), 8, '0') || lpad(to_hex(floor(random()*4294967296)::bigint), 8, '0'), 'hex'); -- 设置版本4和变体标识 bytes := set_byte(bytes, 6, (get_byte(bytes, 6) & 0x0F) | 0x40); bytes := set_byte(bytes, 8, (get_byte(bytes, 8) & 0x3F) | 0x80); RETURN encode(bytes, 'hex')::uuid; END; $$ LANGUAGE plpgsql VOLATILE;
使用前先执行setseed(1/7.0);,再调用这个函数就能得到可复现的UUID。
- 临时替换表默认值:测试期间将表的
id默认值改为上述自定义函数,测试完成后再改回gen_random_uuid()。 - 使用固定UUID数据集:预先生成一批固定UUID,测试时直接插入这些值,完全规避随机生成的不确定性。
内容的提问来源于stack exchange,提问作者Kjetil S.
相关产品推荐
相关产品推荐

