Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1jmi9t-0003pY-7H for pgsql-hackers@arkaria.postgresql.org; Sat, 20 Jun 2020 18:24:05 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1jmi9s-00065p-3O for pgsql-hackers@arkaria.postgresql.org; Sat, 20 Jun 2020 18:24:04 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1jmi9r-00065i-OM for pgsql-hackers@lists.postgresql.org; Sat, 20 Jun 2020 18:24:03 +0000 Received: from mail-ej1-x643.google.com ([2a00:1450:4864:20::643]) by makus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1jmi9p-0002E1-AU for pgsql-hackers@lists.postgresql.org; Sat, 20 Jun 2020 18:24:02 +0000 Received: by mail-ej1-x643.google.com with SMTP id w6so2471210ejq.6 for ; Sat, 20 Jun 2020 11:24:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=2ndquadrant-com.20150623.gappssmtp.com; s=20150623; h=date:from:to:cc:subject:message-id:references:mime-version :content-disposition:in-reply-to; bh=bMioNUSnPMm2jTXnrADfDDyPkEIRoPn1P26EvndRQw8=; b=elUyuI0ux2/qK91UZhjP0fWUdOHynUjCNvfE+WSiUbB5LE37WIkzvJgPzd9CDkDm1v BnGsBgQnrSpy25vhjAVep4CPVf9hVLoIVX9XmmEn+XKTp6AYNzBtkaHuefoTjhO5FuQ1 Ib96AlMKKzJdeBOxHO66WuM6+eazezkMA1ebd4uyjSjtu7XA6mczC8FCueYR4lGZ8u0c cXe325qOA3GlUaWJNlKdKTFbmCCO2BzEDcDSQ3YZwijkFWWWNnQQoiFq1g8ArxDpVpu+ Pc1b5WcO4Xjqp3NOzevUX6XTW0tBFrQAkOxd8X9cI3k5It3TNUL6cWsQhvJumsLdQ5O+ 8Scg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to; bh=bMioNUSnPMm2jTXnrADfDDyPkEIRoPn1P26EvndRQw8=; b=WkOm+QG4h2gAncRL/4mW5l5tXDUdXKZNyB8HJho3NBGSVjtV37NjNqxXi7bYPUhSZQ fActajnW999RzjvWLFCBflSl9KCqEvowHrG6wbnNqnD+nw0wjbOJ9fEFx9/P+90iQbiQ Cxc400aO9maNIqEIkH+oftqMg2T9IBAevQ7NChsjebIPmjgVSh5SytTw4nR4NY3C2d2F bapUN7U1XmNe3TsTHvQLUgEs2nMhNCxqYvg6GfQ6e2N1vA0Enkj8B1XugM2cYXYBYXuO QYdlPIQMS6yBG2e+FgSN4suSt22AQel8i4MvQwzBZKQwkfgD0+2fGfxqmA3zvFNOZfaO T8/g== X-Gm-Message-State: AOAM532V/0mzYHyRQZPMpSgIo4q8GnP3b/cWvOvS1pe862r+nzRwo3FJ +uCyrSiV8OrryASirdHnHkxI3w== X-Google-Smtp-Source: ABdhPJwwuafgBcPWqc+2xltBsOAA3luxtIRJ8NA0ZQcym1AG/Ya1IpZ/bAGQ6SDuqRxnyHAoeVBpwA== X-Received: by 2002:a17:906:46d0:: with SMTP id k16mr8804189ejs.76.1592677438907; Sat, 20 Jun 2020 11:23:58 -0700 (PDT) Received: from localhost (ip-86-49-253-141.net.upcbroadband.cz. [86.49.253.141]) by smtp.gmail.com with ESMTPSA id o4sm8034439edt.15.2020.06.20.11.23.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 20 Jun 2020 11:23:58 -0700 (PDT) Date: Sat, 20 Jun 2020 20:23:57 +0200 From: Tomas Vondra To: Dmitry Dolgov <9erthalion6@gmail.com> Cc: Teodor Sigaev , Gavin Flower , Andres Freund , Michael Paquier , PostgreSQL Developers Subject: Re: POC: GROUP BY optimization Message-ID: <20200620182357.cspxophvqjgqfvys@development> References: <20190409152100.5q25whnxs27zws5m@development> <20190503215510.bcr5ycszntqg65tw@development> <20190524225725.embuha33qvc5avz3@development> <20200514235220.xewrrwjvatxzn3g6@development> <20200516122431.b7wtpm7dspsaxfro@localhost> <20200516145609.vm7nrqy7frj4ha6r@development> <20200620143010.z3af7wa4v22h4lu4@localhost> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii; format=flowed Content-Disposition: inline In-Reply-To: <20200620143010.z3af7wa4v22h4lu4@localhost> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk On Sat, Jun 20, 2020 at 04:30:10PM +0200, Dmitry Dolgov wrote: >> On Sat, May 16, 2020 at 04:56:09PM +0200, Tomas Vondra wrote: >> >> So I don't think there will be a single "interesting" grouping pathkeys >> (i.e. root->group_pathkeys), but a collection of pathkeys. And we'll >> need to build grouping paths for all of those, and leave the planner to >> eventually pick the one giving us the cheapest plan ... >> >> A "brute-force" approach would be to generate all possible orderings of >> group_pathkeys, but that's probably not going to fly - it might easily >> cause an explosion in number of paths we track, etc. So we'll need to >> pick/prioritize orderings that are more likely to give us efficient >> plans, and ORDER BY seems like a good example because it means we won't >> need an explicit sort. > >Yes, I see. I've already rebased the original version of patch and now >working on your suggestions. In the meantime one question: > >> But there are also decisions that can be made only after we build the >> grouping paths. For example, we may have both GROUP BY and ORDER BY, and >> there is no "always correct" way to combine those. In some cases it may >> be correct to use the same pathkeys, in other cases it's better to use >> different ones (which will require an extra Sort, with additional cost). > >Do I understand correctly, your idea is that in some cases it's cheaper >to use different order for GROUP BY than in ORDER BY even with an extra >sorting? In the current patch implementation there is an assumption that >says it's always better to match the order of pathkeys for both GROUP >BY/ORDER BY (which means that the only degree of freedom there is to >reorder the tail, which in turn makes both "unsorted input" and >"partially sorted input" cases from your original email essentially the >same). Can you show such an example when this assumption is invalid? > Yes, that is mostly the point - I don't think we can assume simply using the ORDER BY pathkeys (if specified) for grouping is optimal. As a trivial counter-example, consider this CREATE TABLE t ( a int, b int, c int); INSERT INTO t SELECT 1000 * random(), 1000 * random(), i FROM generate_series(1,10000000) s(i); CREATE INDEX ON t (a, b, c); VACUUM ANALYZE t; And now some simple queries (you may need to tweak the planning options a bit, to get these plans - disable hashagg etc.). test=# explain analyze select a, b, count(c) from t group by a, b; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------- GroupAggregate (cost=0.43..389008.55 rows=1000018 width=16) (actual time=0.037..10509.505 rows=1001923 loops=1) Group Key: a, b -> Index Only Scan using t_a_b_c_idx on t (cost=0.43..304007.06 rows=10000175 width=12) (actual time=0.024..5189.908 rows=10000000 loops=1) Heap Fetches: 0 Planning Time: 0.113 ms Execution Time: 10941.677 ms (6 rows) test=# explain analyze select a,b, count(c) from t group by a, b order by a, b; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------- GroupAggregate (cost=0.43..389008.55 rows=1000018 width=16) (actual time=0.033..10606.925 rows=1001923 loops=1) Group Key: a, b -> Index Only Scan using t_a_b_c_idx on t (cost=0.43..304007.06 rows=10000175 width=12) (actual time=0.020..5240.332 rows=10000000 loops=1) Heap Fetches: 0 Planning Time: 0.100 ms Execution Time: 11043.358 ms (6 rows) test=# explain analyze select a,b, count(c) from t group by a, b order by b, a; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------ GroupAggregate (cost=1658556.19..1768558.12 rows=1000018 width=16) (actual time=14707.676..25533.053 rows=1001923 loops=1) Group Key: b, a -> Sort (cost=1658556.19..1683556.63 rows=10000175 width=12) (actual time=14707.659..20011.024 rows=10000000 loops=1) Sort Key: b, a Sort Method: external merge Disk: 219200kB -> Seq Scan on t (cost=0.00..154056.75 rows=10000175 width=12) (actual time=0.028..4751.244 rows=10000000 loops=1) Planning Time: 0.103 ms Execution Time: 25989.412 ms (8 rows) test=# explain analyze select * from (select a,b, count(c) from t group by a, b offset 0) foo order by b,a; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------- Sort (cost=515759.00..518259.04 rows=1000018 width=16) (actual time=11281.094..11783.920 rows=1001923 loops=1) Sort Key: t.b, t.a Sort Method: external merge Disk: 34880kB -> GroupAggregate (cost=0.43..389008.55 rows=1000018 width=16) (actual time=0.064..10507.256 rows=1001923 loops=1) Group Key: t.a, t.b -> Index Only Scan using t_a_b_c_idx on t (cost=0.43..304007.06 rows=10000175 width=12) (actual time=0.052..5209.939 rows=10000000 loops=1) Heap Fetches: 0 Planning Time: 0.111 ms Execution Time: 12218.370 ms (9 rows) To sum this up: grouping (a,b): 10941 ms grouping (a,b) + ordering (a,b): 11043 grouping (b,a) + ordering (b,a): 25989 grouping (a,b) + ordering (b,a): 12218 So, it's fast as long as we can use the IOS, even if we have to do an extra Sort at the end. This obviously relies on the grouping to reduce the number of rows (in this case from 10M to 1M), which makes the extra cost much cheaper. I'm pretty sure I could construct similar examples e.g. with incremental sort, and so on. Does this explain why I think we can't make the assumption that it's always better to match the pathkeys? FWIW, I think the change of plan for the third query (where we specify "GROUP BY a,b" but the planner decides to just change that) is rather annoying, and it clearly makes things worse :-( regards -- Tomas Vondra http://www.2ndQuadrant.com PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services