postgreSQL数据倾斜时全表vs索引

开发反馈一个简单的count语句接口压测超时,查看到该表上原本作为条件的索引被disable了,加上表中数据倾斜严重,导致不论传参在表中分布情况如何都会走全表扫

测试表

taria=# create table skewtest(id int, record varchar(30));
CREATE TABLE
taria=# create index idx_record on  skewtest(record);
CREATE INDEX
taria=# \d+ skewtest
                                         Table "public.skewtest"
 Column |         Type          | Collation | Nullable | Default | Storage  | Stats target | Description 
--------+-----------------------+-----------+----------+---------+----------+--------------+-------------
 id     | integer               |           |          |         | plain    |              | 
 record | character varying(50) |           |          |         | extended |              | 
Indexes:
    "idx_record" btree (record)
Access method: heap

造数据

ALTER TABLE skewtest ALTER COLUMN record TYPE varchar(50);

taria=# insert into skewtest(id,record) select generate_series(0,9) ,md5(random()::text) from generate_series(1,10); 
INSERT 0 100
taria=# select * from skewtest limit 10 ;
 id |              record              
----+----------------------------------
  0 | 515e2288a1f4472c24b1a5374c9ddc9a
  1 | 4c4d0a0c541f4fc2ff017e621a0c291b
  2 | a74a4a39a08649578319cc7066e404b5
  3 | 68bd3b4c42b2143d88204e663022021c
  4 | 1d81e8daf0f0e6c605a1e80aa271a41f
  5 | f047ab023c79015541e4a4c5a35f915b
  6 | 3878cf3b8774689868e04dbbe269e7c3
  7 | 5635dc3b9306943b1711c20492ee132d
  8 | 575677864bb988f1d3520ddc9d65348c
  9 | c651c43aacf7fbece4be09bb733bf3c1
(10 rows)

taria=# insert into  skewtest2(id,record) select generate_series(0,9),'f047ab023c79015541e4a4c5a35f915b' from generate_series(0,3350000);
INSERT 0 3350010
taria=# select count(*) from skewtest;
  count  
---------
 3350110
(1 row)

普通数据走索引
 Aggregate  (cost=4.58..4.58 rows=1 width=8) (actual time=0.148..0.148 rows=1 loops=1)
   ->  Index Only Scan using idx_record on skewtest  (cost=0.56..4.57 rows=1 width=0) (actual time=0.139..0.141 rows=1 loops=1)
         Index Cond: (record = '3878cf3b8774689868e04dbbe269e7c3'::text)
         Heap Fetches: 0
 Planning Time: 0.096 ms
 Execution Time: 0.182 ms
(6 rows)

 update pg_index set indisvalid = true where indexrelid ='idx_record':: regclass ;

强制普通数据走全表
 Aggregate  (cost=46366.59..46366.60 rows=1 width=8) (actual time=220.071..220.071 rows=1 loops=1)
   ->  Gather  (cost=1000.00..46366.59 rows=1 width=0) (actual time=0.189..223.421 rows=1 loops=1)
         Workers Planned: 2
         Workers Launched: 2
         ->  Parallel Seq Scan on skewtest  (cost=0.00..45366.49 rows=1 width=0) (actual time=142.571..215.831 rows=0 loops=3)
               Filter: ((record)::text = '3878cf3b8774689868e04dbbe269e7c3'::text)
               Rows Removed by Filter: 1116703
 Planning Time: 0.077 ms
 Execution Time: 223.464 ms
倾斜数据走全表
 explain analyze select count(*) from skewtest where record ='f047ab023c79015541e4a4c5a35f915b';
 Finalize Aggregate  (cost=49856.40..49856.41 rows=1 width=8) (actual time=402.832..402.832 rows=1 loops=1)
   ->  Gather  (cost=49856.19..49856.40 rows=2 width=8) (actual time=402.714..405.983 rows=3 loops=1)
         Workers Planned: 2
         Workers Launched: 2
         ->  Partial Aggregate  (cost=48856.19..48856.20 rows=1 width=8) (actual time=398.518..398.518 rows=1 loops=3)
               ->  Parallel Seq Scan on skewtest  (cost=0.00..45366.49 rows=1395879 width=0) (actual time=0.062..284.255 rows=1116670 loops=3)
                     Filter: ((record)::text = 'f047ab023c79015541e4a4c5a35f915b'::text)
                     Rows Removed by Filter: 33
 Planning Time: 0.103 ms
 Execution Time: 406.033 ms
(10 rows)

taria=# set enable_seqscan TO off;
SET
这里不是不走全表扫,是走了个不带并发聚合的全表 更慢了

倾斜数据强制走索引
  Aggregate  (cost=10000078169.65..10000078169.66 rows=1 width=8) (actual time=1160.122..1160.122 rows=1 loops=1)
   ->  Seq Scan on skewtest  (cost=10000000000.00..10000069794.38 rows=3350110 width=0) (actual time=0.049..820.449 rows=3350011 loops=1)
         Filter: ((record)::text = 'f047ab023c79015541e4a4c5a35f915b'::text)
         Rows Removed by Filter: 99
 Planning Time: 0.089 ms
 Execution Time: 1160.159 ms
(6 rows)
 
 差异在Partial Aggregate 因为count带来的并行聚合,可以用set max_parallel_workers控制并发

为了测试安个pg_hint_plan

