如何在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
相关产品推荐
相关产品推荐

