2012-07-26 17 views
1

我有5代表一个导轨3应用嵌套在2级外出时(表1有许多> table2中有许多>表3)保持大量的信息。把它想象成一个网站访问者的跟踪系统,在这个系统中保存了大量数据,并且当我们检索显示数据时需要快速保存并执行大量查询,因为计数是用来提取数据的。PostgreSQL的性能连接VS平原选择

我创造了我的应用程序没有太多的SQL记起初只是为了得到它去那么我想我会开始优化数据库的部分,因为会有数据的工作。

我在那里我有大约100万条记录在我所有的表总结了点,我认为是时候,因为我在每次请求1秒的响应,启动优化。

我的Rails应用程序为每个计数执行一个查询,而不涉及任何连接。就像user.websites.hits这样的默认行为(选择用户然后执行另一个选择来获取网站,并为每个网站执行选择以获取访问者数量)。总的来说,我认为它使大约80个查询获得我的页面结果(我知道...),其中包含我需要的所有内容,因此我创建了一个查询来获取单个请求的所有结果。

问题是,当我在我的数据库管理查询运行大约需要2秒钟的页面管理执行80个查询,加载模板和资产,1.1秒渲染来获取。

我不是一个数据库,亲,但我查询坏,有时,最好不要使用跨多个表联接像我这样做。如果我的数据继续以这种方式增长,我的连接查询会变得更快,还是两个测试的加载速度都会变慢?

我有索引,对所有的连接点和WHERE该查询的字段,所以我不认为这是个问题。

我已经考虑过缓存,但我觉得对于1百万个小数据记录来说,现在开始这么做已经太快了。

任何建议?

domain -> has_many: channels(we use it for split testing) 
channel -> has_many: visits, visitors (unique visits by ip), sales 
product -> has_many: visits, visitors (unique visits by ip), sales 

The query tries to get the domains which includes: 
domain_name, 
channels_count, 
visits_count, 
visitors_count, 
sales_count and 
products_count via the visits table 


ACTUAL QUERY: 
SELECT 
    domains.id, 
    domains.domain, 
    COUNT(distinct kc.id) AS visits_count, 
    COUNT(distinct kv.id) AS visits_count, 
    COUNT(distinct kv.ip_address) AS visitors_count, 
    COUNT(distinct kp.id) AS products_count, 
    COUNT(distinct ks.id) AS sales_count 
FROM 
    domains 
LEFT JOIN 
    channels AS kc ON domains.id=kc.domain_id 
LEFT JOIN 
    visits AS kv ON kc.id=kv.channel_id 
LEFT JOIN 
    products AS kp ON kv.product_id=kp.id 
LEFT JOIN 
    sales AS ks ON kc.id=ks.channel_id 
WHERE 
    (domains.user_id=2) 
GROUP BY 
    domains.id 
LIMIT 20 
OFFSET 0 


