在 PostgreSQL 中作为主键的 UUID 是否会给索引性能带来不良影响?

我使用 PostgreSQL 数据库在 Heroku 的 Rails 中创建了一个应用程序。

它有两个表,可以与移动设备同步,数据可以在不同的地方创建。因此,我有一个 uuid 字段,它是一个字符串,除了自动增量主键之外,还存储 GUID。Uuid 是服务器和客户端之间通信的 uuid。

在服务器端实现了同步引擎之后,我意识到当需要始终在 uuid <-> id 之间进行映射时,这会导致性能问题(在编写对象时,我需要查询 uuid 来在保存前获取 id,而在发送回数据时则相反)。

我现在正在考虑切换到只使用 UUID 作为主键,使写作和阅读更加简单和快速。

我了解到,当使用聚集主键索引时,作为主键的 UUID 有时会导致索引性能不佳(索引碎片)。PostgreSQL 是否存在这个问题,或者可以使用 UUID 作为主键?

今天我已经有了一个 UUID 列,所以从存储的角度来说,这样会更好,因为我删除了常规的 id 列。

27986 次浏览

(我在 Heroku Postgres 工作)

我们在一些系统上使用 UUID 作为主键,它工作得很好。

我建议您使用 uuid-ossp扩展,甚至让 postgres 为您生成 UUID:

heroku pg:psql
psql (9.1.4, server 9.1.6)
SSL connection (cipher: DHE-RSA-AES256-SHA, bits: 256)
Type "help" for help.


dcvgo3fvfmbl44=> CREATE EXTENSION "uuid-ossp";
CREATE EXTENSION
dcvgo3fvfmbl44=> CREATE TABLE test (id uuid primary key default uuid_generate_v4(), name text);
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "test_pkey" for table "test"
CREATE TABLE
dcvgo3fvfmbl44=> \d test
Table "public.test"
Column | Type |              Modifiers
--------+------+-------------------------------------
id     | uuid | not null default uuid_generate_v4()  name   | text |
Indexes:
"test_pkey" PRIMARY KEY, btree (id)


dcvgo3fvfmbl44=> insert into test (name) values ('hgmnz');
INSERT 0 1
dcvgo3fvfmbl44=> select * from test;
id                  | name
--------------------------------------+-------
e535d271-91be-4291-832f-f7883a2d374f | hgmnz
(1 row)

编辑性能影响

它将 一直都是取决于您的工作负载。

整数主键具有局部性的优势,其中 like-data 位置更靠近。这对于诸如 WHERE id between 1 and 10000之类的范围类型查询很有帮助,尽管锁争用更加严重。

如果您的读取工作负载是完全随机的,因为您总是进行主键查找,那么不应该有任何可测量的性能下降: 您只需为更大的数据类型付费。

你给这张桌子写了很多东西吗? 这张桌子很大吗?有可能,虽然我还没有测量过,维持这个指数还是有意义的。但是对于许多数据集来说,UUID 就很好了,并且使用 UUID 作为标识符有一些很好的属性。

最后,我可能不是最合适的人来讨论或建议这一点,因为我从来没有运行一个表足够大的 UUID PK 已成为一个问题。YMMV.(话虽如此,我还是很乐意听到有人在这种方法上遇到问题!)

正如可接受的答案所述,在这种情况下,范围查询可能比较慢,但不仅仅是在 id上。

自动增量自然是按日期排序的,所以当使用自动增量时,数据按时间顺序存储在磁盘上(参见 B-Tree) ,这加快了读取速度(不寻找硬盘驱动器)。例如,如果一个列出所有的用户,自然顺序将按日期创建,这与自动增量相同,因此范围查询在硬盘上执行速度更快,而在固态硬盘上,我猜想,差异将不存在,因为固态硬盘设计总是随机访问(没有头寻找,没有机械零件参与,只是纯电力)