在PostgreSQL中,将UUID作为主键会影响索引性能吗?

74
我已在Heroku上使用PostgreSQL数据库创建了一个Rails应用程序。它有几个表格,设计用于能够与移动设备同步,在不同的地方可以创建数据。因此,我有一个uuid字段,它是存储GUID的字符串,除了自动递增的主键外。 uuid是服务器和客户端之间通信的标识符。
在实现服务器端的同步引擎后,我意识到每次需要映射uuid<->id时会导致性能问题(在编写对象时,我需要查询uuid以获取id,然后保存并在发送数据时进行相反操作)。
我现在正在考虑仅使用UUID作为主键,使编写和读取变得更简单和更快。我已经阅读过,将UUID用作主键有时会导致索引性能较差(索引碎片化),当使用聚集主键索引时。PostgreSQL是否存在此问题,是否可以使用UUID作为主键?
我已经有一个UUID列,因此在存储方面,删除常规ID列将更好。

2
数据库中的 id 字段是否被任何其他关系作为外键使用?你只是保留这个 id 字段,因为你认为主键应该是一个序列类型,出于你所描述的原因吗? - Joshua Berry
通过合成主键进行聚类只有在您需要查询这些pkey值的范围时才有益处 - 在现实世界中相当罕见。 UUID是主键的完美类型,它足够紧凑(16字节),并且与文本类型相比更快速。 - dbenhur
@Joshua id字段用作外键,UUID字段仅用作常规字段,用于在通信时引用关系(这就是需要经常在它们之间进行转换的原因)。 - thejaz
@dbenhur 但是在PostgreSQL中,主键默认情况下是否聚集?如果我只添加一个随机UUID作为主键并继续,是否会遇到任何索引性能问题?您有使用UUID作为Rails主键的经验吗? - thejaz
1
http://dba.stackexchange.com/questions/322/what-are-the-drawbacks-with-using-uuid-or-guid-as-a-primary-key - Mike T
我在具有多个基数为1e8+的表的数据库中使用uuid作为主键,没有因键类型选择而导致的任何问题。 Pg从不自动按索引聚集。如果需要,可以使用CLUSTER语句执行一次聚集。当键被任意分配(SERIAL或随机uuid)时,很少有聚集是有帮助的,因为通常是通过某些其他谓词或通过连接获取散布的pkey集进行选择 -- 对于任意的pkey没有自然的调用范围扫描。 - dbenhur
2个回答

69

(我在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)

编辑性能影响

这将始终取决于您的工作负载。

整数主键具有相邻数据位置的优点。例如,范围类型查询(如WHERE id between 1 and 10000)可以受益于此,尽管锁争用更严重。

如果您的读工作负载完全是随机的,即您总是进行主键查找,则不应有任何可测量的性能下降:您只需支付更大的数据类型代价。

您是否经常向此表写入,并且该表非常大?维护该索引可能会有一些影响,虽然我没有衡量过。对于许多数据集而言,UUIDs也可以很好地运行,并且使用UUID作为标识符具有某些良好的特性。

最后,我可能不是最合适的人来讨论或建议这个问题,因为我从未运行过一个足够大的带有UUID PK的表,在那里它成为了一个问题。 YMMV。(话虽如此,我很乐意听到遇到这种问题的人!)


3
你有没有处理大数据集和UUID主键的经验?例如:一个数据库中,主键索引不在内存中(例如:工作集大于内存)。这篇参考文章表明,在这种情况下,它们会遇到性能下降问题:[UUID key bad for performance] (http://news.ycombinator.com/item?id=5310662)。 - TaiwanGrapefruitTea
1
@hgmnz 另外,似乎使用UUID v1或v4(在您的示例中使用)很重要。 UUID v1可能更易于索引。 - TaiwanGrapefruitTea
1
@hgmnz 这是另一个关于uuid键性能差的评论,似乎是针对uuid v1(而不是v4)的,来源于poor performance uuid v1 - TaiwanGrapefruitTea
1
@TaiwanGrapfruitTea 对性能影响提出了一些想法。 - hgmnz
4
我原以为Postgres没有聚集索引。如果索引没有聚集(即在磁盘上排序),那么“类似的数据如何更接近”? - Michael
显示剩余4条评论

3
如所接受的答案所述,在这种情况下,范围查询可能会很慢,但不仅仅是在 id 上。
自增长按日期排序,因此当使用自增长时,数据以时间顺序存储在磁盘上(请参阅 B-Tree),这加快了读取速度(对于 HDDs 不需要寻找)。例如,如果列出所有用户,则自然顺序将按创建日期排序,这与自增长相同,因此在 HDDs 上执行范围查询速度更快,而在 SSD 上,我想差异将不存在,因为 SSD 总是随机访问(无需寻道,没有机械部件参与,只有纯电力)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接