我有两个一对多关系的表。假设对于表foo
中的每一行,表bar
中可以有0行或更多行引用foo
中的行。
客户想知道bar
中foo
中有多少行引用foo
中SELECT count(bar_id) FROM bar WHERE bar.foo_id = foo.foo_id;
中所有行的行。
我可以通过以下查询来完成此任务:
foo
但是,如果表bar
和foo
很大,怎么办?说bar
有100万行,foo
有1000万行。我们还要说bar
中99%的行的引用次数少于1,000 foo
行。假设客户端通常一次要求大约100行bar
。
我应该使用带有外键索引的naive count()查询,还是保留计数器会更好?甚至可以保留一个柜台?通过使用{{1}}上的触发器以原子增量和减量更新计数器,我相信这是可能的,但我可能是错的。
答案 0 :(得分:5)
也许与直觉相反,您可能会发现简单的count
方法更快,除非您的工作量偏向于读取。
这样做的原因是计数器表的效果将是序列化更新,因此在任何给定时间只有一个更新给定foo
的事务可以在飞行中。那是因为更新计数器的触发器的更新将锁定计数器表中的foo
条目,并且在事务回滚或提交之前不会释放它。
更糟糕的是,如果您的交易影响多个foo
,另一个交易会影响另一个,那么由于死锁,其中一个交易很可能会中止。
坚持一个简单的计数,直到你有充分的理由改变它。
答案 1 :(得分:3)
关于索引的甜蜜之处在于它们为查询操作提供了对数的复杂性。因此,对于10*10^6
行,索引只需要ln(10*10^6)=16.1
个比较来查找一个特定的id。使其成为1亿行,您只需要进行2到3次比较。简而言之:索引并不太关心表的大小。
当然,您仍然可以使用存储的计数器存档一些性能提升。这是一个典型的权衡。维护计数器会使bar
的插入和删除更加昂贵,并使您的计数查询更便宜一些。
因此,如果您的表很少被更改并且查询经常运行(例如,每小时数千次),您可能会使用存储的计数器过程获得性能。但是,在大多数情况下,我会说索引列,让数据库为您完成剩下的工作。