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

如何在PostgreSQL中实现类CockroachDB的unique_rowid()功能

如何在PostgreSQL中实现类CockroachDB的unique_rowid()功能

我太懂你这种迁移后的需求了——从CockroachDB转PostgreSQL,就想把顺手的unique_rowid()给搬过来,要的就是64位BIGINT、时间戳排序、高并发下绝不重复,对吧?下面给你几个落地的方案,首推第一个,简单靠谱还贴合需求:

方案一:自定义函数+序列(推荐)

这个方案完全复刻了CockroachDBunique_rowid()的核心逻辑:用时间戳的高位保证有序性,用序列的自增值保证同一时间点内的唯一性,完美满足你的三个要求。

步骤1:创建专用序列

先建一个序列来处理同一微秒内的并发请求,设置缓存可以大幅提升高并发下的性能:

CREATE SEQUENCE unique_rowid_seq CACHE 1000;

注:CACHE值可以根据你的并发量调整,比如并发极高可以设为10000,默认1也没问题,只是性能稍差。

步骤2:实现自定义函数

写一个PL/pgSQL函数,把时间戳和序列值组合成64位BIGINT:

CREATE OR REPLACE FUNCTION unique_rowid()
RETURNS BIGINT AS $$
BEGIN
  -- 把当前微秒时间戳左移16位,腾出后16位放序列值
  RETURN (EXTRACT(EPOCH FROM clock_timestamp()) * 1000000)::BIGINT << 16 
         -- 取序列的下一个值,只保留后16位,避免溢出
         | nextval('unique_rowid_seq')::BIGINT & 65535;
END;
$$ LANGUAGE plpgsql VOLATILE;

为什么用clock_timestamp()而不是now()?因为now()返回的是事务开始的时间,高并发下同一事务里的多次调用会得到相同时间,而clock_timestamp()是函数调用瞬间的真实时间,能保证时间戳的实时性。

步骤3:在表中使用

建表时直接把这个函数设为ID列的默认值就行:

CREATE TABLE your_table (
  id BIGINT PRIMARY KEY DEFAULT unique_rowid(),
  -- 其他列示例
  content TEXT NOT NULL
);

方案二:基于随机数的简化实现(适合低并发场景)

如果你的系统并发不高,也可以不用序列,改用随机数填充后16位,但这种方式在极高并发下有极小的概率出现重复,所以只推荐给低流量场景:

CREATE OR REPLACE FUNCTION unique_rowid_low_concurrency()
RETURNS BIGINT AS $$
BEGIN
  RETURN (EXTRACT(EPOCH FROM clock_timestamp()) * 1000000)::BIGINT << 16 
         | floor(random() * 65536)::BIGINT;
END;
$$ LANGUAGE plpgsql VOLATILE;

警告:高并发下重复概率会上升,不建议用于生产核心表。

关键注意事项

  • 时间有序性:因为ID的高位是微秒时间戳,所以新插入的记录ID一定比旧的大(除非人为修改系统时间),完全符合时间排序的要求。
  • 64位范围:48位的微秒时间戳足够用到10895年,远超出业务系统的生命周期,不用担心溢出问题。
  • 性能优化:序列的CACHE参数是关键,合理设置能减少PostgreSQL对序列的磁盘IO操作,提升高并发下的插入速度。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 09:54:38