硬编码功能参数产生5倍加速

时间:2016-11-10 11:32:45

标签: sql postgresql

我有以下存储过程来生成动态查询。

根据条件/过滤器列表,它会找到属于给定Visitors的所有Appapp_id作为参数传入。

如果我使用app id调用该函数,并在动态查询中使用此参数,它将在大约200ms内运行。

但是,如果我对app_id进行硬编码,则会在<为20ms。

这是我如何调用过程

的示例
SELECT id
FROM find_matching_visitors('my_app_id', '{}', '{( field = ''app_name'' and string_value ILIKE ''My awesome app''  )}')

任何想法都是为什么?

    CREATE OR REPLACE FUNCTION find_matching_visitors(app_id text, default_filters text[], custom_filters text[])
    RETURNS TABLE (
      id varchar
    ) AS
    $body$
    DECLARE
        default_filterstring text;
        custom_filterstring text;
        default_filter_length integer;
        custom_filter_length integer;
        sql VARCHAR;
    BEGIN
        default_filter_length := COALESCE(array_length(default_filters, 1), 0);
        custom_filter_length := COALESCE(array_length(custom_filters, 1), 0);

        default_filterstring := array_to_string(default_filters, ' AND ');
        custom_filterstring := array_to_string(custom_filters, ' OR ');

        IF custom_filterstring = '' or custom_filterstring is null THEN
            custom_filterstring := '1=1';
        END IF;

        IF default_filterstring = '' or default_filterstring is null THEN
            default_filterstring := '1=1';
        END IF;

        sql := format('
                    SELECT v.id FROM visitors v
                    LEFT JOIN trackings t on v.id = t.visitor_id
                    WHERE v.app_id = ''HARDCODED_APP_ID'' and (%s) and (%s)
                    group by v.id

                ', custom_filterstring, default_filterstring, custom_filter_length, custom_filter_length);
        RETURN QUERY EXECUTE sql;

    END;
    $body$
    LANGUAGE 'plpgsql';

分析没有硬编码app_id

Limit  (cost=481.86..481.99 rows=50 width=531) (actual time=163.579..163.581 rows=9 loops=1)
2     ->  Sort  (cost=481.86..484.26 rows=960 width=531) (actual time=163.578..163.579 rows=9 loops=1)
3           Sort Key: v0.last_seen DESC
4           Sort Method: quicksort  Memory: 30kB
5           ->  WindowAgg  (cost=414.62..449.97 rows=960 width=531) (actual time=163.553..163.560 rows=9 loops=1)
6                 ->  Hash Join  (cost=414.62..437.97 rows=960 width=523) (actual time=163.525..163.537 rows=9 loops=1)
7                       Hash Cond: ((find_matching_visitors.id)::text = (v0.id)::text)
8                       ->  Function Scan on find_matching_visitors  (cost=0.25..10.25 rows=1000 width=32) (actual time=153.918..153.918 rows=9 loops=1)
9                       ->  Hash  (cost=354.19..354.19 rows=4814 width=523) (actual time=9.578..9.578 rows=4887 loops=1)
10                            Buckets: 8192  Batches: 1  Memory Usage: 2145kB
11                            ->  Seq Scan on visitors v0  (cost=0.00..354.19 rows=4814 width=523) (actual time=0.032..4.993 rows=4887 loops=1)
12                                  Filter: ((NOT merged) AND (((type)::text = 'user'::text) OR ((type)::text = 'lead'::text)))
13                                  Rows Removed by Filter: 138
14  Planning time: 1.134 ms
15  Execution time: 163.705 ms

分析何时硬编码app_id

Limit  (cost=481.86..481.99 rows=50 width=531) (actual time=25.890..25.893 rows=9 loops=1)
2     ->  Sort  (cost=481.86..484.26 rows=960 width=531) (actual time=25.888..25.890 rows=9 loops=1)
3           Sort Key: v0.last_seen DESC
4           Sort Method: quicksort  Memory: 30kB
5           ->  WindowAgg  (cost=414.62..449.97 rows=960 width=531) (actual time=25.862..25.870 rows=9 loops=1)
6                 ->  Hash Join  (cost=414.62..437.97 rows=960 width=523) (actual time=25.830..25.841 rows=9 loops=1)
7                       Hash Cond: ((find_matching_visitors.id)::text = (v0.id)::text)
8                       ->  Function Scan on find_matching_visitors  (cost=0.25..10.25 rows=1000 width=32) (actual time=15.875..15.876 rows=9 loops=1)
9                       ->  Hash  (cost=354.19..354.19 rows=4814 width=523) (actual time=9.936..9.936 rows=4887 loops=1)
10                            Buckets: 8192  Batches: 1  Memory Usage: 2145kB
11                            ->  Seq Scan on visitors v0  (cost=0.00..354.19 rows=4814 width=523) (actual time=0.013..5.232 rows=4887 loops=1)
12                                  Filter: ((NOT merged) AND (((type)::text = 'user'::text) OR ((type)::text = 'lead'::text)))
13                                  Rows Removed by Filter: 138
14  Planning time: 0.772 ms
15  Execution time: 26.006 ms

更新1:为两种情况添加了解释。注意:它们实际上是完全相同的计划,只花费的时间不同

更新2:事实证明我需要将app_id作为参数传递给格式函数,而不是直接嵌入它。这使查询时间缩短到大约20 / 30ms

1 个答案:

答案 0 :(得分:2)

硬编码值对于确定最佳查询计划非常重要。 例如:

select * from some_table where id_person=231
select * from some_table where id_person=10

当90%的some_table有id_person = 231 pg使用全表扫描时,因为这是最快的。 当1%的记录具有id_person = 10时,它使用索引扫描。 所以使用的计划取决于参数的值。

当您使用的不是硬编码值时,例如

select * from some_table where id_person=?

无法确定最佳查询计划,查询可能会变慢。