2017-11-13 3 views
1

Googleドライブと同様に、ユーザーが所有するファイル/フォルダのリストを返すAPIを作成しようとしています。ユーザのファイル/フォルダ/その他の情報は、PostgreSQLのテーブルに格納されています。PostgreSQLのクエリでビューに参加するとパフォーマンスが低下する

これらのテーブルからファイルリストを取得するgc_driveというビューを作成しました。はい、ファイル、フォルダ、仮想ファイルのテーブルが異なるため、テーブルと呼ぶので、これらのレコードをすべて結合してファイルシステムとして表示するようにします.100%のファイルシステムではありません。

ビューのパフォーマンスは正常に見えますが、必要な列をインデックス化しています。ビューには270万以上のデータがすべて結合されていますが、IDでフィルタリングされ、フィルタリングされたレコードはミリ秒単位で親によって戻されます。ただし、gc_driveからcount(*)を選択しようとすると、ファイル、フォルダ、リソースなどのリストを含む基本表にはそれぞれ700万を超えるレコードがあるため、69秒かかることになります。

ユーザーがリソースにアクセスできるかどうかについての情報を持つfn_resource_permissionsテーブルを使用して、gc_driveビューに参加したいとします。

私は、ユーザーの権限でレコードを現在のログに記録されたを取得するために、次のクエリを使用し、ほぼ瞬時に戻る:

select*from 
fn_resource_permissions 
WHERE ((fn_resource_permissions.permission_id IN (23, 
                24, 
                25, 
                37, 
                36) 
AND (fn_resource_permissions.user_id = 2 
    OR fn_resource_permissions.user_id = 1))) 

--59 records returned 

上記のクエリは私にユーザーが持っているリソースへのすべての権限を返します。これらのレコードを使用してgc_driveビューをフィルタリングします。私はgc_driveビューをフィルタリングするために、次のクエリでこれらの59件のレコードを使用しようとすると

レコードも瞬時に返す:

SELECT gc_drive.id, 
     "fn_resources"."owner_id" AS "fn_resources.owner_id", 
     "gc_drive".* 
FROM "gc_drive" 
JOIN "fn_resources" ON ("gc_drive"."resource_id" = "fn_resources"."id") 
WHERE "fn_resources"."archived" = 0 
AND gc_drive.id IN(
    1234, 
    1235, 
    1236, 
    ... 
); 

を、私は一緒に上記2つのクエリに参加しようとすると、問題がある:

SELECT gc_drive.id, 
     "fn_resources"."owner_id" AS "fn_resources.owner_id", 
     "gc_drive".* 
FROM "gc_drive" 
JOIN "fn_resources" ON ("gc_drive"."resource_id" = "fn_resources"."id") 
JOIN "fn_resource_permissions" ON ("fn_resource_permissions"."resource_id" = fn_resources.id) 
WHERE "fn_resources"."archived" = 0 
AND ((fn_resource_permissions.permission_id IN (23, 
               24, 
               25, 
               37, 
               36) 
      AND (fn_resource_permissions.user_id = 2 
       OR fn_resource_permissions.user_id = 1))) 
LIMIT 101; 

私はExplainプランを見ると、PostgreSQLがgc_driveビュー全体を実現していることがわかりましたが、その後はリソース権限でフィルタリングしようとしています。これにより、ミリ秒単位ではなく数分でクエリが実行されます。私はまた、リソースのパーミッションをwith節に入れようとしましたが、それは同じです。私は解決策の1つが各ユーザのコンテンツを独自のスキーマで分けることができることを知っていますが、gc_driveビューとPostgreSQLを使わずに他のテーブルに結合する方がより効率的な方法があるかどうかを知りたいと思っています。 PostgreSQLがレコードセット全体でフィルタリングできるようにクエリを変更する方法はありますか?

は、上記のクエリに計画を分析説明:

┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 
│                      QUERY PLAN                      │ 
├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ 
│ Limit (cost=4845.56..57547586.51 rows=101 width=722) (actual time=201371.236..201371.236 rows=0 loops=1)                    │ 
│ -> Hash Join (cost=4845.56..681971785.33 rows=1197 width=722) (actual time=201371.234..201371.234 rows=0 loops=1)                 │ 
│   Hash Cond: ("*SELECT* 1".resource_id = fn_resources.id)                              │ 
│   -> Append (cost=36.10..681748786.17 rows=15867640 width=247) (actual time=17521.979..200235.975 rows=2737742 loops=1)              │ 
│    -> Subquery Scan on "*SELECT* 1" (cost=36.10..533636.75 rows=11252 width=150) (actual time=0.004..0.004 rows=0 loops=1)            │ 
│      -> Hash Join (cost=36.10..533524.23 rows=11252 width=150) (actual time=0.003..0.003 rows=0 loops=1)                │ 
│       Hash Cond: (fnr_child.type = fnrt.type)                              │ 
│       -> Nested Loop (cost=0.00..66613.04 rows=1940 width=122) (actual time=0.003..0.003 rows=0 loops=1)              │ 
│         -> Nested Loop (cost=0.00..49607.76 rows=1940 width=100) (actual time=0.002..0.002 rows=0 loops=1)             │ 
│          -> Nested Loop (cost=0.00..32602.48 rows=1940 width=104) (actual time=0.002..0.002 rows=0 loops=1)           │ 
│            -> Nested Loop (cost=0.00..16315.94 rows=1940 width=12) (actual time=0.002..0.002 rows=0 loops=1)          │ 
│             -> Seq Scan on gc_virtual_file gvf (cost=0.00..29.40 rows=1940 width=8) (actual time=0.001..0.001 rows=0 loops=1)     │ 
│             -> Index Scan using gc_file_resource_id_key on gc_file gf_child (cost=0.00..8.38 rows=1 width=4) (never executed)     │ 
│               Index Cond: (resource_id = gvf.resource_id)                     │ 
│            -> Index Scan using gc_file_resource_id_key on gc_file gf_parent (cost=0.00..8.38 rows=1 width=92) (never executed)      │ 
│             Index Cond: (resource_id = gvf.parent_id)                       │ 
│          -> Index Scan using fn_resources_pkey on fn_resources fnr_parent (cost=0.00..8.75 rows=1 width=4) (never executed)       │ 
│            Index Cond: (id = gvf.parent_id)                           │ 
│         -> Index Scan using fn_resources_pkey on fn_resources fnr_child (cost=0.00..8.75 rows=1 width=30) (never executed)         │ 
│          Index Cond: (id = gvf.resource_id)                            │ 
│       -> Hash (cost=21.60..21.60 rows=1160 width=36) (never executed)                       │ 
│         -> Seq Scan on fn_resource_types fnrt (cost=0.00..21.60 rows=1160 width=36) (never executed)              │ 
│       SubPlan 6                                     │ 
│        -> Index Scan using users_pkey on fn_users (cost=0.00..8.28 rows=1 width=13) (never executed)               │ 
│         Index Cond: (id = fnr_child.owner_id)                            │ 
│       SubPlan 7                                     │ 
│        -> Index Scan using gc_tables_resource_id_key on gc_tables gt (cost=0.00..8.28 rows=1 width=18) (never executed)          │ 
│         Index Cond: (resource_id = fnr_child.id)                           │ 
│       SubPlan 8                                     │ 
│        -> Index Scan using gc_webmapservices_resource_id_key on gc_webmapservices gwms (cost=0.00..8.27 rows=1 width=418) (never executed)      │ 
│         Index Cond: (resource_id = fnr_child.id)                           │ 
│       SubPlan 9                                     │ 
│        -> Index Scan using gc_tables_resource_id_key on gc_tables gt (cost=0.00..8.31 rows=1 width=14) (never executed)          │ 
│         Index Cond: (resource_id = fnr_child.id)                           │ 
│       SubPlan 10                                     │ 
│        -> Index Scan using gc_webmapservices_resource_id_key on gc_webmapservices gwms (cost=0.00..8.30 rows=1 width=267) (never executed)      │ 
│         Index Cond: (resource_id = fnr_child.id)                           │ 
│    -> Subquery Scan on "*SELECT* 2" (cost=580092.62..681215149.42 rows=15856388 width=247) (actual time=17521.724..199482.039 rows=2737742 loops=1)      │ 
│      -> Hash Join (cost=580092.62..681056585.54 rows=15856388 width=247) (actual time=17521.668..198482.521 rows=2737742 loops=1)         │ 
│       Hash Cond: (fnr_child.type = fnrt.type)                              │ 
│       -> Hash Join (cost=580056.53..1189954.39 rows=2733860 width=219) (actual time=16768.431..88474.618 rows=2737742 loops=1)         │ 
│         Hash Cond: (gf_parent.resource_id = fnr_parent.id)                         │ 
│         -> Hash Join (cost=388738.70..943959.36 rows=2733860 width=219) (actual time=13619.604..72625.523 rows=2737742 loops=1)        │ 
│          Hash Cond: (gf_child.path = gf_parent.pathname)                         │ 
│          -> Hash Join (cost=202378.85..538142.64 rows=2733860 width=168) (actual time=6429.263..53255.517 rows=2737743 loops=1)      │ 
│            Hash Cond: (fnr_child.id = gf_child.resource_id)                       │ 
│            -> Seq Scan on fn_resources fnr_child (cost=0.00..114415.70 rows=6152170 width=30) (actual time=21.243..19364.289 rows=6119914 loops=1) │ 
│            -> Hash (cost=112139.60..112139.60 rows=2733860 width=142) (actual time=5666.626..5666.626 rows=2737743 loops=1)      │ 
│             Buckets: 262144 Batches: 2 Memory Usage: 240441kB                     │ 
│             -> Seq Scan on gc_file gf_child (cost=0.00..112139.60 rows=2733860 width=142) (actual time=0.015..3442.788 rows=2737751 loops=1) │ 
│          -> Hash (cost=112139.60..112139.60 rows=2733860 width=92) (actual time=7173.094..7173.094 rows=2737751 loops=1)        │ 
│            Buckets: 262144 Batches: 2 Memory Usage: 169423kB                      │ 
│            -> Seq Scan on gc_file gf_parent (cost=0.00..112139.60 rows=2733860 width=92) (actual time=0.007..5057.412 rows=2737751 loops=1)  │ 
│         -> Hash (cost=114415.70..114415.70 rows=6152170 width=4) (actual time=3145.424..3145.424 rows=6119914 loops=1)          │ 
│          Buckets: 1048576 Batches: 1 Memory Usage: 215154kB                       │ 
│          -> Seq Scan on fn_resources fnr_parent (cost=0.00..114415.70 rows=6152170 width=4) (actual time=0.008..1186.819 rows=6119914 loops=1)   │ 
│       -> Hash (cost=21.60..21.60 rows=1160 width=36) (actual time=0.021..0.021 rows=21 loops=1)                 │ 
│         Buckets: 1024 Batches: 1 Memory Usage: 1kB                           │ 
│         -> Seq Scan on fn_resource_types fnrt (cost=0.00..21.60 rows=1160 width=36) (actual time=0.005..0.012 rows=21 loops=1)        │ 
│       SubPlan 1                                     │ 
│        -> Index Scan using users_pkey on fn_users (cost=0.00..8.28 rows=1 width=13) (actual time=0.009..0.009 rows=1 loops=2737742)       │ 
│         Index Cond: (id = fnr_child.owner_id)                            │ 
│       SubPlan 2                                     │ 
│        -> Index Scan using gc_file_resource_id_key on gc_file gf (cost=0.00..8.62 rows=1 width=47) (never executed)           │ 
│         Index Cond: (resource_id = fnr_child.id)                           │ 
│       SubPlan 3                                     │ 
│        -> Index Scan using gc_file_resource_id_key on gc_file gf (cost=0.00..8.62 rows=1 width=47) (actual time=0.025..0.025 rows=1 loops=2665337)    │ 
│         Index Cond: (resource_id = fnr_child.id)                           │ 
│       SubPlan 4                                     │ 
│        -> Index Scan using gc_file_resource_id_key on gc_file gf (cost=0.00..8.63 rows=1 width=129) (never executed)           │ 
│         Index Cond: (resource_id = fnr_child.id)                           │ 
│       SubPlan 5                                     │ 
│        -> Index Scan using gc_file_resource_id_key on gc_file gf (cost=0.00..8.66 rows=1 width=145) (actual time=0.003..0.003 rows=1 loops=2665337)   │ 
│         Index Cond: (resource_id = fnr_child.id)                           │ 
│   -> Hash (cost=4804.79..4804.79 rows=374 width=12) (actual time=412.434..412.434 rows=59 loops=1)                   │ 
│    Buckets: 1024 Batches: 1 Memory Usage: 3kB                               │ 
│    -> Nested Loop (cost=13.32..4804.79 rows=374 width=12) (actual time=156.510..412.313 rows=59 loops=1)                 │ 
│      -> Bitmap Heap Scan on fn_resource_permissions (cost=13.32..1454.38 rows=374 width=4) (actual time=155.407..402.734 rows=59 loops=1)       │ 
│       Recheck Cond: ((user_id = 2) OR (user_id = 1))                            │ 
│       Filter: (permission_id = ANY ('{23,24,25,37,36}'::integer[]))                        │ 
│       -> BitmapOr (cost=13.32..13.32 rows=391 width=0) (actual time=88.402..88.402 rows=0 loops=1)                │ 
│         -> Bitmap Index Scan on resource_permissions_user (cost=0.00..6.57 rows=196 width=0) (actual time=88.380..88.380 rows=61 loops=1)     │ 
│          Index Cond: (user_id = 2)                              │ 
│         -> Bitmap Index Scan on resource_permissions_user (cost=0.00..6.57 rows=196 width=0) (actual time=0.016..0.016 rows=0 loops=1)      │ 
│          Index Cond: (user_id = 1)                              │ 
│      -> Index Scan using fn_resources_pkey on fn_resources (cost=0.00..8.95 rows=1 width=8) (actual time=0.154..0.155 rows=1 loops=59)        │ 
│       Index Cond: (id = fn_resource_permissions.resource_id)                          │ 
│       Filter: (archived = 0)                                  │ 
│ Total runtime: 201373.236 ms                                       │ 
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 

感謝を。 fn_resourcesfn_resource_permissionsにより濾過し、次に同じデータがgc_driveに存在するにもかかわらず、gc_drive上に接合されるように

+0

参加する前にfn_resourcesをフィルタリングしてみてください? –

+0

**あなたの質問を**編集し** ** 'explain(analyze、verbose、buffers)' **を使用して生成された実行計画を追加してください。 [**フォーマットされたテキスト**](http://stackoverflow.com/help/formatting)、[スクリーンショットなし](http://meta.stackoverflow.com/questions/285551/why-may-i-not -upload-images-of-ask-a-question/285557#285557) –

+0

'explain'を使って生成した計画を追加しました。**' explain(analyze 、verbose、buffers) '** –

答えて

0

あなたのクエリが構成されています。

これはややこしいことですが、早めにwhere節でgc_driveによるfn_resource_permissionsのフィルタリングを試してみてください。

select 
    gc_drive.id, 
    fn_resources.owner_id, 
    gc_drive.* 
from 
    gc_drive 
    join fn_resources on(
     gc_drive.resource_id = fn_resources.id 
    ) 
where 
    gc_drive.resource_id in(
     select 
      fn_resource_permissions.resource_id 
     from 
      fn_resource_permissions 
     where 
      fn_resource_permissions.permission_id in (23, 24, 25, 37, 36) 
      and fn_resource_permissions.user_id in(1, 2) 
    ) 
    and fn_resources.archived = 0 
limit 101; 

Alternativley、たぶんユーザーによって分割

select 
    gc_drive.id, 
    fn_resources.owner_id, 
    gc_drive.* 
from 
    gc_drive 
    join fn_resources on(
     gc_drive.resource_id = fn_resources.id 
     and fn_resources.id in(
      select 
       fn_resource_permissions.resource_id 
      from 
       fn_resource_permissions 
      where 
       fn_resource_permissions.permission_id in (23, 24, 25, 37, 36) 
       and fn_resource_permissions.user_id in(1, 2) 
     ) 
    ) 
where 
    fn_resources.archived = 0 
limit 101; 
関連する問題