You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL中冲突重roll的随机唯一int8默认值实现方案

解决方案:PostgreSQL中生成随机唯一的大范围int8并支持批量插入

刚好处理过类似的需求,结合PostgreSQL的特性,给你整理一套符合惯用写法、性能可靠的方案,分场景拆解:

一、先打基础:确保唯一性约束

不管哪种方案,第一步必须给person表的id列加唯一约束(或者主键),这是保证唯一性的底层保障:

ALTER TABLE person ADD CONSTRAINT person_id_unique UNIQUE (id);

二、小范围场景(如0-999999)的批量插入方案

针对你要插入Paul、Kelly、David、Katie的示例,这里有两种实用方法:

方法1:纯SQL批量生成候选ID(高效首选)

这种方法适合批量插入数量远小于范围总数量的情况,纯SQL实现,符合PostgreSQL惯用写法:

WITH new_names(name) AS (
    VALUES ('Paul'), ('Kelly'), ('David'), ('Katie')
),
available_ids AS (
    -- 生成比需要数量多的随机ID,排除已存在的
    SELECT DISTINCT floor(random() * 999999)::int8 AS id
    FROM generate_series(1, 10) -- 生成10个候选,远大于需要的4个,降低冲突概率
    WHERE floor(random() * 999999)::int8 NOT IN (SELECT id FROM person)
    LIMIT (SELECT COUNT(*) FROM new_names) -- 只取需要的数量
)
INSERT INTO person(id, name)
SELECT a.id, n.name
FROM new_names n
CROSS JOIN LATERAL (SELECT id FROM available_ids LIMIT 1 OFFSET ROW_NUMBER() OVER () - 1) a;

说明一下:generate_series生成足够多的候选ID,用DISTINCT去重+NOT IN排除已存在的ID,最后通过ROW_NUMBER()把唯一ID逐个分配给待插入的名字。只要插入数量远小于范围总量(比如你这里插4条,范围有100万),几乎不会出现候选ID不够的情况,批量插入效率很高。

方法2:自定义函数循环生成(绝对保证成功)

如果需要绝对保证插入成功(哪怕范围剩余ID不多),可以写个PL/pgSQL函数循环生成:

CREATE OR REPLACE FUNCTION generate_unique_person_id() RETURNS int8 AS $$
DECLARE
    new_id int8;
BEGIN
    LOOP
        new_id := floor(random() * 999999)::int8;
        -- 检查是否存在,不存在则返回
        IF NOT EXISTS (SELECT 1 FROM person WHERE id = new_id) THEN
            RETURN new_id;
        END IF;
    END LOOP;
END;
$$ LANGUAGE plpgsql VOLATILE;

然后批量插入时直接调用:

INSERT INTO person(id, name)
VALUES
    (generate_unique_person_id(), 'Paul'),
    (generate_unique_person_id(), 'Kelly'),
    (generate_unique_person_id(), 'David'),
    (generate_unique_person_id(), 'Katie');

这个函数逻辑很直白:循环生成随机ID,每次生成后检查库里有没有,找到不存在的就返回。好处是绝对能拿到唯一ID,哪怕范围里剩余的ID不多也没问题,适合小批量插入场景。

三、大范围int8的均匀随机生成(解决random()精度问题)

当你需要生成的范围超过2^53时,random() * n就不靠谱了——因为PostgreSQL的random()返回的是double precision类型,只有53位有效精度,超过这个范围的话,会有很多整数没法被表示出来,导致随机分布出现间隙。这时候我们用pgcrypto扩展的gen_random_bytes()来生成真正的随机字节,转换为int8,完美解决这个问题。

1. 先启用pgcrypto扩展

CREATE EXTENSION IF NOT EXISTS pgcrypto;

2. 生成任意范围的均匀随机int8(拒绝采样法)

以下函数可以生成[min_val, max_val]范围内的均匀随机int8,避免模运算导致的分布偏差:

CREATE OR REPLACE FUNCTION generate_random_int8(min_val int8, max_val int8) RETURNS int8 AS $$
DECLARE
    range_size int8 := max_val - min_val + 1;
    random_bytes bytea;
    random_int int8;
BEGIN
    -- 处理范围无效的情况
    IF min_val > max_val THEN
        RAISE EXCEPTION 'min_val must be <= max_val';
    END IF;

    -- 如果范围只有一个值,直接返回
    IF range_size = 1 THEN
        RETURN min_val;
    END IF;

    LOOP
        -- 生成8字节随机数据,转换成int8
        random_bytes := gen_random_bytes(8);
        random_int := ('x' || encode(random_bytes, 'hex'))::bit(64)::int8;

        -- 只保留在[0, range_size-1]范围内的值,保证分布均匀
        IF random_int >= 0 AND random_int < range_size THEN
            RETURN min_val + random_int;
        END IF;
    END LOOP;
END;
$$ LANGUAGE plpgsql VOLATILE;

为啥用拒绝采样法?因为如果范围大小不是2的幂,直接用模运算(random_int % range_size)会导致某些值出现的概率略高,分布不均匀。拒绝采样法会丢弃超出范围的随机值,只保留符合要求的,这样就能保证每个值的概率完全均匀。而且gen_random_bytes()生成的是真正的随机字节,满足你“不可预测”的要求,比序列加密方案靠谱多了。

3. 结合唯一性的大范围批量插入

把上面的随机生成函数和唯一性检查结合,修改成生成唯一大ID的函数(这里默认范围是1到int8的最大值):

CREATE OR REPLACE FUNCTION generate_unique_large_id(min_val int8 = 1, max_val int8 = 9223372036854775807) RETURNS int8 AS $$
DECLARE
    new_id int8;
BEGIN
    LOOP
        new_id := generate_random_int8(min_val, max_val);
        IF NOT EXISTS (SELECT 1 FROM person WHERE id = new_id) THEN
            RETURN new_id;
        END IF;
    END LOOP;
END;
$$ LANGUAGE plpgsql VOLATILE;

批量插入时直接调用即可:

INSERT INTO person(id, name)
VALUES
    (generate_unique_large_id(), 'Paul'),
    (generate_unique_large_id(), 'Kelly'),
    (generate_unique_large_id(), 'David'),
    (generate_unique_large_id(), 'Katie');

四、性能优化小tips

  • 批量插入量大的话,优先用CTE一次性生成候选ID的方法,减少函数调用的开销。
  • 如果要插上千条甚至更多,可以先把已有的ID导入临时表,生成候选ID时用临时表做排除查询,比直接查主表快很多。
  • 别忘了id列的唯一约束会自动创建索引,这个索引是保证EXISTS检查和NOT IN查询快的关键,千万别删。

内容的提问来源于stack exchange,提问作者semicolon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 09:17:58