【发布时间】:2020-03-04 07:31:55
【问题描述】:
我想设计一个电话簿应用程序,每个联系人可以有多个号码。有两种数据库设计:
- 在每个号码中使用contact外键。
- 将号码存储在每个联系人内的
ArrayField。
哪种解决方案在生产中性能更高,为什么?
提前致谢。
【问题讨论】:
标签: django database postgresql optimization
我想设计一个电话簿应用程序,每个联系人可以有多个号码。有两种数据库设计:
ArrayField。哪种解决方案在生产中性能更高,为什么?
提前致谢。
【问题讨论】:
标签: django database postgresql optimization
如果您在数组列上构建 GIN 索引,并且 Django 将编写查询以使用该索引,那么两者的读取性能将非常相似。
性能差异不太可能成为此选择背后的驱动因素。例如,除了电话号码之外,您是否需要电话号码背后的更多信息,例如添加的时间,上次使用的时间,是手机还是其他东西等。
数组列应该更快,因为它只需要查询一个索引和表,而不是每个两个。此外,它会更紧凑,因此更易于缓存。
另一方面,当estimating rare values 时,您的数组列的统计估计会出现问题,您可能会在这里遇到问题,因为没有电话号码可能会被很多人共享。这种错误估计可能会对您的查询性能产生毁灭性的影响。例如在一个小测试中,高估了数千倍的行数导致它为单行查询启动并行工作者,导致它比关闭并行化时慢约 20 倍,比使用时慢 10 倍不受估计问题影响的外键表示。
例如:
create table contact as select md5(floor(random()*50000000)::text) as name, array_agg(floor(random()*100000000)::int) phones from generate_series(1,100000000) f(x) group by name;
vacuum analyze contact;
create index on contact using gin (phones );
explain analyze select * from contact where phones @> ARRAY[123456];
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------
Gather (cost=3023.30..605045.19 rows=216167 width=63) (actual time=0.668..8.071 rows=2 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Parallel Bitmap Heap Scan on contact (cost=2023.30..582428.49 rows=90070 width=63) (actual time=0.106..0.110 rows=1 loops=3)
Recheck Cond: (phones @> '{123456}'::integer[])
Heap Blocks: exact=2
-> Bitmap Index Scan on contact_phones_idx (cost=0.00..1969.25 rows=216167 width=0) (actual time=0.252..0.252 rows=2 loops=1)
Index Cond: (phones @> '{123456}'::integer[])
Planning Time: 0.820 ms
Execution Time: 8.137 ms
您可以看到它估计了 216167 行,但实际上只有 2 行。(为方便起见,我使用整数,而不是您可能用于电话号码的文本字段,但这不会改变任何基本的东西)。
如果这对您来说真的很重要,那么您应该使用自己的数据和自己的架构进行测试并查看。这将取决于内存中的内容和不适合的内容,您正在执行的查询类型(您是否曾经批量查找数字?除了立即讨论的外键之外,将它们加入其他表?),也许您的驱动程序/库处理具有数组类型的列/参数。
【讨论】: