We migrated our postgresql db from EC2 (pg 12.1 running on Docker) into RDS (pg 12.6). We then notice some queries become very slow (10x slower)
Here's one of our query
SELECT cp."FirstName" ,
cp."LastName" ,
cp."DealerGroupId" ,
count(*) AS "DuplicateCount"
FROM "sc_CustomerProfiles" cp
WHERE (cp."FirstName" IS NOT NULL
OR cp."LastName" IS NOT NULL)
AND cp."UpdatedDate" > '2020-07-01'
AND EXISTS
(SELECT 1
FROM "sc_CustomerProfiles" scp
WHERE scp."FirstName" = cp."FirstName"
AND cp."LastName" = scp."LastName"
AND cp."DealerGroupId" = scp."DealerGroupId"
AND scp."ProfileId" < 0 )
GROUP BY cp."FirstName" ,
cp."LastName" ,
cp."DealerGroupId"
HAVING count(*) > 1
Running EXPLAIN ANALYZE on our old DB on EC2 gives below result
Finalize GroupAggregate (cost=818304.54..922603.67 rows=196075 width=61) (actual time=1679.259..1931.629 rows=623 loops=1)
Group Key: cp."FirstName", cp."LastName", cp."DealerGroupId"
Filter: (count(*) > 1)
Rows Removed by Filter: 2257
-> Gather Merge (cost=818304.54..906894.61 rows=668500 width=61) (actual time=1678.763..1934.877 rows=3290 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Partial GroupAggregate (cost=817304.52..828733.10 rows=334250 width=61) (actual time=1637.652..1886.456 rows=1097 loops=3)
Group Key: cp."FirstName", cp."LastName", cp."DealerGroupId"
-> Merge Semi Join (cost=817304.52..822048.10 rows=334250 width=53) (actual time=1637.597..1886.015 rows=1212 loops=3)
Merge Cond: (((cp."FirstName")::text = (scp."FirstName")::text) AND ((cp."LastName")::text = (scp."LastName")::text) AND (cp."DealerGroupId" = scp."DealerGroupId"))
-> Sort (cost=564987.54..565957.09 rows=387821 width=53) (actual time=1632.503..1841.309 rows=284808 loops=3)
Sort Key: cp."FirstName", cp."LastName", cp."DealerGroupId"
Sort Method: external merge Disk: 18248kB
Worker 0: Sort Method: external merge Disk: 18720kB
Worker 1: Sort Method: external merge Disk: 18720kB
-> Parallel Seq Scan on "sc_CustomerProfiles" cp (cost=0.00..515729.99 rows=387821 width=53) (actual time=575.396..1171.259 rows=284808 loops=3)
Filter: ((("FirstName" IS NOT NULL) OR ("LastName" IS NOT NULL)) AND ("UpdatedDate" > '2020-07-01 00:00:00+07'::timestamp with time zone))
Rows Removed by Filter: 2613490
-> Sort (cost=252316.98..252533.20 rows=86489 width=53) (actual time=4.940..5.162 rows=2937 loops=3)
Sort Key: scp."FirstName", scp."LastName", scp."DealerGroupId"
Sort Method: quicksort Memory: 440kB
Worker 0: Sort Method: quicksort Memory: 440kB
Worker 1: Sort Method: quicksort Memory: 440kB
-> Index Scan using "sc_CustomerProfiles_ProfileId" on "sc_CustomerProfiles" scp (cost=0.43..242267.28 rows=86489 width=53) (actual time=0.018..1.700 rows=3055 loops=3)
Index Cond: ("ProfileId" < 0)
Planning Time: 1.337 ms
JIT:
Functions: 79
Options: Inlining true, Optimization true, Expressions true, Deforming true
Timing: Generation 16.630 ms, Inlining 216.395 ms, Optimization 990.256 ms, Emission 518.330 ms, Total 1741.611 ms
Execution Time: 1992.259 ms
While running on our new DB on RDS give this result
Finalize GroupAggregate (cost=744995.34..848665.34 rows=195480 width=61) (actual time=144257.571..194501.899 rows=621 loops=1)
Group Key: cp."FirstName", cp."LastName", cp."DealerGroupId"
Filter: (count(*) > 1)
Rows Removed by Filter: 2261
-> Gather Merge (cost=744995.34..833031.15 rows=664296 width=61) (actual time=144214.280..194498.590 rows=3190 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Partial GroupAggregate (cost=743995.31..755354.88 rows=332148 width=61) (actual time=139429.298..187940.480 rows=1063 loops=3)
Group Key: cp."FirstName", cp."LastName", cp."DealerGroupId"
-> Merge Semi Join (cost=743995.31..748711.92 rows=332148 width=53) (actual time=139405.473..187938.320 rows=1212 loops=3)
Merge Cond: (((cp."FirstName")::text = (scp."FirstName")::text) AND ((cp."LastName")::text = (scp."LastName")::text) AND (cp."DealerGroupId" = scp."DealerGroupId"))
-> Sort (cost=493373.41..494335.55 rows=384857 width=53) (actual time=138424.282..182706.702 rows=285254 loops=3)
Sort Key: cp."FirstName", cp."LastName", cp."DealerGroupId"
Sort Method: external merge Disk: 19464kB
Worker 0: Sort Method: external merge Disk: 17672kB
Worker 1: Sort Method: external merge Disk: 18616kB
-> Parallel Seq Scan on "sc_CustomerProfiles" cp (cost=0.00..444513.80 rows=384857 width=53) (actual time=0.048..1509.801 rows=285255 loops=3)
Filter: ((("FirstName" IS NOT NULL) OR ("LastName" IS NOT NULL)) AND ("UpdatedDate" > '2020-07-01 00:00:00+07'::timestamp with time zone))
Rows Removed by Filter: 2613405
-> Sort (cost=250621.90..250838.81 rows=86762 width=53) (actual time=977.557..978.400 rows=2940 loops=3)
Sort Key: scp."FirstName", scp."LastName", scp."DealerGroupId"
Sort Method: quicksort Memory: 441kB
Worker 0: Sort Method: quicksort Memory: 441kB
Worker 1: Sort Method: quicksort Memory: 441kB
-> Index Scan using "sc_CustomerProfiles_ProfileId" on "sc_CustomerProfiles" scp (cost=0.43..240537.35 rows=86762 width=53) (actual time=0.079..3.373 rows=3057 loops=3)
Index Cond: ("ProfileId" < 0)
Planning Time: 31.569 ms
Execution Time: 194505.100 ms
I notice that the 'scan' parts have a similar duration but the 'sort' parts are far apart. The instance specs are not same but I don't see high CPU or memory utilization. What could the root cause of this or how should I investigate this issue?
Solution
Turn out the root cause is the bug only for the Thai language in glibc collation function (https://sourceware.org/bugzilla/show_bug.cgi?id=18441). The reason this was working on EC2 is that we were using a postgres alpine docker image which uses musl instead of glibc.
Since we do not actually need to order columns in Thai, changing the LC_COLLATE to 'C' does solve the problem. Note that if we need to, we could also use ICU collation instead.