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 1lJlQX-0007zj-CC for pgsql-hackers@arkaria.postgresql.org; Tue, 09 Mar 2021 23:06:09 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1lJlQW-0007Oc-A9 for pgsql-hackers@arkaria.postgresql.org; Tue, 09 Mar 2021 23:06:08 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1lJlQV-0007OV-U4 for pgsql-hackers@lists.postgresql.org; Tue, 09 Mar 2021 23:06:08 +0000 Received: from mail-ej1-x635.google.com ([2a00:1450:4864:20::635]) by magus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1lJlQQ-0007AQ-HT for pgsql-hackers@lists.postgresql.org; Tue, 09 Mar 2021 23:06:07 +0000 Received: by mail-ej1-x635.google.com with SMTP id c10so32676693ejx.9 for ; Tue, 09 Mar 2021 15:06:02 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=enterprisedb-com.20150623.gappssmtp.com; s=20150623; h=subject:to:cc:references:from:message-id:date:user-agent :mime-version:in-reply-to:content-language; bh=fL0r36pRr2NA5yR5YEg8ewOM5hJvw/FYI50MiLSmyXA=; b=kj4vZOwrOUSNR5PrRuvdztW37gsAMGjPl5nKDvE2mCVj07csigQ1PxnmmvjB9BoTdq t5ncCGGcuvDoQbRnmNBxbCAO3b4No8xI2ykWutD52e1HPNbbXCwCyVfyatoiGr5tshgt e6vh701wt5+cB+tsAhR3dFLuCp/Ci7i0xqfX1jH+y/QCy3TFxPtQ/37svrp4wMEn9QGg R04A5C9ZmhaHkAWbYjMnL1ey3KrElVJvX15cbj5eJQ5KsaNTiGA90PpsvsMvNe0wFNWD Xc0eI5UGSABavLEEQ2c3O7BXn02Cm2hDcBFQRb6uDZe9W8+7Of1QQjWDE5YmbQNIbDp1 nkjQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:to:cc:references:from:message-id:date :user-agent:mime-version:in-reply-to:content-language; bh=fL0r36pRr2NA5yR5YEg8ewOM5hJvw/FYI50MiLSmyXA=; b=ZLRT1FgZTlgNjYOvoSQ9y9bxlzLhesRxfhLLwAO9H3d6oWv9Lcv0xv23daE2MmSWR6 SqnQNlvmHtva379aYu/Jdl4HLDu/AvLTCMN5G0IgcvjBz/wYBFuaa0LtQhuUwdJDMqyP A6ndjHZYGgLA5NlDvhYjkYneTttE9cmGj6qu6U84PAQt61NfvBNnQ7zaqrtFGS2npmj/ HWVr4RIi11+fHxTAzr8Fo5ZoR1oBNNhpS0CAAVRrJp82g2ow3/az8bf/Ymxt77SThWFG 5M8duZz7p6I1mnvZhwLyf8NDlmgztbQYf94qp7uBsYOROTTRu6em7H1pT4yZyG9yVnXH yqhw== X-Gm-Message-State: AOAM530d8nPNPUijgbUhPqR41pg3BU+4Bz3TsuDr9u69BQsq3xqx9qUD g4fUh4N/OpQlpBIk8uHJk5CAZ+L+2g/LwWCmvx0JGrdkcTswMNjc1zGHZYdegRlXCjcw7n9IJp+ ANSidDJftkSOT2QVQ1iimUUhOm6tayCJ+ofRclq4jums45kxZAnvYxAX88ZXMeHdAzFkpwjyCYt vpXbdGNbAExw+0CqlQB+4cEOM1uZ8MYu1vjdDhH4efWmB9eYu5V4D5gwuTH1VnrIagF1KdaXFaI AcfEYFO0GXV7EzCk9Yq0B2GPs5mUxG7DNiTlCTUO/6VRsR62XIWBQ== X-Google-Smtp-Source: ABdhPJy7TxxTHvvRVMM7bMt6L2fvs2A+epHL5VACyq6OP+uL3OiEaQjawQNRUf2zbWKaaruJguqqMg== X-Received: by 2002:a17:906:1182:: with SMTP id n2mr443009eja.234.1615331159570; Tue, 09 Mar 2021 15:05:59 -0800 (PST) Received: from [10.137.0.20] (ip-86-49-253-127.net.upcbroadband.cz. [86.49.253.127]) by smtp.gmail.com with ESMTPSA id v13sm10100976ede.5.2021.03.09.15.05.57 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 09 Mar 2021 15:05:57 -0800 (PST) Subject: Re: POC: GROUP BY optimization To: Pavel Borisov , Ibrar Ahmed Cc: Tomas Vondra , Dmitry Dolgov <9erthalion6@gmail.com>, Teodor Sigaev , Gavin Flower , Andres Freund , Michael Paquier , PostgreSQL Developers References: <20190503215510.bcr5ycszntqg65tw@development> <20190524225725.embuha33qvc5avz3@development> <20200514235220.xewrrwjvatxzn3g6@development> <20201026085721.g6h5xljxvodnmk34@localhost> <20201026104040.6bigvej6f55vjvgp@localhost> <20201027201509.jfv6sfhrmxhdwlvk@development> <20201029145025.tn3tekboot37nkbf@localhost> From: Tomas Vondra Message-ID: <22c44f98-bfa8-8630-62b5-5155e11eb284@enterprisedb.com> Date: Wed, 10 Mar 2021 00:05:55 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.8.0 MIME-Version: 1.0 In-Reply-To: Content-Type: multipart/mixed; boundary="------------A955BFDEDE369DCD57A45C5A" Content-Language: en-US X-CLOUD-SEC-AV-Info: enterprisedb,google_mail,monitor X-CLOUD-SEC-AV-Sent: true X-Gm-Spam: 0 X-Gm-Phishy: 0 List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk This is a multi-part message in MIME format. --------------A955BFDEDE369DCD57A45C5A Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit Hi, I take a look at the patch today. Attached is the v12 and a separate patch with some comment tweaks and review comments. 1) I see the patch results in some plan changes in postgres_fdw. I assume it's somehow related to the sort costing changes, but I find it a bit suspicious. Why should the plan change from this: Foreign Scan Remote SQL: ... ORDER BY ... OFFSET 100 LIMIT 10; to Limit Foreign Scan Remote SQL: ... ORDER BY ... But even if this is due to changes to order by costing, postponing the limit seems like a net loss - the sort happens before the limit, and by postponing the limit after foreign scan we need to transfer 100 more rows. So what's causing this? The difference in plan cost seems fairly substantial (old: 196.52, new: 241.94). But maybe that's OK and expected. 2) I wonder how much more expensive (in terms of CPU) is the new sort costing code. It's iterative, and it's calling functions to calculate number of groups etc. I wonder what's the impact simple queries? 3) It's not clear to me why we need "fake var" protection? Why would the estimate_num_groups() fail for that? 4) In one of the places a comment says DEFAULT_NUM_DISTINCT is not used because we're dealing with multiple columns. That makes sense, but maybe we should use DEFAULT_NUM_DISTINCT at least to limit the range. That is, with N columns we should restrict the nGroups estimate by: min = DEFAULT_NUM_DISTINCT max = Min(pow(DEFAULT_NUM_DISTINCT, ncolumns), tuples) Also, it's not clear what's the logic behind the formula: nGroups = ceil(2.0 + sqrt(tuples) * list_length(pathkeyExprs) / list_length(pathkeys)); A comment explaining that would be helpful. 5) The compute_cpu_sort_cost comment includes two references (in a quite mixed-up way), and then there's another reference in the code. I suggest we list all of them in the function comment. 6) There's a bunch of magic values (various multipliers etc.). It's not clear which values are meant to be equal, etc. Let's introduce nicer constants with reasonable names. 7) The comment at get_cheapest_group_keys_order is a bit misleading, because it talks about "uniqueness" - but that's not what we're looking at here (I mean, is the column unique or not). We're looking at number of distinct values in the column, which is a different thing. Also, it'd be good to roughly explain what get_cheapest_group_keys_order does - how it calculates the order (permutations up to 4 values, etc.). 8) The comment at compute_cpu_sort_cost should also explain basics of the algorithm. I tried doing something like that, but I'm not sure I got all the details right and it probably needs further improvements. 9) The main concern I have is still about the changes in planner.c, and I think I've already shared it before. The code takes the grouping pathkeys, and just reshuffles them to what it believes is a better order (cheaper, ...). That is, there's on input pathkey list, and it gets transformed into another pathkey list. The problem is that even if this optimizes the sort, it may work against some optimization in the upper part of the plan. Imagine a query that does something like this: SELECT a, b, count(*) FROM ( SELECT DISTINCT a, b, c, d FROM x ) GROUP BY a, b; Now, from the costing perspective, it may be cheaper to do the inner grouping by "c, d, a, b" for example. But that works directly against the second grouping, which would benefit from having the input sorted by "a, b". How exactly would this behaves depends on the number of distinct values in the columns, how expensive the comparisons are for each column, and so on. But you get the idea. I admit I haven't managed to construct a reasonably query that'd be significantly harmed by this, but I haven't spent a lot of time on it. I'm pretty sure I used this trick in the past, when doing aggregations on large data sets, where it was much better to "pipe" the data through multiple GroupAggregate nodes. For this reason I think the code generating paths should work a bit like get_useful_pathkeys_for_relation and generate_useful_gather_paths. * group_keys_reorder_by_pathkeys should not produce just one list of pathkeys, but multiple interesting options. At the moment it should produce at least the input grouping pathkeys (as specified in the query), and the "optimal" pathkeys (by lowest cost). * the places calling group_keys_reorder_by_pathkeys should loop on the result, and generate separate path for each option. I'd guess in the future we'll "peek forward" in the plan and see if there are other interesting pathkeys (that's the expectation for get_useful_pathkeys_for_relation). regards -- Tomas Vondra EnterpriseDB: http://www.enterprisedb.com The Enterprise PostgreSQL Company --------------A955BFDEDE369DCD57A45C5A Content-Type: text/x-patch; charset=UTF-8; name="0001-v12-20210309.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="0001-v12-20210309.patch"