"QUERY PLAN" 
"Limit (cost=7449.20..18656.41 rows=20 width=50) (actual time=947.837..5093.929 rows=20 loops=1)" 
" -> GroupAggregate (cost=7449.20..20897.86 rows=24 width=50) (actual time=947.832..5093.845 rows=20 loops=1)" 
"  -> Merge Left Join (cost=7449.20..17367.45 rows=282413 width=50) (actual time=947.463..4661.418 rows=99940 loops=1)" 
"    Merge Cond: (domains.id = kc.domain_id)" 
"    Filter: (kc.deleted_at IS NULL)" 
"    -> Index Scan using domains_pkey on domains (cost=0.00..12.67 rows=24 width=30) (actual time=0.022..0.146 rows=21 loops=1)" 
"     Filter: ((deleted_at IS NULL) AND (user_id = 2))" 
"    -> Materialize (cost=7449.20..16619.27 rows=58836 width=32) (actual time=947.430..4277.029 rows=99923 loops=1)" 
"     -> Nested Loop Left Join (cost=7449.20..16472.18 rows=58836 width=32) (actual time=947.424..3872.057 rows=99923 loops=1)" 
"       Join Filter: (kc.id = kv.channel_id)" 
"       -> Index Scan using index_channels_on_domain_id on channels kc (cost=0.00..12.33 rows=5 width=16) (actual time=0.008..0.090 rows=5 loops=1)" 
"       -> Materialize (cost=7449.20..10814.25 rows=58836 width=24) (actual time=189.470..536.745 rows=99923 loops=5)" 
"        -> Hash Right Join (cost=7449.20..10175.07 rows=58836 width=24) (actual time=947.296..1446.256 rows=99923 loops=1)" 
"          Hash Cond: (ks.product_id = kp.id)" 
"          -> Seq Scan on sales ks (cost=0.00..1082.22 rows=59022 width=8) (actual time=0.027..119.767 rows=59022 loops=1)" 
"          -> Hash (cost=6368.75..6368.75 rows=58836 width=20) (actual time=947.213..947.213 rows=58836 loops=1)" 
"           Buckets: 2048 Batches: 4 Memory Usage: 808kB" 
"           -> Hash Left Join (cost=3151.22..6368.75 rows=58836 width=20) (actual time=376.685..817.777 rows=58836 loops=1)" 
"             Hash Cond: (kv.product_id = kp.id)" 
"             -> Seq Scan on visits kv (cost=0.00..1079.36 rows=58836 width=20) (actual time=0.011..135.584 rows=58836 loops=1)" 
"             -> Hash (cost=1704.43..1704.43 rows=88143 width=4) (actual time=376.537..376.537 rows=88143 loops=1)" 
"              Buckets: 4096 Batches: 4 Memory Usage: 785kB" 
"              -> Seq Scan on products kp (cost=0.00..1704.43 rows=88143 width=4) (actual time=0.006..187.174 rows=88143 loops=1)" 
"Total runtime: 5096.723 ms" 
+1

运行连接不同事物的查询和运行80个不同的(平面)查询是有区别的。有可能数据库(如果没有错误的统计*和*有正确的索引)会*击败*本地传输数据并对其执行本地连接。当然,这一切都“依赖”。 (例如,一个单独的查询可以很快变成超超非规范化的交叉连接。) – 2012-07-26 02:17:17

+0

如果你没有嵌套表,你会有很多重复的数据。你的数据库可能要大几倍。但是如果你可以用简单的选择获得数据,为什么还要加入?获取你所需要的,从不使用*。尽量减少查询;多次查询数据库总是比获取大量数据和使用系统内存(或客户端内存)慢。 – 2012-07-26 02:27:05

+0

我的意图是让他们保持在最低限度,当我发现80个查询在速度方面超过1时,这就是讨论的原因。 – 2012-07-26 02:40:20

回答

3

100万条记录不是很多,加入5个表格对于数据库来说是一项简单的工作。有索引很好,但他们有用吗? EXPLAIN ANALYZE告诉你关于你的查询的是什么?那配置怎么样?默认配置刚刚开始,它不是为所有类型的工作负载提供最佳性能的设置。

但是不要担心几个联接,关系数据库就是用来这样做的。

+0

我在db服务器上改变的唯一的事情是将'shared_buffers'设置为'150MB'。以下是分析的查询粘贴:http:// pastebin。com/XB40df2X我不确定这些索引是否有用,因为我从来不必处理这个数量的记录,我只是通过创建索引来说明加入点等。除此之外,我在黑暗中 – 2012-07-26 05:45:05

+1

work_mem太低,请参阅Materialize步骤。此外,还有一些Seq Scan(所有数据的顺序表扫描)表明数据库没有有用的索引,或者查询只是要求从表中获取太多数据。如果没有实际的查询,很难为您提供帮助,但您有很多选择可以提高性能。 – 2012-07-26 06:44:15

+0

我已经用解释结果和查询示例更新了最初的问题。感谢您的帮助。 – 2012-07-26 11:55:04