我想设计一个电话簿应用程序,每个联系人可以有多个号码。有两种数据库设计:
-
在每个号码中
- 使用联系人外键。
- 将号码存储在每个联系人内部的
ArrayField
中。
哪种解决方案在生产中性能更高,为什么?
谢谢。
我想设计一个电话簿应用程序,每个联系人可以有多个号码。有两种数据库设计:
ArrayField
中。哪种解决方案在生产中性能更高,为什么?
谢谢。
如果您在数组列上构建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行。(为方便起见,我使用的是整数,而不是您可能会用于电话号码的文本字段,但这并没有改变任何基本的东西。)
如果这对您真的很重要,那么您应该使用自己的数据和体系结构进行测试并查看。这将取决于内存中哪些内容适合和哪些内容不适合,您正在执行哪种查询(您是否曾经批量查询数字?除了即将讨论的外键以外,将它们加入其他表中?),以及驱动程序/库处理具有数组类型的列/参数。