UUIDv7 登陆 PostgreSQL 18

PostgreSQL 18 正式发布。本次版本的众多改进中,最值得关注的是对 UUIDv7 的支持——这种基于时间戳的 UUID 变体能与 btree 索引完美配合。本文将探讨 UUID 的基本原理、UUIDv7 的独特价值,以及在 Postgres 中如何有效运用它。
PostgreSQL 18
PostgreSQL 18 版本已于日前发布。本次发布包含大量新特性、优化与漏洞修复。社区成员仍被鼓励积极试用并反馈问题。
本次版本亮点包括:
- 异步I/O(基于io_uring)——顺序扫描与vacuum操作速度提升2-3倍
- 多列B树索引支持跳过扫描 + 更智能的OR/IN优化
- 重大升级过程中保留规划器统计信息
- UUIDv7函数支持
- 虚拟生成列功能
- OAuth登录机制 + md5弃用警告
- EXPLAIN ANALYZE新增I/O、CPU、WAL消耗统计
- 时序约束支持、非确定性排序规则下的LIKE操作、大小写转换
- 新版通信协议:3.2(自2003年以来首次升级!)
虽然uuidv7()并非最令人兴奋的功能(非同步I/O才是),但它可能是最受期待的特性。该功能曾接近在PHP 17中实现,许多用户对其未能入选感到有些失望。我对此充满期待,因此决定试用测试版并撰写博客文章。
什么是UUID?为何如此实用?
UUID是128位值,用于为各类对象(从交易记录到企业实体)生成标识符。其设计确保在时空维度上具有唯一性,且能高效批量生成,无需依赖中心化服务。
传统关系型数据库采用自动递增类型(如SERIAL或identity)生成唯一标识符。这种方式在单台机器上效率较高(尽管存在局限),但当需要横向扩展时,就必须采用能在所有节点保持唯一性的标识符生成方案。Instagram团队在分片Postgres数据库时,撰写了关于迁移至UUID的简短博客。
在以下常见场景中,UUID作为数据库主键具有显著优势:
- 分布式数据库中生成唯一ID:
尽管多数分布式数据库支持自动递增(标识)列,但其存在局限性和性能问题。 - 不可预测的公共标识符:
正确生成的UUID无法被猜测、预测或用于推断系统信息。例如,若使用自动递增作为客户标识符,攻击者可扫描所有现有标识符并尝试使用,还能推测下一个标识符并估算客户数量。 - 允许客户端生成标识符:
使用UUID可让客户端在无需与服务器协调的情况下生成标识符。这在移动应用和无服务器环境中尤为实用,能最大限度减少与服务器的通信。
基于这些优势,UUID被广泛用作数据库的主键。但数据库中使用UUID也存在三大问题:
- 排序:UUID无法按值进行有意义的排序。
- 索引局部性:新UUID在索引中分布分散,导致插入操作随机分布于索引各处。这可能引发索引膨胀及其他性能问题,详见此博客文章中的图表。
- 占用空间:UUID是128位值。多数开发者默认使用
INT(32位)或BIGINT(64位)作为主键。对于包含大量微小记录的表,这会造成显著的资源开销。
正如下一节所述,UUIDv7解决了上述三项问题中的两项。
当磁盘空间或网络带宽受限时,UUID的大小可能成为问题,但值得注意的是现代CPU可通过单条指令(CMEQ,SIMD指令集的一部分)比较128位值,因此数据库对UUID的操作已高度优化。关键在于确保数据库和应用程序中均使用UUID的二进制表示形式(正确的UUID类型),而非字符串表示形式。
为何选择UUIDv7?
UUID最早于2005年在RFC 4122中标准化。该RFC定义了5种UUID变体,其中第1和第4变体最为常用。该规范后经修订,在2024年5月发布的RFC 9562中新增了第6-8种变体(尽管首个公开工作草案早在2020年就已发布)。祝贺RFC 9562与UUIDv7诞生!
为阐明规范更新的动因,RFC 9562 讨论了将 UUID 用作数据库主键的常见场景:
UUID 在数据库键领域广受欢迎…但由[RFC4122]最初定义的 UUID 版本 1-5 缺乏某些理想特性,例如:
非时间排序的UUID版本(如第5.4节所述的UUIDv4)具有较差的数据库索引局部性。这意味着连续生成的新值在索引中彼此分散,导致插入操作需随机定位。这种特性对常用数据库结构(B树及其变体)造成的性能负面影响可能极为显著。
众多分布式数据库应用及大型应用供应商致力于解决如何创建更优的时间基准排序唯一标识符作为数据库键的问题。过去十余年间,针对同一问题涌现出大量略有差异的实现方案。
该RFC文件随后规定了16种(!)非标准UUID实现方案,每种方案各有取舍。其中包括广受欢迎的ULID、Twitter的Snowflake、Instagram的ShardId等众多方案。新规范设计过程中对所有这些方案均进行了评估。
虽然新RFC定义了3种UUID变体,但真正值得关注的只有UUIDv7。UUIDv6仅为向后兼容而保留——RFC明确指出“不涉及旧版UUIDv1的系统应改用UUIDv7”。而UUIDv8则为实验性及厂商专属扩展提供了格式规范。
UUIDv7 同时解决了排序和索引局部性问题。它采用 Unix 纪元时间戳作为最高 48 位,其余 74 位保留为随机值(额外位用于版本和变体)。这使得 UUID 既可按时间顺序排序又具有唯一性。该标准还提供在UUID中包含毫秒级时间戳和/或精心初始化的计数器的选项,以支持单秒内的排序(如有需要)。因此,UUIDv7非常适合用作数据库的主键——它保证唯一性、可排序性,并具有良好的索引局部性。
PostgreSQL 18 中的 UUIDv7
在 PostgreSQL 18 之前,UUIDv7 未获得原生支持。内置函数 gen_random_uuid() 生成的是 UUIDv4,而流行的 uuid-ossp 扩展虽增加了对其他 UUID 变体的支持,但仅限于 RFC 4122 规定的变体。
PostgreSQL 18新增函数uuidv7()用于生成UUIDv7值。其实现包含标准允许但非强制要求的12位亚毫秒级时间戳小数部分,确保同一Postgres会话(即相同后端进程)生成的所有UUIDv7值具有单调性。
为保持命名一致性,PostgreSQL 18 将 gen_random_uuid() 别名化为 uuidv4()。
调用 uuidv7() 将生成一个新的 UUIDv7 值,其中时间戳为当前时间。若需为其他时间生成 UUIDv7 值,可向函数传递可选的 interval 参数。
Postgres 现有用于从 UUID 中提取时间戳和版本的函数也已更新以支持 UUIDv7。以下是新函数的使用示例:
postgres=# select uuidv7();
uuidv7
--------------------------------------
0196ea4a-6f32-7fd0-a9d9-9c815a0750cd
(1 row)
postgres=# select uuidv7(INTERVAL '1 day');
uuidv7
--------------------------------------
0196ef74-8d09-77b0-a84b-5301262f05ad
(1 row)
postgres=# SELECT uuid_extract_version(uuidv4());
uuid_extract_version
----------------------
4
(1 row)
postgres=# SELECT uuid_extract_version(uuidv7());
uuid_extract_version
----------------------
7
(1 row)
postgres=# SELECT uuid_extract_timestamp(uuidv7());
uuid_extract_timestamp
----------------------------
2025-05-19 20:50:40.381+00
(1 row)
postgres=# SELECT uuid_extract_timestamp(uuidv7(INTERVAL '1 hour'));
uuid_extract_timestamp
----------------------------
2025-05-19 21:50:59.388+00
(1 row)
postgres=# SELECT uuid_extract_timestamp(uuidv7(INTERVAL '-1 day'));
uuid_extract_timestamp
----------------------------
2025-05-18 20:51:15.774+00
(1 row)
将 uuidv7() 用作表的主键操作简单直观,配合时间戳提取功能,既能轻松将UUID作为可排序键使用,还能追溯记录的创建时间:
CREATE TABLE test (
id uuid DEFAULT uuidv7() PRIMARY KEY,
name text
);
INSERT INTO test (name) VALUES ('foo');
INSERT INTO test (name) VALUES ('bar');
-- this will be sorted to the beginning of the list since we are making it 1h older than the other two
INSERT INTO test (id, name) VALUES (uuidv7(INTERVAL '-1 hour'), 'oldest');
SELECT uuid_extract_timestamp(id), name FROM test ORDER BY id;
uuid_extract_timestamp | name
----------------------------+--------
2025-05-19 19:55:43.87+00 | oldest
2025-05-19 20:55:01.304+00 | foo
2025-05-19 20:55:01.305+00 | bar
(3 rows)
所有函数均在PostgreSQL文档中详述。若需了解实现细节,可查阅补丁进行查阅。
立即体验!
PostgreSQL 18正式发布后,您可通过常规安装流程使用uuidv7()及所有新增功能。
结语
PostgreSQL 18 带来了经验丰富的开发者会真正欣赏的实用改进。对 UUIDv7 的原生支持虽低调却影响深远,解决了数据库设计中长期存在的痛点。
UUID 一直存在取舍:在分布式系统中安全、保证唯一且生成高效,但用于 B 树索引时存在性能缺陷。UUIDv7实现了两全其美——既具备全局唯一性,又采用与B树索引及高写入负载完美适配的排序方式。Postgres 18更使其使用体验大幅提升。
若您曾因顾虑而犹豫将UUID用作主键,此刻正是重新审视决策的良机。请尝试测试版,在您的模式中进行测试,观察其行为表现。无论您是在构建多租户应用程序,还是仅需更稳定的ID生成方案,UUIDv7都值得关注。
参与早期开发是塑造Postgres未来的最佳途径——请立即创建测试实例,并将您的发现分享给社区。

我们评估了UUIDv7,并认定将其用作主键并不明智。
在某些应用场景中,我们能够控制主键的生成过程,且主键会暴露给终端用户——例如使用基于Rails、Phoenix、Loco、Laravel等框架构建的典型Web应用时。对于这类应用,UUIDv7的时间戳特性存在严重的安全隐患,因此我们更倾向于采用二进制存储的UUIDv4,尽管其效率稍低。
另有一类应用场景:我们既能控制主键生成过程,又能确保主键永不暴露给用户。在此类场景中,UUIDv7在插入和连接操作上效率较低,因此我们倾向于使用BIGSERIAL作为主键,同时采用二进制存储的UUIDv4在URL等场景中向用户展示。
暴露主键为何会损害安全性?若系统安全性*以任何形式*依赖数据库私钥的随机性,那根本存在更严重的问题。主键本就不承担增强安全性的职责。更何况UUIDv7包含6个随机字节,对绝大多数Web应用(包括金融领域)而言,这已足够随机。试想攻击者需要发送多少次请求才能猜中单个UUID(6个随机字节有281万亿种组合,还需准确猜中毫秒级的Unix时间戳)。唯一能想到的例外是将主键当作API密钥使用。
这里的关键点之一是去匿名化和账户关联。假设某个应用中,用户/产品与特定B2B账户的关联关系属于敏感信息——可能是为了竞标公平性需要匿名交互,也可能是有人试图通过抓取“账户X已注册多少用户”这类元数据获取金融优势。
若用户/产品在批量注册或B2B账户开通时被纳入系统,那么任何能返回其UUID的查询操作,都会泄露每个实体的创建时间戳——这种元数据虽不完美,却足以实现用户关联。
通常情况下,UUID的自然排序优势能抵消此风险。但在决定切换至UUIDv7前,仍需权衡此利弊。
> 系统的安全性_在任何情况下_都依赖于数据库私钥的随机性
未公开URL(如YouTube视频)是知名科技公司常用的示例。
> UUIDv7包含6个随机字节
需注意:规范允许74位随机填充。但允许用最多12位替换为更精确的时间戳,以及最多42位的计数器。若能确定时间戳和计数器,随机部分仅提供20位(100万种可能)。
例如Python 3.14rc版本实现的UUIDv7仅包含32位随机位。
核心要点在于:必须确认具体实现方案的行为。
仅32位,因此每微秒可尝试40亿次猜测…即便YouTube每微秒上传100万条视频,你也永远无法在速率限制生效前猜出所有视频。
你混淆了几个概念。32位随机数生成发生在Python实现中,该实现使用了毫秒计数器。
你提供的数字虽可疑,但攻击可行性极高。40亿ID中包含100万ID意味着每次猜测成功率约为1/4000。以每秒一次的频率,一小时内可发起4000次请求。成功攻击只需猜中单个ID,无需枚举全部ID。
啊,我当时查的是pg_uuidv7 Python包。
向后兼容性是个极端的权衡取舍。
无论如何我的评论是夸张了,但核心概念相同:每毫秒处理10000条记录就明白我的意思了。对于99.999%的SQL用例,UUIDv7完全够用。
我之所以极力推荐UUID,是因为职业生涯中三次不得不添加UUID来防止患者数量泄露,避免用户通过递增方式抓取网站数据(还有其他防护措施)。直接用UUID处理一切要简单得多。
不知此处是否适用,但无论时间窗口如何,随机性始终仅限于32位。若您认为这种随机性足以保障安全,便可放心使用——比如保护某只猫咪打碎杯子的私人家庭录像?完全没问题。
企业文件共享端点?不行。请使用另一个基于uuid4的“共享uuid”,并在内部将其映射到主键uuid7。
这让我想起德国坦克问题。虽然问题本质不同,但同样存在看似无害的ID泄露超额信息的案例。德国人泄露了产量数据,而UUID v7则泄露了时间戳信息。
https://en.wikipedia.org/wiki/German_tank_problem
ID其余部分的随机性足以使猜测耗时极长——除非所有坦克都在同一微秒内插入。不过我不确定这算不算UUID的安全问题!
因为任何知道主键的人现在都能知道时间戳。UUID本身泄露了信息。问题不在于它没有增加安全性,而在于它实际上削弱了安全性。
API极有可能记录了插入时间戳。老实说,我宁愿数据排序正确,也不愿幻想泄露插入时间这种极端罕见的情况会导致天塌地陷。通常你反而需要这些信息。
而且我真心不赞成公共服务将主键用于任何重要场景。我更倾向于使用美观或简短的URL。
改进方案可能是:能否自动从UUID的随机位中高效检索记录,用索引替代时间戳?
UUID能还原时间戳?
> 泄露信息
这必须泄露敏感信息才算“削弱安全性”,暗示你依赖时间戳保密性来保障安全。这正是gp提到的“其他问题”之一。
几乎任何信息都能被用于_某种目的_。你忽略了他们关于“对应用程序安全性非关键的信息仍可能因其他原因不宜泄露”的论述。例如:从防损或企业安全角度看,Target和沃尔玛或许不依赖卫星无法拍摄其停车场影像。但这仍会泄露财务分析师不愿知晓的业绩信息。
你用类比而非实例论证观点:类比虽有助于阐释概念,却鲜少精确到能证明逻辑等价性。
若能举例说明泄露数据库条目创建时间戳的危害,讨论你的论点合理性会容易得多。
否则,停车场与数据库创建时间戳之间没有任何实质关联,与你的论点毫无意义。你不能随意泛化所有世间概念就草草收场。
另一帖子明确提到使用创建时间戳来评估平台上企业的增长率。
我的类比本意是为具备基本逻辑推理能力的读者服务,以便更好地解读父母与叔姨辈的回复。
> 具备基本逻辑推理能力的读者
请允许我坦诚相告(绝无讽刺之意):请假设我是个蠢货,因为我完全承认自己可能忽略了显而易见的要点,此刻只是试图更深入理解你的论点。
> 另一条帖子
> 父母及叔姨辈的回复
我已重新阅读本帖中父母/祖辈的回复,试图确认是否遗漏关键信息,但始终找不到通过公开数据库记录时间戳估算网络公司增长率的论述。
我也无法在脑海中构想出实现这种估算的明显系统。我承认这种假想系统几乎肯定存在,但它似乎并不显而易见(至少对我而言),因此很难进行推理和讨论。
我喜欢你。哈哈
山姆·沃尔顿曾驾驶私人飞机载着投资者飞越沃尔玛门店,要求他们统计停车场车辆数量;随后又飞往竞争对手门店重复同样的操作。这段轶事生动展现了商业竞争中真实存在的场景!
示例:若用户ID并非随机生成而是采用Bigserial(自动递增)模式,且通过API暴露,则API客户端可推断出用户在系统中的创建时间。若系统存储的是大规模人群的健康数据,用户年龄便易被推测。诸如此类。这并非安全问题,而是信息治理问题。但确实是个问题。若主张不应暴露这些ID——可以,但无论暴露什么本质上仍是某种标识符。
我始终认为公开使用主键绝非良策。UUID4虽允许用户在URL中插入垃圾数据,但相较于路径名或更简洁的ID,它对网络或用户体验并无裨益。
这取决于主键的熵值大小。
若主键采用单调递增或时间戳机制,恶意攻击者就能轻松遍历API接口。
部署UUIDv7确实需要更深入考量其影响。多数情况下泄露生成时间并无大碍,但某些场景则不然。
一个有趣的折中方案是在API边界将UUIDv7转换为UUIDv4,例如UUIDv47 [1]。另一方面,若采用此方案,也可使用u64主键并进行转换
1: https://github.com/stateless-me/uuidv47
这正是UUID版本应采用字母而非数字标识的原因。各版本并非彼此替代,而是承担不同功能。数字版本号容易让人误解“数字越大越好”,但实际并非如此,也绝非设计初衷。
我怀疑问题在于向终端用户暴露内部ID。相信各位专家早已考虑过这点,但能否解释为何对主键的外部视图使用加密甚至HMAC都不合理?或许因为额外处理成本高于直接使用UUIDv4?对UUIDv7的随机位应用argon2id等密钥派生函数(KDF),似乎能很好地适用于外部ID。
(话说为什么不同类型的UUID要统称为“版本”?)
因为从现在起,在永恒的未来里,任何人只要编写代码将数据从这张表移动到其他地方——无论出于何种目的——都必须牢记:主键暴露了某项数据的创建时间,而这个时间可能与其他信息产生关联。许多人不会注意到这一点,而注意到的人中又会有人采取错误的补救措施。现在你更别想通过简单的数据库视图向任何不该获取创建时间的人或程序提供信息了。
你已使系统变得脆弱不堪。
问题在于为何不使用加密(如sqids/hashids等)来保护公开暴露的代理键,我认为这个回答偏离了重点…代理键理想状态下永远不应暴露(原因远不止信息泄露),因此对其加密是完全合理的做法(互联网各处皆可见此例)。另一方面,将任何形式的uuid用作代理键对数据库引擎而言是极糟糕的做法(徒增负担却毫无收益)
> 你已使系统变得脆弱不堪。
这正是将代理键保留在内部的核心理由——它们本质上应被视为指针,脱离控制的悬空指针极易引发问题。理想情况下,任何暴露在外部且指向代理键的元素都应附带额外信息,以便通过解码实现无效化(如同安全指针机制!)
但能否解释一下,为何对主键的外部视图使用加密甚至HMAC没有意义?
这完全合理,且正是你应该采取的做法——而非为此目的使用UUID作为主键。
这确实合情合理,且是严重被低估的工具。
当分布式系统需要按时间顺序处理海量数据时,UUIDv7才真正适用——典型场景如事件日志表。
正如你所言,其他场景使用它都有些牵强。
正是分布式特性迫使我们在服务器外部创建ID,这使得UUID大显身手,同时也提升了系统可靠性。
去年我去更新身份证时,工作人员说抱歉,(集中式)系统故障了。但在计算机普及前,人们采用更具容错性的本地离线编辑+方便时同步的方式,根本不会出现“抱歉,系统拒绝处理,请重新预约”这种情况。
最近有人分享了加密时间戳部分的方法:
https://news.ycombinator.com/item?id=45275973
我认为隐私比安全更合适。若主键被当作密钥使用,那你的架构设计可能有误。需要加密时该如何操作?
目前正在评估将UUIDv7作为某些库存来源的主键。我认为用于此类场景应该可行,因为它能标识创建时间?大家怎么看?
你必须明确要解决什么具体问题?除非有充分理由使用UUID,否则坚持使用自动递增ID会简单得多。
作为近期刚将部分表从自动递增ID转换为UUID的人,我深有体会:当时这些分片表依赖ID的全局唯一性,且大量使用ID按时间顺序扫描记录。因此UUID既能解决唯一性问题,又能保留时间排序功能。
确实很大程度上取决于规模。库存系统若仅存放数千件商品,UUID只会徒增麻烦却收益甚微。
你的分布式表场景恰是UUIDv7的绝佳应用场景。
这完全是个合理选择,此处多数抱怨都言过其实。若库存有SKU,尽可能在外部/链接及API查询中使用SKU。
若ID信息本身会损害安全性,那应用系统的设计恐怕就是垃圾。
真正的担忧在于隐私问题。
从隐私角度看:
– 序列ID泄露会暴露实体的生成速率和数量,这可能转化为客户数量或销售速率等敏感信息。
– 带时间戳的ID会泄露活动模式。交叉引用数据时问题更严重。
– 随机ID不会泄露任何信息。
—
安全性方面:
– 顺序ID可能被猜中。
性能方面:
– 顺序ID可能导致自生热点。
– 随机ID便于分片,但会增加索引构建、列压缩及插入后排序维护的难度。
– 序列ID泄露实体创建速率与数量,可推算出客户数量或销售速率。
这意味着存在一个返回项目列表的接口,该接口本身可用于推断客户数量或销售频率。这也表明你的安全模型存在漏洞,泄露了客户列表或销售清单——这些数据本就不应被访问。
– 时间戳ID泄露活动模式。当数据交叉引用时,问题将加剧。
若能自由列举项目,攻击者仍可通过捕获现有数据并进行差异比对,推断更新时间与创建时间。
序列ID通常采用两种机制:
– 全局表序列 :: 向所有能创建和查看新ID的用户泄露活动信号。这是在表中创建增量ID时默认采用的简单序列。
– 用户本地序列号 :: 表示单个用户拥有的发票数量,若仅限单用户访问则安全可靠。但此类序列号生成速度较慢且操作较为繁琐。
假设某商店允许用户仅查看自身发票。
– store.com/profile/invoices/{序列号}/
这并不意味着使用随机ID会返回其他用户的数据,因此安全性未必如你所想的那般糟糕。你很可能收到404错误,系统甚至不会承认该ID的存在(但可能存在定时攻击来猜测ID是否有效)。
—
采用时间戳ID确实需要突破单用户数据隔离层。数据库设计本就应防范此类风险——这正是我们对密码添加盐值并仅存储摘要的原因(对吧?)。
为何泄露主键?主键是为数据库管理系统设计的,而非面向终端用户。
主键对应什么?用户若要获取特定数据,必须知道该数据对应的用户可见ID。
若需保留转换层,可用不透明ID掩盖内部ID。此外,分布式场景中常需独立生成新ID,这些ID在节点同步时终将暴露。
好吧,永远别用顺序ID
是的,我正试图想象这样一个世界:物品的创建时间竟能破坏你的安全模型,而每条探索路径都指向系统存在严重的安全漏洞。
我知道我跟踪的对象曾在时间点Y左右于服务X创建了匿名账户。结合其他线索,我锁定了若干可疑账户。创建时间被泄露给我——要么是某个本应无害的漏洞所致,要么是因为写代码的人“无法想象存在这样一个世界:某个项目的创建时间竟能破坏你的安全体系”。我利用创建时间锁定目标账户。
构思这个方案耗时不到15秒。
你只花了15秒是因为这根本不是个好例子——“大约在Y时间”这个表述严重歪曲了概念本质。而“结合其他信息”的设定,意味着某些信息正在助长这种漏洞。
实际情况是,我通常同时掌握“时间点Y”和“其他信息”。你需要将目标从10个账户缩小到1个,或从100个缩小到10个。
绝大多数情况下,数据负载里本就包含时间戳——毕竟多数数据库记录都会保留未屏蔽的createdOn/updatedOn字段用于界面展示。
《UUID趣味探索》将深入探讨此议题,演讲将于2025年9月12日在PGday Lowlands大会举行。
_UUID常遭诟病,主要源于随机分配的UUID对索引的影响。但UUID也为我们提供了16字节的空间进行灵活运用,这反而能成为优势。我们可以利用UUID的空间来构建和编码数据,将其封装到标识符中。这对多租户应用、分片或分区非常有用。UUID还能通过避免泄露顺序分配的ID来提升Web应用的安全性。
> 我们将探讨随机分配UUID、顺序ID以及合理结构化UUID对索引性能的影响。
> 我们将探讨如何从UUID中提取信息、在PostgreSQL中构建UUID,以及评估相关函数的性能表现。
> 最后将介绍如何通过扩展模块为UUID添加位运算支持。
幻灯片 https://www.postgresql.eu/events/pgdaynl2025/schedule/sessio…
直播 https://youtube.com/watch?v=tJYEuIpzch4&t=2h36m
关于Postgres中UUIDv7的旧讨论中有条有趣评论:https://news.ycombinator.com/item?id=39262286
竞争对手挖走销售人员并让他导出Salesforce数据库的可能性高出300%。
这种事发生的频率远超企业愿意承认的程度。
原帖作者不出所料地省略了他们如何抓到这些人的细节。
原帖作者在某条子评论中补充了更多背景:
UUID相较于自动递增ID,难道不具备易于分片的显著优势吗?这通常是关键考量点。
另一个关键优势在于UUID可在客户端生成并提交,这能极大简化代码(比如实现幂等性)。
完全正确
这里95%的评论都脱离实际。
关于UUIDv7的意外之处 https://medium.com/@sergeyprokhorenko777/what-may-surprise-y…
针对此类担忧存在一个理想解决方案…
仅在大规模数据库中将UUIDv7用作主键,且仅限于内部键值。切勿将这些键值暴露给数据库或应用程序外部的任何系统。
使用带有唯一索引的UUIDv4列作为“外部ID”,通过API将其暴露给其他系统。
基本上,为每条记录创建两个ID——一个随机生成的非主键ID,以及一个顺序递增的主键ID。
我在实际系统中实施过这种方案,效果良好。
采用UUID作为记录标识符的核心意义在于防止用户通过递增递减ID来推测相邻记录。若添加顺序编号,岂非背离初衷?
在面向外部的应用程序或API中使用此方案时,添加使用限制似乎更为明智。
即便精确知晓行创建的毫秒级时间戳(实际难以实现),仍需每秒尝试10亿次猜测持续73年。
理想情况下应为API设置速率限制…
UUIDv7包含48位时间戳(其中12位提供亚毫秒精度或随机生成,在PostgreSQL中提供精度),以及62位随机选择的字段。
虽然UUIDv7的生成时间会泄露,但猜测下一个时间戳值仍完全不可行。若每次尝试都需要API请求,62位已足够保障安全性
…而后续系统维护者可能认为“反正这东西不可预测,泄露未加盐的哈希值也没关系”。前提是他们会思考——这本身就存疑。
为何人人都想找借口留下安全漏洞?
下一个ID不能简单地通过加1来获取,对吧?你打算如何推测下一个值?
有人知道针对带有UUIDv7主键和
date_created列的表,是否存在任何优化方案(无论是内部实现还是用户可用的)?不同引擎都提供专为整数序列设计的“快速键”优化——若对比整型/序列键与UUID的性能,不同引擎间的差异可能从显著到令人作呕。
“不可预测的公共标识符” 按X键表示怀疑
这很大程度上取决于UUID类型。例如类型1 UUID仅包含时间戳、MAC地址和“碰撞计数器”——当系统时钟运行过慢或需要批量生成UUID时使用。
本文讨论的是UUID v7规范,因此针对v1的批评在此无关紧要(况且Postgres数据库已长期原生支持v4)。