大数据量3000w+
explain analyze select count(*) from skewtest where record ='f047ab023c79015541e4a4c5a35f915b';                             
---------------------------------------------------------------------------------------------------------------------------------------------------
 Finalize Aggregate  (cost=489543.90..489543.91 rows=1 width=8) (actual time=2038.704..2039.813 rows=1 loops=1)
   ->  Gather  (cost=489543.69..489543.90 rows=2 width=8) (actual time=2038.634..2039.807 rows=3 loops=1)
         Workers Planned: 2
         Workers Launched: 2
         ->  Partial Aggregate  (cost=488543.69..488543.70 rows=1 width=8) (actual time=2034.316..2034.316 rows=1 loops=3)
               ->  Parallel Seq Scan on skewtest  (cost=0.00..453647.74 rows=13958379 width=0) (actual time=0.056..1412.934 rows=11166670 loops=3)
                     Filter: ((record)::text = 'f047ab023c79015541e4a4c5a35f915b'::text)
                     Rows Removed by Filter: 33
 Planning Time: 0.314 ms
 Execution Time: 2039.909 ms
(10 rows)

explain analyze /*+ IndexScan(skewtest) */select count(*) from skewtest where record ='f047ab023c79015541e4a4c5a35f915b';
                                                                             QUERY PLAN                                                                              
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Finalize Aggregate  (cost=825342.34..825342.35 rows=1 width=8) (actual time=2220.391..2227.868 rows=1 loops=1)
   ->  Gather  (cost=825342.13..825342.34 rows=2 width=8) (actual time=2220.386..2227.864 rows=3 loops=1)
         Workers Planned: 2
         Workers Launched: 2
         ->  Partial Aggregate  (cost=824342.13..824342.14 rows=1 width=8) (actual time=2215.918..2215.919 rows=1 loops=3)
               ->  Parallel Index Scan using idx_record on skewtest  (cost=0.56..789446.18 rows=13958379 width=0) (actual time=0.129..1593.646 rows=11166670 loops=3)
                     Index Cond: ((record)::text = 'f047ab023c79015541e4a4c5a35f915b'::text)
 Planning Time: 0.097 ms
 Execution Time: 2227.973 ms
(9 rows)

小数据量33w+
taria=# select count(*) from skewtest2;
 count  
--------
 335110
(1 row)
      1 | c2876aa9a0642bbc86b6955731b35a19
      1 | c3a7199d80054e67b798e889b0618af4
      1 | c40cd2b1e0dcb8a6aae65a81e3ccfe58
      1 | c433904380e4ef6075b8cd1e5d8536f8
      1 | c64c7c448294aec005e8d00b4113e96b
      1 | c7fbf598f403f62ca704da0059d4c31a
      1 | c8c6b566ff4ccd52ae523c303b4f50be
      1 | cd28ad7edb887b46d29c576cae60a012
      1 | d0026ad8f3753e5c52e1e1dbb7746b56
      1 | d582ed9a85d618e60251989e91820179
      1 | d76ad7e27cc0fa261554167c4e358fa0
      1 | d7c555e9e731b724de2e7a07d861339c
      1 | dec495dc37e497e92b3ac7b38993737f
      1 | e4101c5bcd989a9b0af539184d477643
      1 | e933bf160e13a94c8f66101e4949b5f1
      1 | e9b75293969bf33e70a0571b9cbcc208
      1 | ecb4306a179958491a9d6b94d22c31ab
      1 | ef5c7f2f06ecd9f9316d20af59ce81f3
 335010 | f047ab023c79015541e4a4c5a35f915b
      1 | f255cf5361158e5847d843ac200d6442
      1 | fe78bb64b36914d41fc92f0b18245bcf

set max_parallel_workers =0;

explain analyze /*+ SeqScan(skewtest2)*/select count(*) from skewtest2 where record ='f047ab023c79015541e4a4c5a35f915b';                 
                                                                 QUERY PLAN                                                                 
--------------------------------------------------------------------------------------------------------------------------------------------
 Finalize Aggregate  (cost=6749.80..6749.81 rows=1 width=8) (actual time=72.599..72.623 rows=1 loops=1)
   ->  Gather  (cost=6749.69..6749.80 rows=1 width=8) (actual time=72.595..72.619 rows=1 loops=1)
         Workers Planned: 1
         Workers Launched: 0
         ->  Partial Aggregate  (cost=5749.69..5749.70 rows=1 width=8) (actual time=72.477..72.478 rows=1 loops=1)
               ->  Parallel Seq Scan on skewtest2  (cost=0.00..5257.04 rows=197058 width=0) (actual time=0.015..49.267 rows=335010 loops=1)
                     Filter: ((record)::text = 'f047ab023c79015541e4a4c5a35f915b'::text)
                     Rows Removed by Filter: 100
 Planning Time: 0.119 ms
 Execution Time: 72.644 ms
(10 rows)



explain analyze /*+ IndexScan(skewtest2) */select count(*) from skewtest2 where record ='f047ab023c79015541e4a4c5a35f915b';
                                                                           QUERY PLAN                                                                           
----------------------------------------------------------------------------------------------------------------------------------------------------------------
 Finalize Aggregate  (cost=9883.59..9883.60 rows=1 width=8) (actual time=75.539..75.570 rows=1 loops=1)
   ->  Gather  (cost=9883.48..9883.59 rows=1 width=8) (actual time=75.535..75.567 rows=1 loops=1)
         Workers Planned: 1
         Workers Launched: 0
         ->  Partial Aggregate  (cost=8883.48..8883.49 rows=1 width=8) (actual time=75.417..75.417 rows=1 loops=1)
               ->  Parallel Index Scan using idx_record2 on skewtest2  (cost=0.42..8390.83 rows=197058 width=0) (actual time=0.019..51.857 rows=335010 loops=1)
                     Index Cond: ((record)::text = 'f047ab023c79015541e4a4c5a35f915b'::text)
 Planning Time: 0.092 ms
 Execution Time: 75.597 ms

没什么好总结,感觉就是全表好像没比走索引快多少(这个小数据集),根据表情况不同差距应该越明显,简单记录下。

你可能感兴趣的:(Postgres,数据库,postgresql)