Changes between Initial Version and Version 1 of QueryOptimization


Ignore:
Timestamp:
05/26/26 16:29:28 (35 hours ago)
Author:
231088
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • QueryOptimization

    v1 v1  
     1= Оптимизација на прашалници и погледи =
     2
     3Во оваа фаза ќе ги анализираме погледите дефинирани во [[DatabaseCreation|Фаза 2]] преку прашалници базирани на реални сценарија кои ќе бидат присутни во нашата апликација и истите ќе се обидеме да ги оптимизираме.
     4
     5=== 1. Анализа на поглед 1, добивање на бројот на следбеници и бројот на профили кои ги следи даден корисник ===
     6
     7Прашалниците кои ќе ги тестираме се следните:
     8{{{
     9-- 1A: информации за конкретен корисник
     10SELECT * FROM user_follow_info WHERE user_id = 5;
     11
     12-- 1B: топ 10 најследени корисници
     13SELECT * FROM user_follow_info ORDER BY followers DESC LIMIT 10;
     14}}}
     15
     16==== Време на извршување без индекси:
     17
     18**1A - 18.639 ms**
     19
     20{{{
     21 Nested Loop Left Join  (cost=0.84..2007.41 rows=1 width=41) (actual time=18.370..18.375 rows=1 loops=1)
     22   ->  Nested Loop Left Join  (cost=0.42..1998.96 rows=1 width=24) (actual time=16.163..16.167 rows=1 loops=1)
     23         ->  GroupAggregate  (cost=0.42..8.45 rows=1 width=16) (actual time=0.099..0.101 rows=1 loops=1)
     24               ->  Index Only Scan using follows_follower_user_id_followed_user_id_key on follows  (cost=0.42..8.44 rows=1 width=16) (actual time=0.090..0.093 rows=1 loops=1)
     25                     Index Cond: (follower_user_id = 5)
     26                     Heap Fetches: 1
     27         ->  GroupAggregate  (cost=0.00..1990.49 rows=1 width=16) (actual time=16.060..16.061 rows=1 loops=1)
     28               ->  Seq Scan on follows follows_1  (cost=0.00..1986.00 rows=1793 width=8) (actual time=0.073..15.916 rows=1727 loops=1)
     29                     Filter: (followed_user_id = 5)
     30                     Rows Removed by Filter: 98273
     31   ->  Index Scan using users_pkey on users u  (cost=0.42..8.44 rows=1 width=25) (actual time=2.202..2.202 rows=1 loops=1)
     32         Index Cond: (id = 5)
     33 Planning Time: 2.882 ms
     34 Execution Time: 18.639 ms
     35}}}
     36
     37**1B - 6055.326 ms**
     38
     39{{{
     40 Limit  (cost=193416.23..193416.26 rows=10 width=41) (actual time=6013.127..6013.139 rows=10 loops=1)
     41   ->  Sort  (cost=193416.23..207558.87 rows=5657054 width=41) (actual time=5995.646..5995.657 rows=10 loops=1)
     42         Sort Key: (COALESCE(uf2.followers, '0'::bigint)) DESC
     43         Sort Method: top-N heapsort  Memory: 25kB
     44         ->  Hash Left Join  (cost=17633.85..71169.33 rows=5657054 width=41) (actual time=1875.103..5973.746 rows=95177 loops=1)
     45               Hash Cond: (follows.follower_user_id = uf2.user_id)
     46               ->  Hash Right Join  (cost=9034.90..60811.91 rows=92693 width=33) (actual time=1798.455..5836.973 rows=95177 loops=1)
     47                     Hash Cond: (u.id = follows.follower_user_id)
     48                     ->  Seq Scan on users u  (cost=0.00..35027.00 rows=1000000 width=25) (actual time=1598.256..4992.144 rows=1000000 loops=1)
     49                     ->  Hash  (cost=7423.24..7423.24 rows=92693 width=16) (actual time=200.143..200.146 rows=95177 loops=1)
     50                           Buckets: 16384  Batches: 16  Memory Usage: 407kB
     51                           ->  GroupAggregate  (cost=0.42..7423.24 rows=92693 width=16) (actual time=0.180..164.602 rows=95177 loops=1)
     52                                 Group Key: follows.follower_user_id
     53                                 ->  Index Only Scan using follows_follower_user_id_followed_user_id_key on follows  (cost=0.42..5996.31 rows=100000 width=16) (actual time=0.134..130.406 rows=100000 loops=1)
     54                                       Heap Fetches: 100000
     55               ->  Hash  (cost=8386.37..8386.37 rows=12206 width=16) (actual time=76.356..76.358 rows=30563 loops=1)
     56                     Buckets: 16384 (originally 16384)  Batches: 4 (originally 2)  Memory Usage: 480kB
     57                     ->  Subquery Scan on uf2  (cost=7361.00..8386.37 rows=12206 width=16) (actual time=43.415..66.845 rows=30563 loops=1)
     58                           ->  HashAggregate  (cost=7361.00..8264.31 rows=12206 width=16) (actual time=43.406..64.059 rows=30563 loops=1)
     59                                 Group Key: follows_1.followed_user_id
     60                                 Planned Partitions: 4  Batches: 21  Memory Usage: 601kB  Disk Usage: 824kB
     61                                 ->  Seq Scan on follows follows_1  (cost=0.00..1736.00 rows=100000 width=8) (actual time=0.022..11.332 rows=100000 loops=1)
     62 Planning Time: 1.715 ms
     63 Execution Time: 6055.326 ms
     64}}}
     65
     66
     67Веќе постои индекс на `(follower_user_id, followed_user_id)` поради unique constraint во ddl-от, па `follower_user_id` може да се земе од таму, но за да се земе `followed_user_id` мора да се скенира табелата секвенцијално. Затоа, го додаваме следниот индекс:
     68{{{
     69CREATE INDEX idx_follows_followed_user_id ON follows(followed_user_id);
     70}}}
     71
     72
     73==== Време на извршување со индекс:
     74
     75**1A — 3.993 ms** (беше 18.639 ms)
     76
     77{{{
     78 Nested Loop Left Join  (cost=27.03..806.01 rows=1 width=41) (actual time=3.702..3.704 rows=1 loops=1)
     79   ->  Nested Loop Left Join  (cost=26.61..797.56 rows=1 width=24) (actual time=3.683..3.685 rows=1 loops=1)
     80         ->  GroupAggregate  (cost=0.42..8.45 rows=1 width=16) (actual time=0.097..0.098 rows=1 loops=1)
     81               ->  Index Only Scan using follows_follower_user_id_followed_user_id_key on follows  (cost=0.42..8.44 rows=1 width=16) (actual time=0.088..0.090 rows=1 loops=1)
     82                     Index Cond: (follower_user_id = 5)
     83                     Heap Fetches: 1
     84         ->  GroupAggregate  (cost=26.19..789.09 rows=1 width=16) (actual time=3.582..3.583 rows=1 loops=1)
     85               ->  Bitmap Heap Scan on follows follows_1  (cost=26.19..784.60 rows=1793 width=8) (actual time=0.363..3.396 rows=1727 loops=1)
     86                     Recheck Cond: (followed_user_id = 5)
     87                     Heap Blocks: exact=666
     88                     ->  Bitmap Index Scan on idx_follows_followed_user_id  (cost=0.00..25.74 rows=1793 width=0) (actual time=0.255..0.255 rows=1727 loops=1)
     89                           Index Cond: (followed_user_id = 5)
     90   ->  Index Scan using users_pkey on users u  (cost=0.42..8.44 rows=1 width=25) (actual time=0.016..0.016 rows=1 loops=1)
     91         Index Cond: (id = 5)
     92 Planning Time: 2.325 ms
     93 Execution Time: 3.993 ms
     94}}}
     95
     96**1B — 1158.681 ms** (беше 6055.326 ms)
     97
     98{{{
     99 Limit  (cost=190809.26..190809.28 rows=10 width=41) (actual time=1119.728..1119.739 rows=10 loops=1)
     100   ->  Sort  (cost=190809.26..204951.89 rows=5657054 width=41) (actual time=1104.495..1104.505 rows=10 loops=1)
     101         Sort Key: (COALESCE(uf2.followers, '0'::bigint)) DESC
     102         Sort Method: top-N heapsort  Memory: 25kB
     103         ->  Hash Left Join  (cost=15026.87..68562.35 rows=5657054 width=41) (actual time=312.601..1083.510 rows=95177 loops=1)
     104               Hash Cond: (follows.follower_user_id = uf2.user_id)
     105               ->  Hash Right Join  (cost=9034.90..60811.91 rows=92693 width=33) (actual time=210.510..922.405 rows=95177 loops=1)
     106                     Hash Cond: (u.id = follows.follower_user_id)
     107                     ->  Seq Scan on users u  (cost=0.00..35027.00 rows=1000000 width=25) (actual time=62.089..259.973 rows=1000000 loops=1)
     108                     ->  Hash  (cost=7423.24..7423.24 rows=92693 width=16) (actual time=148.205..148.208 rows=95177 loops=1)
     109                           Buckets: 16384  Batches: 16  Memory Usage: 407kB
     110                           ->  GroupAggregate  (cost=0.42..7423.24 rows=92693 width=16) (actual time=0.140..116.621 rows=95177 loops=1)
     111                                 Group Key: follows.follower_user_id
     112                                 ->  Index Only Scan using follows_follower_user_id_followed_user_id_key on follows  (cost=0.42..5996.31 rows=100000 width=16) (actual time=0.094..83.835 rows=100000 loops=1)
     113                                       Heap Fetches: 100000
     114               ->  Hash  (cost=5779.40..5779.40 rows=12206 width=16) (actual time=101.825..101.827 rows=30563 loops=1)
     115                     Buckets: 16384 (originally 16384)  Batches: 4 (originally 2)  Memory Usage: 480kB
     116                     ->  Subquery Scan on uf2  (cost=0.29..5779.40 rows=12206 width=16) (actual time=1.560..90.836 rows=30563 loops=1)
     117                           ->  GroupAggregate  (cost=0.29..5657.34 rows=12206 width=16) (actual time=1.552..87.771 rows=30563 loops=1)
     118                                 Group Key: follows_1.followed_user_id
     119                                 ->  Index Only Scan using idx_follows_followed_user_id on follows follows_1  (cost=0.29..5035.28 rows=100000 width=8) (actual time=0.057..71.498 rows=100000 loops=1)
     120                                       Heap Fetches: 100000
     121 Planning Time: 1.857 ms
     122 Execution Time: 1158.681 ms
     123}}}
     124
     125=== 2. Анализа на поглед 2, најактивни корисници на платформата според бројот на слушања во изминатите 30 дена ===
     126
     127Прашалниците кои ќе ги тестираме се следните:
     128{{{
     129-- 2A: активност на еден корисник
     130SELECT * FROM user_activity_last_30_days WHERE user_id = 100376;
     131
     132-- 2B: топ 10 најактивни корисници
     133SELECT * FROM user_activity_last_30_days ORDER BY stream_count DESC LIMIT 10;
     134}}}
     135
     136==== Време на извршување без индекси:
     137
     138
     139**2A — 389.404 ms**
     140
     141{{{
     142 Nested Loop  (cost=1000.42..128052.52 rows=1 width=33) (actual time=341.291..351.235 rows=1 loops=1)
     143   ->  Index Scan using users_pkey on users u  (cost=0.42..8.44 rows=1 width=25) (actual time=0.760..0.764 rows=1 loops=1)
     144         Index Cond: (id = 100376)
     145   ->  GroupAggregate  (cost=1000.00..128044.07 rows=1 width=16) (actual time=340.519..350.456 rows=1 loops=1)
     146         ->  Gather  (cost=1000.00..128044.06 rows=1 width=16) (actual time=137.273..350.389 rows=2 loops=1)
     147               Workers Planned: 2
     148               Workers Launched: 2
     149               ->  Parallel Seq Scan on song_streams ss  (cost=0.00..127043.96 rows=1 width=16) (actual time=149.103..281.419 rows=1 loops=3)
     150                     Filter: ((user_id = 100376) AND (streamed_at <= now()) AND (streamed_at >= (CURRENT_DATE - 30)))
     151                     Rows Removed by Filter: 2258560
     152 Planning Time: 1.687 ms
     153 Execution Time: 389.404 ms
     154}}}
     155
     156**2B — 1976.733 ms**
     157
     158{{{
     159Limit  (cost=193729.83..193814.38 rows=10 width=162) (actual time=1965.655..1965.803 rows=10 loops=1)
     160  ->  Result  (cost=193729.83..5188284.34 rows=590722 width=162) (actual time=1953.440..1953.585 rows=10 loops=1)
     161        ->  Sort  (cost=193729.83..195206.63 rows=590722 width=16) (actual time=1953.266..1953.270 rows=10 loops=1)
     162              Sort Key: (count(ss.song_id)) DESC
     163              Sort Method: top-N heapsort  Memory: 25kB
     164              ->  HashAggregate  (cost=165561.66..180964.54 rows=590722 width=16) (actual time=1326.637..1865.932 rows=593461 loops=1)
     165                    Group Key: ss.user_id
     166                    Planned Partitions: 8  Batches: 9  Memory Usage: 8273kB  Disk Usage: 31768kB
     167                    ->  Bitmap Heap Scan on song_streams ss  (cost=24927.09..103270.10 rows=972356 width=16) (actual time=109.482..553.897 rows=970108 loops=1)
     168                          Recheck Cond: ((streamed_at >= (CURRENT_DATE - 30)) AND (streamed_at <= now()))
     169                          Heap Blocks: exact=56465
     170                          ->  Bitmap Index Scan on idx_song_streams_streamed_at_song_id  (cost=0.00..24684.00 rows=972356 width=0) (actual time=95.352..95.353 rows=970108 loops=1)
     171                                Index Cond: ((streamed_at >= (CURRENT_DATE - 30)) AND (streamed_at <= now()))
     172        SubPlan 1
     173          ->  Index Scan using users_pkey on users  (cost=0.42..8.44 rows=1 width=17) (actual time=0.021..0.022 rows=1 loops=10)
     174                Index Cond: (id = ss.user_id)
     175Planning Time: 0.476 ms
     176JIT:
     177  Functions: 24
     178"  Options: Inlining false, Optimization false, Expressions true, Deforming true"
     179"  Timing: Generation 2.144 ms (Deform 0.808 ms), Inlining 0.000 ms, Optimization 1.505 ms, Emission 19.128 ms, Total 22.777 ms"
     180Execution Time: 1976.733 ms
     181}}}
     182
     183Бидејќи `song_streams` нема индекс на `user_id`, за прашалник 2А потребно е секвенцијално скенирање за да се најдат стримовите за еден корисник. Затоа, додаваме индекс на таа колона:
     184
     185{{{
     186CREATE INDEX idx_song_streams_user_id ON song_streams(user_id);
     187}}}
     188
     189==== Време за извршување по додавање на индекс
     190
     191**2A — 0.453 ms** (was 389.404 ms)
     192
     193{{{
     194 Nested Loop  (cost=0.86..45.14 rows=1 width=33) (actual time=0.295..0.297 rows=1 loops=1)
     195   ->  Index Scan using users_pkey on users u  (cost=0.42..8.44 rows=1 width=25) (actual time=0.071..0.072 rows=1 loops=1)
     196         Index Cond: (id = 100376)
     197   ->  GroupAggregate  (cost=0.43..36.68 rows=1 width=16) (actual time=0.220..0.221 rows=1 loops=1)
     198         ->  Index Scan using idx_song_streams_user_id on song_streams ss  (cost=0.43..36.67 rows=1 width=16) (actual time=0.089..0.214 rows=2 loops=1)
     199               Index Cond: (user_id = 100376)
     200               Filter: ((streamed_at <= now()) AND (streamed_at >= (CURRENT_DATE - 30)))
     201               Rows Removed by Filter: 8
     202 Planning Time: 1.843 ms
     203 Execution Time: 0.453 ms
     204}}}
     205
     2062Б остана непроменето - бидејќи прашалникот треба да направи комплексна агрегација на големи табели нема баш некој конкретен индекс што може да ги подобри перформансите. Доколку овој прашалник се извршува често во апликацијата, јасно е дека тоа може да доведе до проблеми. Ова можеме да го решиме на повеќе начини: со менување на погледот во материјализиран поглед, со кеширање и слично. Првиот пристап (материјализирани погледи) како решение ќе го погледнеме понатаму во оптимизацијата на други погледи, а конкретно за овој поглед ќе одиме со вториот пристап, поточно со кеширање кое ќе биде имплементирано во самиот апликациски код.
     207
     208
     209
     210=== 3. Анализа на поглед 3, рангирање на песни по нивните просечни оценки и бројот на вкупни оценки, соодветно ===
     211
     212Прашалниците кои ќе ги тестираме се следните:
     213
     214{{{
     215-- 3A: просечна оценка за една песна
     216SELECT * FROM song_average_grade WHERE song_id = 1;
     217
     218-- 3B: топ 10 најдобро оценети песни
     219SELECT * FROM song_average_grade ORDER BY avg_grade DESC, num_reviews DESC LIMIT 10;
     220}}}
     221
     222==== Време за извршување без индекси
     223
     224**3A — 705.179 ms**
     225
     226{{{
     227 Nested Loop  (cost=1000.85..136433.74 rows=1 width=86) (actual time=645.720..658.140 rows=1 loops=1)
     228   ->  Nested Loop  (cost=1000.43..136425.30 rows=1 width=69) (actual time=645.601..658.019 rows=1 loops=1)
     229         ->  Index Scan using songs_pkey on songs s  (cost=0.43..8.45 rows=1 width=29) (actual time=0.191..0.196 rows=1 loops=1)
     230               Index Cond: (id = 1)
     231         ->  Finalize GroupAggregate  (cost=1000.00..136416.84 rows=1 width=48) (actual time=645.391..657.803 rows=1 loops=1)
     232               ->  Gather  (cost=1000.00..136416.81 rows=2 width=48) (actual time=645.091..657.742 rows=3 loops=1)
     233                     Workers Planned: 2
     234                     Workers Launched: 2
     235                     ->  Partial GroupAggregate  (cost=0.00..135416.61 rows=1 width=48) (actual time=585.860..585.861 rows=1 loops=3)
     236                           ->  Parallel Seq Scan on reviews r  (cost=0.00..135416.59 rows=2 width=12) (actual time=313.794..585.721 rows=2 loops=3)
     237                                 Filter: (song_id = 1)
     238                                 Rows Removed by Filter: 3333331
     239   ->  Index Scan using users_pkey on users u  (cost=0.42..8.44 rows=1 width=25) (actual time=0.077..0.078 rows=1 loops=1)
     240         Index Cond: (id = s.owner_artist_id)
     241 Planning Time: 3.643 ms
     242 Execution Time: 705.179 ms
     243}}}
     244
     245**3B — 20559.318 ms**
     246
     247{{{
     248 Limit  (cost=1696797.46..1696797.48 rows=10 width=86) (actual time=20499.331..20499.476 rows=10 loops=1)
     249   ->  Sort  (cost=1696797.46..1701259.58 rows=1784848 width=86) (actual time=20067.449..20067.593 rows=10 loops=1)
     250         Sort Key: ag.avg_grade DESC, ag.num_reviews DESC
     251         Sort Method: top-N heapsort  Memory: 27kB
     252         ->  Hash Join  (cost=1016994.20..1658227.53 rows=1784848 width=86) (actual time=5289.583..19373.444 rows=1939589 loops=1)
     253               Hash Cond: (s.owner_artist_id = u.id)
     254               ->  Hash Join  (cost=962631.20..1550509.28 rows=1784848 width=69) (actual time=4610.737..16796.605 rows=1939589 loops=1)
     255                     Hash Cond: (ag.song_id = s.id)
     256                     ->  Subquery Scan on ag  (cost=868957.86..1407435.71 rows=1784848 width=48) (actual time=3543.588..8643.418 rows=1939589 loops=1)
     257                           ->  Finalize GroupAggregate  (cost=868957.86..1389587.23 rows=1784848 width=48) (actual time=3543.580..8291.260 rows=1939589 loops=1)
     258                                 Group Key: r.song_id
     259                                 ->  Gather Merge  (cost=868957.86..1340503.91 rows=3569696 width=48) (actual time=3543.530..6378.424 rows=4760010 loops=1)
     260                                       Workers Planned: 2
     261                                       Workers Launched: 2
     262                                       ->  Partial GroupAggregate  (cost=867957.83..927472.39 rows=1784848 width=48) (actual time=3194.440..4716.258 rows=1586670 loops=3)
     263                                             Group Key: r.song_id
     264                                             ->  Sort  (cost=867957.83..878374.35 rows=4166608 width=12) (actual time=3194.358..3822.645 rows=3333333 loops=3)
     265                                                   Sort Key: r.song_id
     266                                                   Sort Method: external merge  Disk: 86264kB
     267                                                   ->  Parallel Seq Scan on reviews r  (cost=0.00..125000.08 rows=4166608 width=12) (actual time=193.403..737.774 rows=3333333 loops=3)
     268                     ->  Hash  (cost=55943.04..55943.04 rows=1951304 width=29) (actual time=1045.998..1045.999 rows=1951232 loops=1)
     269                           ->  Seq Scan on songs s  (cost=0.00..55943.04 rows=1951304 width=29) (actual time=91.198..412.261 rows=1951232 loops=1)
     270               ->  Hash  (cost=35027.00..35027.00 rows=1000000 width=25) (actual time=574.936..574.937 rows=1000000 loops=1)
     271                     ->  Seq Scan on users u  (cost=0.00..35027.00 rows=1000000 width=25) (actual time=63.431..244.709 rows=1000000 loops=1)
     272 Planning Time: 2.253 ms
     273 Execution Time: 20559.318 ms
     274}}}
     275
     276
     277Во 3А имаме секвенцијално скенирање на `reviews` табелата за да се земат `(song_id, grade)`. Прво пробавме да додадеме индекс на `reviews(song_id)`, но планерот го игнорираше индексот бидејќи секако ќе беше потребно скенирање на табелата за да се земе `grade` колоната. Затоа можеме да воведеме сложен индекс кој ќе ги содржи сите потребни колони и ќе му овозможи на планерот да користи Index Only Scan.
     278
     279{{{
     280CREATE INDEX idx_reviews_song_id_grade ON reviews(song_id, grade);
     281}}}
     282
     283**3A — 0.630 ms** (was 705.179 ms)
     284
     285{{{
     286 Nested Loop  (cost=1.29..41.49 rows=1 width=86) (actual time=0.260..0.263 rows=1 loops=1)
     287   ->  Nested Loop  (cost=0.86..33.05 rows=1 width=69) (actual time=0.215..0.218 rows=1 loops=1)
     288         ->  Index Scan using songs_pkey on songs s  (cost=0.43..8.45 rows=1 width=29) (actual time=0.052..0.053 rows=1 loops=1)
     289               Index Cond: (id = 1)
     290         ->  GroupAggregate  (cost=0.43..24.58 rows=1 width=48) (actual time=0.159..0.159 rows=1 loops=1)
     291               ->  Index Only Scan using idx_reviews_song_id_grade on reviews r  (cost=0.43..24.54 rows=6 width=12) (actual time=0.103..0.145 rows=6 loops=1)
     292                     Index Cond: (song_id = 1)
     293                     Heap Fetches: 5
     294   ->  Index Scan using users_pkey on users u  (cost=0.42..8.44 rows=1 width=25) (actual time=0.041..0.042 rows=1 loops=1)
     295         Index Cond: (id = 1)
     296 Planning Time: 3.016 ms
     297 Execution Time: 0.630 ms
     298}}}
     299
     300Перформансите на 3Б малку се подобрија (од ~20 секунди на ~14 секунди), но јасно е дека тоа е многу бавно.
     301
     302Поради таа причина обичниот поглед во овој случај ќе го замениме со материјализиран поглед.
     303
     304==== Време за извршување на прашалници по додавање на материјализиран поглед
     305
     306**3A - 0.19 ms**
     307
     308{{{
     309 Index Scan using idx_sag_mv_song_id on song_average_grade_mv  (cost=0.43..8.45 rows=1 width=62) (actual time=0.074..0.075 rows=1 loops=1)
     310   Index Cond: (song_id = 1)
     311 Planning Time: 1.116 ms
     312 Execution Time: 0.189 ms
     313}}}
     314
     315**3B - 0.25 ms**
     316
     317{{{
     318 Limit  (cost=0.43..1.23 rows=10 width=718) (actual time=0.081..0.209 rows=10 loops=1)
     319   ->  Index Scan using idx_sag_mv_avg_grade on song_average_grade_mv  (cost=0.43..155226.26 rows=1939589 width=718) (actual time=0.080..0.205 rows=10 loops=1)
     320 Planning Time: 0.936 ms
     321 Execution Time: 0.247 ms
     322}}}
     323
     324Со материјализирани погледи добиваме <1ms за читање, со тоа што свесно дозволуваме во одредени моменти на корисниците да им се прикажуваат податоци кои може да не се најновите податоци како што беше случајот кај обичните погледи.
     325
     326Исто така вреди да се напомене дека во апликацискиот код ќе треба да имплементираме логика за повремено ажурирање на овие погледи, користејќи `REFRESH MATERIALIZED VIEW`, и дека еден ваков refresh трае ~45 секунди.
     327
     328=== 4. Анализа на поглед 4, број на слушања (популарност) на артисте за изминатите 30 дена
     329
     330Прашалник кој ќе го тестираме:
     331
     332{{{
     333SELECT * FROM artist_popularity_last_30_days WHERE artist_display_name='Rush';
     334}}}
     335
     336==== Време на извршување без индекси:
     337
     338**5295.544 ms**
     339
     340{{{
     341 Subquery Scan on artist_popularity_last_30_days  (cost=258658.87..261658.85 rows=500 width=60) (actual time=5248.277..5288.441 rows=41 loops=1)
     342   Filter: ((artist_popularity_last_30_days.artist_display_name)::text = 'Rush'::text)
     343   Rows Removed by Filter: 99959
     344   ->  WindowAgg  (cost=258658.87..260408.85 rows=100000 width=60) (actual time=5247.481..5281.467 rows=100000 loops=1)
     345         ->  Sort  (cost=258658.85..258908.85 rows=100000 width=52) (actual time=5247.444..5253.777 rows=100000 loops=1)
     346               Sort Key: artist_listens.total_listens DESC
     347               Sort Method: quicksort  Memory: 7706kB
     348               ->  Subquery Scan on artist_listens  (cost=249104.03..250354.03 rows=100000 width=52) (actual time=5163.853..5210.492 rows=100000 loops=1)
     349                     ->  HashAggregate  (cost=249104.03..250354.03 rows=100000 width=52) (actual time=5163.847..5200.274 rows=100000 loops=1)
     350                           Group Key: a.id
     351                           Batches: 1  Memory Usage: 22545kB
     352                           ->  Hash Left Join  (cost=173161.35..239347.87 rows=1951232 width=28) (actual time=3172.252..4602.821 rows=1953805 loops=1)
     353                                 Hash Cond: (s.id = sc.song_id)
     354                                 ->  Hash Right Join  (cost=3618.00..64682.53 rows=1951232 width=28) (actual time=123.141..1016.733 rows=1953805 loops=1)
     355                                       Hash Cond: (s.owner_artist_id = a.id)
     356                                       ->  Seq Scan on songs s  (cost=0.00..55942.32 rows=1951232 width=16) (actual time=70.738..304.609 rows=1951232 loops=1)
     357                                       ->  Hash  (cost=2368.00..2368.00 rows=100000 width=20) (actual time=52.166..52.167 rows=100000 loops=1)
     358                                             Buckets: 131072  Batches: 1  Memory Usage: 6157kB
     359                                             ->  Seq Scan on artists a  (cost=0.00..2368.00 rows=100000 width=20) (actual time=21.265..34.097 rows=100000 loops=1)
     360                                 ->  Hash  (cost=167349.12..167349.12 rows=175538 width=16) (actual time=3048.716..3048.831 rows=304092 loops=1)
     361                                       Buckets: 524288 (originally 262144)  Batches: 1 (originally 1)  Memory Usage: 18351kB
     362                                       ->  Subquery Scan on sc  (cost=163838.36..167349.12 rows=175538 width=16) (actual time=2861.695..2967.031 rows=304092 loops=1)
     363                                             ->  Finalize HashAggregate  (cost=163838.36..165593.74 rows=175538 width=16) (actual time=2861.688..2940.091 rows=304092 loops=1)
     364                                                   Group Key: ss.song_id
     365                                                   Batches: 1  Memory Usage: 36881kB
     366                                                   ->  Gather  (cost=88357.02..160327.60 rows=702152 width=16) (actual time=2650.692..2751.352 rows=304092 loops=1)
     367                                                         Workers Planned: 4
     368                                                         Workers Launched: 0
     369                                                         ->  Partial HashAggregate  (cost=87357.02..89112.40 rows=175538 width=16) (actual time=2650.252..2729.313 rows=304092 loops=1)
     370                                                               Group Key: ss.song_id
     371                                                               Batches: 1  Memory Usage: 36881kB
     372                                                               ->  Parallel Seq Scan on song_streams ss  (cost=0.00..86108.61 rows=249682 width=8) (actual time=0.039..2307.530 rows=996439 loops=1)
     373                                                                     Filter: (streamed_at >= (CURRENT_TIMESTAMP - '30 days'::interval))
     374                                                                     Rows Removed by Filter: 5779244
     375 Planning Time: 0.601 ms
     376 JIT:
     377   Functions: 38
     378 "  Options: Inlining false, Optimization false, Expressions true, Deforming true"
     379 "  Timing: Generation 2.191 ms (Deform 0.875 ms), Inlining 0.000 ms, Optimization 0.746 ms, Emission 20.211 ms, Total 23.149 ms"
     380 Execution Time: 5295.544 ms
     381}}}
     382
     383Најбавните делови се секвенцијално скенирање на табелите {{{song_streams}}} и {{{songs}}}, што можеме да го оптимизираме со индекс:
     384
     385{{{
     386CREATE INDEX idx_song_streams_streamed_at_song_id ON song_streams(streamed_at, song_id);
     387CREATE INDEX idx_songs_owner_artist_id ON songs(owner_artist_id);
     388}}}
     389
     390==== Време на извршување со индекси:
     391
     392**2923.180 ms**
     393
     394{{{
     395WindowAgg  (cost=132657.36..134407.34 rows=100000 width=60) (actual time=2878.456..2912.811 rows=100000 loops=1)
     396  ->  Sort  (cost=132657.34..132907.34 rows=100000 width=52) (actual time=2878.421..2884.685 rows=100000 loops=1)
     397        Sort Key: artist_listens.total_listens DESC
     398        Sort Method: quicksort  Memory: 7706kB
     399        ->  Subquery Scan on artist_listens  (cost=123102.52..124352.52 rows=100000 width=52) (actual time=2795.069..2840.462 rows=100000 loops=1)
     400              ->  HashAggregate  (cost=123102.52..124352.52 rows=100000 width=52) (actual time=2795.063..2830.225 rows=100000 loops=1)
     401                    Group Key: a.id
     402                    Batches: 1  Memory Usage: 22545kB
     403                    ->  Hash Left Join  (cost=47159.84..113346.36 rows=1951232 width=28) (actual time=826.776..2239.576 rows=1953805 loops=1)
     404                          Hash Cond: (s.id = sc.song_id)
     405                          ->  Hash Right Join  (cost=3618.00..64682.53 rows=1951232 width=28) (actual time=118.783..1004.286 rows=1953805 loops=1)
     406                                Hash Cond: (s.owner_artist_id = a.id)
     407                                ->  Seq Scan on songs s  (cost=0.00..55942.32 rows=1951232 width=16) (actual time=69.772..303.956 rows=1951232 loops=1)
     408                                ->  Hash  (cost=2368.00..2368.00 rows=100000 width=20) (actual time=48.780..48.781 rows=100000 loops=1)
     409                                      Buckets: 131072  Batches: 1  Memory Usage: 6157kB
     410                                      ->  Seq Scan on artists a  (cost=0.00..2368.00 rows=100000 width=20) (actual time=17.634..30.840 rows=100000 loops=1)
     411                          ->  Hash  (cost=41347.61..41347.61 rows=175538 width=16) (actual time=707.572..707.574 rows=304102 loops=1)
     412                                Buckets: 524288 (originally 262144)  Batches: 1 (originally 1)  Memory Usage: 18351kB
     413                                ->  Subquery Scan on sc  (cost=37836.85..41347.61 rows=175538 width=16) (actual time=533.395..631.097 rows=304102 loops=1)
     414                                      ->  HashAggregate  (cost=37836.85..39592.23 rows=175538 width=16) (actual time=533.387..597.876 rows=304102 loops=1)
     415                                            Group Key: ss.song_id
     416                                            Batches: 1  Memory Usage: 36881kB
     417                                            ->  Index Only Scan using idx_song_streams_streamed_at_song_id on song_streams ss  (cost=0.44..32842.98 rows=998774 width=8) (actual time=0.057..245.680 rows=996484 loops=1)
     418                                                  Index Cond: (streamed_at >= (CURRENT_TIMESTAMP - '30 days'::interval))
     419                                                  Heap Fetches: 0
     420Planning Time: 0.573 ms
     421JIT:
     422  Functions: 32
     423"  Options: Inlining false, Optimization false, Expressions true, Deforming true"
     424"  Timing: Generation 2.147 ms (Deform 0.709 ms), Inlining 0.000 ms, Optimization 0.652 ms, Emission 16.640 ms, Total 19.439 ms"
     425Execution Time: 2923.180 ms
     426}}}
     427
     428Сега планерот го користи креираниот индекс за табелата {{{song_streams}}}, но сепак табелата {{{songs}}} треба секвенцијално да се скенира за да се пресмета статистиката за артистите. Дополнителна оптимизација правиме со материјализиран поглед:
     429
     430{{{
     431CREATE MATERIALIZED VIEW artist_popularity_last_30_days_mv AS
     432WITH streams_count AS (
     433    SELECT ss.song_id, COUNT(*) AS cnt
     434    FROM song_streams ss
     435    WHERE ss.streamed_at >= CURRENT_TIMESTAMP - INTERVAL '30 days'
     436    GROUP BY ss.song_id
     437),
     438artist_listens AS (
     439    SELECT
     440        a.id AS artist_id,
     441        a.display_name AS artist_display_name,
     442        COALESCE(SUM(sc.cnt), 0) AS total_listens
     443    FROM artists a
     444    LEFT JOIN songs s ON s.owner_artist_id = a.id
     445    LEFT JOIN streams_count sc ON sc.song_id = s.id
     446    GROUP BY a.id, a.display_name
     447)
     448SELECT
     449    ROW_NUMBER() OVER (ORDER BY total_listens DESC) AS rank,
     450    artist_id,
     451    artist_display_name,
     452    total_listens
     453FROM artist_listens;
     454
     455}}}
     456
     457==== Време за извршување на прашалникот по додавање на материјализиран поглед
     458
     459{{{
     460Seq Scan on artist_popularity_last_30_days_mv  (cost=0.00..2082.00 rows=2 width=31) (actual time=0.210..9.704 rows=41 loops=1)
     461  Filter: ((artist_display_name)::text = 'Rush'::text)
     462  Rows Removed by Filter: 99959
     463Planning Time: 0.094 ms
     464Execution Time: 9.731 ms
     465}}}
     466
     467Со материјализиран поглед добиваме <10 ms за читање.
     468
     469=== 5. Анализа на поглед 5, број на слушања (популарност) на песните за изминатите 30 дена
     470
     471Прашалник кој ќе го тестираме:
     472
     473{{{
     474SELECT * FROM most_popular_songs_last_30_days WHERE rank=15;
     475}}}
     476
     477==== Време за извршување со креираниот индекс {{{idx_song_streams_streamed_at_song_id}}}
     478
     479**1682.017 ms**
     480
     481{{{
     482Subquery Scan on most_popular_songs_last_30_days  (cost=96713.63..122558.33 rows=878 width=96) (actual time=1471.660..1677.911 rows=1 loops=1)
     483  Filter: (most_popular_songs_last_30_days.rank = 17)
     484  Rows Removed by Filter: 16
     485  ->  WindowAgg  (cost=96713.63..120364.14 rows=175535 width=96) (actual time=1471.640..1677.902 rows=17 loops=1)
     486        Run Condition: (row_number() OVER (?) <= 17)
     487        ->  Gather Merge  (cost=96713.50..117731.12 rows=175535 width=88) (actual time=1471.591..1677.843 rows=18 loops=1)
     488              Workers Planned: 4
     489              Workers Launched: 4
     490              ->  Sort  (cost=95713.44..95823.15 rows=43884 width=88) (actual time=1433.450..1433.565 rows=564 loops=5)
     491                    Sort Key: sc.total_streams DESC
     492                    Sort Method: quicksort  Memory: 9470kB
     493                    Worker 0:  Sort Method: quicksort  Memory: 9145kB
     494                    Worker 1:  Sort Method: quicksort  Memory: 2511kB
     495                    Worker 2:  Sort Method: quicksort  Memory: 2431kB
     496                    Worker 3:  Sort Method: quicksort  Memory: 9520kB
     497                    ->  Parallel Hash Join  (cost=49328.85..92329.67 rows=43884 width=88) (actual time=1114.045..1407.397 rows=60789 loops=5)
     498                          Hash Cond: (s.owner_artist_id = a.id)
     499                          ->  Hash Left Join  (cost=46637.31..89522.94 rows=43884 width=84) (actual time=1076.395..1343.191 rows=60789 loops=5)
     500                                Hash Cond: (s.published_by_label_id = l.id)
     501                                ->  Hash Join  (cost=43501.50..86090.08 rows=43884 width=48) (actual time=1071.308..1327.862 rows=60789 loops=5)
     502                                      Hash Cond: (s.id = sc.song_id)
     503                                      ->  Parallel Seq Scan on songs s  (cost=0.00..41308.08 rows=487808 width=40) (actual time=0.092..135.322 rows=390246 loops=5)
     504                                      ->  Hash  (cost=41307.31..41307.31 rows=175535 width=16) (actual time=1069.687..1069.689 rows=303945 loops=5)
     505                                            Buckets: 524288 (originally 262144)  Batches: 1 (originally 1)  Memory Usage: 18344kB
     506                                            ->  Subquery Scan on sc  (cost=37796.61..41307.31 rows=175535 width=16) (actual time=837.380..966.924 rows=303945 loops=5)
     507                                                  ->  HashAggregate  (cost=37796.61..39551.96 rows=175535 width=16) (actual time=837.372..937.795 rows=303945 loops=5)
     508                                                        Group Key: song_streams.song_id
     509                                                        Batches: 1  Memory Usage: 36881kB
     510                                                        Worker 0:  Batches: 1  Memory Usage: 36881kB
     511                                                        Worker 1:  Batches: 1  Memory Usage: 36881kB
     512                                                        Worker 2:  Batches: 1  Memory Usage: 36881kB
     513                                                        Worker 3:  Batches: 1  Memory Usage: 36881kB
     514                                                        ->  Index Only Scan using idx_song_streams_streamed_at_song_id on song_streams  (cost=0.44..32809.02 rows=997519 width=8) (actual time=0.137..407.430 rows=995226 loops=5)
     515                                                              Index Cond: (streamed_at >= (CURRENT_TIMESTAMP - '30 days'::interval))
     516                                                              Heap Fetches: 95
     517                                ->  Hash  (cost=3131.12..3131.12 rows=375 width=48) (actual time=4.957..4.961 rows=375 loops=5)
     518                                      Buckets: 1024  Batches: 1  Memory Usage: 38kB
     519                                      ->  Nested Loop Left Join  (cost=13.86..3131.12 rows=375 width=48) (actual time=0.389..4.675 rows=375 loops=5)
     520                                            ->  Hash Left Join  (cost=13.44..21.18 rows=375 width=39) (actual time=0.317..0.558 rows=375 loops=5)
     521                                                  Hash Cond: (la.label_id = l.id)
     522                                                  ->  Seq Scan on label_admins la  (cost=0.00..6.75 rows=375 width=24) (actual time=0.068..0.157 rows=375 loops=5)
     523                                                  ->  Hash  (cost=8.75..8.75 rows=375 width=31) (actual time=0.214..0.215 rows=375 loops=5)
     524                                                        Buckets: 1024  Batches: 1  Memory Usage: 33kB
     525                                                        ->  Seq Scan on labels l  (cost=0.00..8.75 rows=375 width=31) (actual time=0.051..0.109 rows=375 loops=5)
     526                                            ->  Index Scan using users_pkey on users u  (cost=0.42..8.29 rows=1 width=25) (actual time=0.010..0.010 rows=1 loops=1875)
     527                                                  Index Cond: (id = la.user_id)
     528                          ->  Parallel Hash  (cost=1956.24..1956.24 rows=58824 width=20) (actual time=37.138..37.139 rows=20000 loops=5)
     529                                Buckets: 131072  Batches: 1  Memory Usage: 6528kB
     530                                ->  Parallel Seq Scan on artists a  (cost=0.00..1956.24 rows=58824 width=20) (actual time=27.866..30.351 rows=20000 loops=5)
     531Planning Time: 1.492 ms
     532JIT:
     533  Functions: 235
     534"  Options: Inlining false, Optimization false, Expressions true, Deforming true"
     535"  Timing: Generation 14.075 ms (Deform 6.062 ms), Inlining 0.000 ms, Optimization 5.950 ms, Emission 133.686 ms, Total 153.711 ms"
     536Execution Time: 1682.017 ms
     537
     538}}}
     539
     540За дополнителна оптимизација креираме материјализиран поглед:
     541
     542{{{
     543CREATE MATERIALIZED VIEW most_popular_songs_last_30_days_mv AS
     544WITH stream_counts AS (
     545    SELECT
     546        song_id,
     547        COUNT(*) AS total_streams
     548    FROM song_streams
     549    WHERE streamed_at >= CURRENT_TIMESTAMP - INTERVAL '30 days'
     550    GROUP BY song_id
     551)
     552SELECT
     553    ROW_NUMBER() OVER (ORDER BY sc.total_streams DESC) AS rank,
     554    s.id AS song_id,
     555    s.title AS song_title,
     556    a.display_name AS artist_display_name,
     557    s.visibility AS song_visibility,
     558    l.name AS label_name,
     559    sc.total_streams
     560FROM stream_counts sc
     561JOIN songs s ON s.id = sc.song_id
     562JOIN artists a ON s.owner_artist_id = a.id
     563LEFT JOIN labels l ON l.id = s.published_by_label_id;
     564
     565}}}
     566
     567==== Време за извршување на прашалникот по додавање на материјализиран поглед
     568
     569{{{
     570Gather  (cost=1000.00..6167.16 rows=1 width=96) (actual time=0.381..671.251 rows=1 loops=1)
     571  Workers Planned: 2
     572  Workers Launched: 2
     573  ->  Parallel Seq Scan on most_popular_songs_last_30_days_mv  (cost=0.00..5167.06 rows=1 width=96) (actual time=417.275..638.156 rows=0 loops=3)
     574        Filter: (rank = 17)
     575        Rows Removed by Filter: 101315
     576Planning Time: 0.218 ms
     577Execution Time: 671.277 ms
     578}}}
     579
     580Со материјализиран поглед добиваме <1s време на читање.
     581
     582
     583=== 6. Анализа на поглед 6, детален преглед за артистите групирани по издавачка куќа на која припаѓаат
     584
     585Го тестираме прашалникот:
     586
     587{{{ SELECT * FROM label_artists_info WHERE label_name='Piercing Abyss Records';}}}
     588
     589
     590==== Време за извршување без индекси
     591
     592**3197.076 ms**
     593
     594{{{
     595Subquery Scan on label_artists_info  (cost=48955.19..49185.03 rows=1561 width=51) (actual time=3166.576..3196.926 rows=81 loops=1)
     596  ->  GroupAggregate  (cost=48955.19..49169.42 rows=1561 width=59) (actual time=3166.575..3196.914 rows=81 loops=1)
     597        Group Key: a.id
     598        ->  Gather Merge  (cost=48955.19..49142.10 rows=1561 width=59) (actual time=3166.542..3196.531 rows=1660 loops=1)
     599              Workers Planned: 4
     600              Workers Launched: 4
     601              ->  Sort  (cost=47955.14..47956.11 rows=390 width=59) (actual time=2548.301..2548.327 rows=332 loops=5)
     602"                    Sort Key: a.id, s.id"
     603                    Sort Method: quicksort  Memory: 68kB
     604                    Worker 0:  Sort Method: quicksort  Memory: 35kB
     605                    Worker 1:  Sort Method: quicksort  Memory: 39kB
     606                    Worker 2:  Sort Method: quicksort  Memory: 44kB
     607                    Worker 3:  Sort Method: quicksort  Memory: 60kB
     608                    ->  Nested Loop Left Join  (cost=3303.17..47938.35 rows=390 width=59) (actual time=254.314..2547.203 rows=332 loops=5)
     609                          ->  Hash Join  (cost=3302.88..47724.69 rows=390 width=59) (actual time=202.108..1249.756 rows=328 loops=5)
     610                                Hash Cond: (a.id = al.artist_id)
     611                                ->  Parallel Hash Right Join  (cost=2691.54..45280.17 rows=487808 width=36) (actual time=42.618..1060.686 rows=390761 loops=5)
     612                                      Hash Cond: (s.owner_artist_id = a.id)
     613                                      ->  Parallel Seq Scan on songs s  (cost=0.00..41308.08 rows=487808 width=16) (actual time=0.371..871.469 rows=390246 loops=5)
     614                                      ->  Parallel Hash  (cost=1956.24..1956.24 rows=58824 width=28) (actual time=41.753..41.754 rows=20000 loops=5)
     615                                            Buckets: 131072  Batches: 1  Memory Usage: 7328kB
     616                                            ->  Parallel Seq Scan on artists a  (cost=0.00..1956.24 rows=58824 width=28) (actual time=3.800..28.812 rows=20000 loops=5)
     617                                ->  Hash  (cost=610.34..610.34 rows=80 width=31) (actual time=152.360..152.362 rows=81 loops=5)
     618                                      Buckets: 1024  Batches: 1  Memory Usage: 14kB
     619                                      ->  Hash Join  (cost=9.70..610.34 rows=80 width=31) (actual time=51.681..152.281 rows=81 loops=5)
     620                                            Hash Cond: (al.label_id = l.id)
     621                                            ->  Seq Scan on artist_labels al  (cost=0.00..521.00 rows=30000 width=16) (actual time=11.763..115.238 rows=30000 loops=5)
     622                                            ->  Hash  (cost=9.69..9.69 rows=1 width=31) (actual time=33.047..33.048 rows=1 loops=5)
     623                                                  Buckets: 1024  Batches: 1  Memory Usage: 9kB
     624                                                  ->  Seq Scan on labels l  (cost=0.00..9.69 rows=1 width=31) (actual time=32.998..33.038 rows=1 loops=5)
     625                                                        Filter: ((name)::text = 'Piercing Abyss Records'::text)
     626                                                        Rows Removed by Filter: 374
     627                          ->  Index Scan using idx_follows_followed_user_id on follows f  (cost=0.29..0.47 rows=8 width=16) (actual time=3.764..3.959 rows=1 loops=1638)
     628                                Index Cond: (followed_user_id = a.user_id)
     629Planning Time: 1776.191 ms
     630Execution Time: 3197.076 ms
     631}}}
     632
     633За да го оптимизираме секвенцијалното скенирање на табелите {{{songs}}} и {{{artist_labels}}}, ги креираме индексите:
     634
     635{{{
     636CREATE INDEX idx_songs_owner_artist_id ON songs(owner_artist_id);
     637CREATE INDEX idx_artist_labels_label_id_artist_id ON artist_labels(label_id, artist_id);
     638}}}
     639
     640==== Време за извршување со индекси
     641
     642**5.213 ms**
     643
     644{{{
     645Subquery Scan on label_artists_info  (cost=571.17..618.00 rows=1561 width=51) (actual time=4.634..5.127 rows=81 loops=1)
     646  ->  GroupAggregate  (cost=571.17..602.39 rows=1561 width=59) (actual time=4.633..5.116 rows=81 loops=1)
     647        Group Key: a.id
     648        ->  Sort  (cost=571.17..575.07 rows=1561 width=59) (actual time=4.614..4.708 rows=1660 loops=1)
     649"              Sort Key: a.id, s.id"
     650              Sort Method: quicksort  Memory: 171kB
     651              ->  Nested Loop Left Join  (cost=6.04..488.37 rows=1561 width=59) (actual time=0.125..3.388 rows=1660 loops=1)
     652                    ->  Nested Loop Left Join  (cost=5.62..274.33 rows=80 width=51) (actual time=0.115..0.973 rows=83 loops=1)
     653                          ->  Nested Loop  (cost=5.32..230.51 rows=80 width=51) (actual time=0.105..0.685 rows=81 loops=1)
     654                                ->  Nested Loop  (cost=4.91..175.24 rows=80 width=31) (actual time=0.092..0.265 rows=81 loops=1)
     655                                      ->  Seq Scan on labels l  (cost=0.00..9.69 rows=1 width=31) (actual time=0.051..0.075 rows=1 loops=1)
     656                                            Filter: ((name)::text = 'Piercing Abyss Records'::text)
     657                                            Rows Removed by Filter: 374
     658                                      ->  Bitmap Heap Scan on artist_labels al  (cost=4.91..164.75 rows=80 width=16) (actual time=0.038..0.174 rows=81 loops=1)
     659                                            Recheck Cond: (l.id = label_id)
     660                                            Heap Blocks: exact=72
     661                                            ->  Bitmap Index Scan on idx_artist_labels_label_id_artist_id  (cost=0.00..4.89 rows=80 width=0) (actual time=0.019..0.019 rows=81 loops=1)
     662                                                  Index Cond: (label_id = l.id)
     663                                ->  Index Scan using artists_pkey on artists a  (cost=0.42..0.69 rows=1 width=28) (actual time=0.005..0.005 rows=1 loops=81)
     664                                      Index Cond: (id = al.artist_id)
     665                          ->  Index Scan using idx_follows_followed_user_id on follows f  (cost=0.29..0.47 rows=8 width=16) (actual time=0.003..0.003 rows=1 loops=81)
     666                                Index Cond: (followed_user_id = a.user_id)
     667                    ->  Index Scan using idx_songs_owner_artist_id on songs s  (cost=0.43..2.42 rows=26 width=16) (actual time=0.004..0.026 rows=20 loops=83)
     668                          Index Cond: (owner_artist_id = a.id)
     669Planning Time: 1.229 ms
     670Execution Time: 5.213 ms
     671}}}
     672
     673Планерот го користи и претходно креираниот индекс {{{idx_follows_followed_user_id}}}. Индексите го забрзаа извршувањето за речиси 100%, па заклучуваме дека нема потреба од дополнителна оптимизација.
     674
     675
     676=== 7. Анализа на поглед 7, детални информации за секоја песна
     677
     678Прашалник кој го тестираме:
     679
     680{{{
     681SELECT * FROM songs_details WHERE title='Harmony';
     682}}}
     683
     684==== Време за извршување без индекси
     685
     686**93882.201 ms**
     687
     688{{{
     689Gather  (cost=230200.46..235882.48 rows=2211 width=121) (actual time=3427.813..93875.377 rows=1683 loops=1)
     690  Workers Planned: 3
     691  Workers Launched: 3
     692  ->  Nested Loop Left Join  (cost=229200.46..234661.38 rows=713 width=121) (actual time=3393.680..41801.988 rows=421 loops=4)
     693        ->  Merge Left Join  (cost=229200.03..229252.13 rows=713 width=85) (actual time=3393.619..3396.837 rows=421 loops=4)
     694              Merge Cond: (s.id = pc.song_id)
     695              ->  Sort  (cost=225567.13..225568.91 rows=713 width=77) (actual time=3342.494..3342.739 rows=421 loops=4)
     696                    Sort Key: s.id
     697                    Sort Method: quicksort  Memory: 48kB
     698                    Worker 0:  Sort Method: quicksort  Memory: 44kB
     699                    Worker 1:  Sort Method: quicksort  Memory: 44kB
     700                    Worker 2:  Sort Method: quicksort  Memory: 72kB
     701                    ->  Nested Loop Left Join  (cost=209283.95..225533.34 rows=713 width=77) (actual time=3254.123..3342.167 rows=421 loops=4)
     702                          ->  Parallel Hash Right Join  (cost=209283.53..225211.18 rows=713 width=72) (actual time=3254.061..3337.679 rows=421 loops=4)
     703                                Hash Cond: (at.song_id = s.id)
     704                                ->  Parallel Seq Scan on album_tracks at  (cost=0.00..14292.12 rows=435812 width=16) (actual time=0.016..39.432 rows=337754 loops=4)
     705                                ->  Parallel Hash  (cost=209276.62..209276.62 rows=553 width=64) (actual time=3253.381..3253.392 rows=421 loops=4)
     706                                      Buckets: 4096  Batches: 1  Memory Usage: 192kB
     707                                      ->  Hash Left Join  (cost=164727.43..209276.62 rows=553 width=64) (actual time=3174.557..3252.826 rows=421 loops=4)
     708                                            Hash Cond: (s.id = sc.song_id)
     709                                            ->  Hash Left Join  (cost=909.90..45457.64 rows=553 width=56) (actual time=52.511..130.411 rows=421 loops=4)
     710                                                  Hash Cond: (al.label_id = l.id)
     711                                                  ->  Hash Left Join  (cost=896.46..45442.74 rows=553 width=41) (actual time=52.297..129.994 rows=421 loops=4)
     712                                                        Hash Cond: (a.id = al.artist_id)
     713                                                        ->  Nested Loop Left Join  (cost=0.42..44542.96 rows=553 width=41) (actual time=41.010..118.277 rows=421 loops=4)
     714                                                              ->  Parallel Seq Scan on songs s  (cost=0.00..42527.60 rows=553 width=29) (actual time=40.894..112.485 rows=421 loops=4)
     715                                                                    Filter: ((title)::text = 'Harmony'::text)
     716                                                                    Rows Removed by Filter: 487388
     717                                                              ->  Index Scan using artists_pkey on artists a  (cost=0.42..3.64 rows=1 width=20) (actual time=0.012..0.012 rows=1 loops=1683)
     718                                                                    Index Cond: (id = s.owner_artist_id)
     719                                                        ->  Hash  (cost=521.02..521.02 rows=30002 width=16) (actual time=11.075..11.076 rows=30003 loops=4)
     720                                                              Buckets: 32768  Batches: 1  Memory Usage: 1663kB
     721                                                              ->  Seq Scan on artist_labels al  (cost=0.00..521.02 rows=30002 width=16) (actual time=0.053..4.694 rows=30003 loops=4)
     722                                                  ->  Hash  (cost=8.75..8.75 rows=375 width=31) (actual time=0.190..0.191 rows=375 loops=4)
     723                                                        Buckets: 1024  Batches: 1  Memory Usage: 33kB
     724                                                        ->  Seq Scan on labels l  (cost=0.00..8.75 rows=375 width=31) (actual time=0.030..0.089 rows=375 loops=4)
     725                                            ->  Hash  (cost=161618.58..161618.58 rows=175916 width=16) (actual time=3120.501..3120.503 rows=635893 loops=4)
     726                                                  Buckets: 1048576 (originally 262144)  Batches: 1 (originally 1)  Memory Usage: 38000kB
     727                                                  ->  Subquery Scan on sc  (cost=158100.26..161618.58 rows=175916 width=16) (actual time=2662.167..2922.708 rows=635893 loops=4)
     728                                                        ->  HashAggregate  (cost=158100.26..159859.42 rows=175916 width=16) (actual time=2662.160..2862.916 rows=635893 loops=4)
     729                                                              Group Key: song_streams.song_id
     730                                                              Batches: 1  Memory Usage: 65553kB
     731                                                              Worker 0:  Batches: 1  Memory Usage: 65553kB
     732                                                              Worker 1:  Batches: 1  Memory Usage: 65553kB
     733                                                              Worker 2:  Batches: 1  Memory Usage: 65553kB
     734                                                              ->  Seq Scan on song_streams  (cost=0.00..124221.84 rows=6775684 width=8) (actual time=0.040..834.432 rows=6775685 loops=4)
     735                          ->  Index Scan using albums_pkey on albums alb  (cost=0.42..0.45 rows=1 width=21) (actual time=0.010..0.010 rows=1 loops=1683)
     736                                Index Cond: (id = at.album_id)
     737              ->  Sort  (cost=3632.91..3657.15 rows=9698 width=16) (actual time=51.065..52.559 rows=10235 loops=4)
     738                    Sort Key: pc.song_id
     739                    Sort Method: quicksort  Memory: 706kB
     740                    Worker 0:  Sort Method: quicksort  Memory: 706kB
     741                    Worker 1:  Sort Method: quicksort  Memory: 706kB
     742                    Worker 2:  Sort Method: quicksort  Memory: 706kB
     743                    ->  Subquery Scan on pc  (cost=2796.77..2990.73 rows=9698 width=16) (actual time=45.054..47.748 rows=10288 loops=4)
     744                          ->  HashAggregate  (cost=2796.77..2893.75 rows=9698 width=16) (actual time=45.048..46.739 rows=10288 loops=4)
     745                                Group Key: playlist_tracks.song_id
     746                                Batches: 1  Memory Usage: 1425kB
     747                                Worker 0:  Batches: 1  Memory Usage: 1425kB
     748                                Worker 1:  Batches: 1  Memory Usage: 1425kB
     749                                Worker 2:  Batches: 1  Memory Usage: 1425kB
     750                                ->  Seq Scan on playlist_tracks  (cost=0.00..2171.18 rows=125118 width=8) (actual time=0.038..14.065 rows=125118 loops=4)
     751        ->  Index Scan using idx_sag_mv_song_id on song_average_grade_mv sag  (cost=0.43..7.58 rows=1 width=24) (actual time=91.275..91.276 rows=1 loops=1683)
     752              Index Cond: (song_id = s.id)
     753Planning Time: 2.976 ms
     754JIT:
     755  Functions: 272
     756"  Options: Inlining false, Optimization false, Expressions true, Deforming true"
     757"  Timing: Generation 16.724 ms (Deform 7.604 ms), Inlining 0.000 ms, Optimization 6.390 ms, Emission 157.809 ms, Total 180.923 ms"
     758Execution Time: 93882.201 ms
     759}}}
     760
     761За оптимизирање на секвенцијалните скенирање на табелите {{{artist_labels}}}, {{{album_tracks}}} и {{{songs}}} ги креираме индексите:
     762
     763{{{
     764CREATE INDEX idx_songs_title
     765ON songs(title);
     766
     767CREATE INDEX idx_album_tracks_song_id
     768ON album_tracks(song_id);
     769
     770CREATE INDEX idx_artist_labels_artist_id
     771ON artist_labels(artist_id);
     772}}}
     773
     774==== Време за извршување со индекси
     775
     776**3599.404 ms**
     777
     778{{{
     779Gather  (cost=188600.68..198746.59 rows=2211 width=121) (actual time=3136.649..3592.763 rows=1683 loops=1)
     780  Workers Planned: 1
     781  Workers Launched: 1
     782  ->  Nested Loop Left Join  (cost=187600.68..197525.49 rows=1301 width=121) (actual time=3114.199..3121.952 rows=842 loops=2)
     783        ->  Merge Left Join  (cost=187600.25..187655.31 rows=1301 width=85) (actual time=3114.146..3116.186 rows=842 loops=2)
     784              Merge Cond: (s.id = pc.song_id)
     785              ->  Sort  (cost=183967.34..183970.60 rows=1301 width=77) (actual time=3064.947..3065.100 rows=842 loops=2)
     786                    Sort Key: s.id
     787                    Sort Method: quicksort  Memory: 143kB
     788                    Worker 0:  Sort Method: quicksort  Memory: 40kB
     789                    ->  Hash Left Join  (cost=166553.22..183900.05 rows=1301 width=77) (actual time=3047.496..3064.384 rows=842 loops=2)
     790                          Hash Cond: (s.id = sc.song_id)
     791                          ->  Nested Loop Left Join  (cost=2735.67..20079.09 rows=1301 width=69) (actual time=64.333..80.679 rows=842 loops=2)
     792                                ->  Nested Loop Left Join  (cost=2735.25..19491.26 rows=1301 width=64) (actual time=64.294..76.539 rows=842 loops=2)
     793                                      ->  Hash Left Join  (cost=2734.82..10204.79 rows=1301 width=56) (actual time=64.235..70.862 rows=842 loops=2)
     794                                            Hash Cond: (al.label_id = l.id)
     795                                            ->  Nested Loop Left Join  (cost=2721.39..10187.90 rows=1301 width=41) (actual time=64.055..70.410 rows=842 loops=2)
     796                                                  ->  Parallel Hash Left Join  (cost=2721.10..9762.21 rows=1301 width=41) (actual time=64.004..67.464 rows=842 loops=2)
     797                                                        Hash Cond: (s.owner_artist_id = a.id)
     798                                                        ->  Parallel Bitmap Heap Scan on songs s  (cost=29.56..7067.26 rows=1301 width=29) (actual time=0.629..2.990 rows=842 loops=2)
     799                                                              Recheck Cond: ((title)::text = 'Harmony'::text)
     800                                                              Heap Blocks: exact=1395
     801                                                              ->  Bitmap Index Scan on idx_songs_title  (cost=0.00..29.01 rows=2211 width=0) (actual time=0.323..0.324 rows=1683 loops=1)
     802                                                                    Index Cond: ((title)::text = 'Harmony'::text)
     803                                                        ->  Parallel Hash  (cost=1956.24..1956.24 rows=58824 width=20) (actual time=62.392..62.393 rows=50000 loops=2)
     804                                                              Buckets: 131072  Batches: 1  Memory Usage: 6496kB
     805                                                              ->  Parallel Seq Scan on artists a  (cost=0.00..1956.24 rows=58824 width=20) (actual time=38.029..44.369 rows=50000 loops=2)
     806                                                  ->  Index Scan using idx_artist_labels_artist_id on artist_labels al  (cost=0.29..0.32 rows=1 width=16) (actual time=0.003..0.003 rows=0 loops=1683)
     807                                                        Index Cond: (artist_id = a.id)
     808                                            ->  Hash  (cost=8.75..8.75 rows=375 width=31) (actual time=0.155..0.155 rows=375 loops=2)
     809                                                  Buckets: 1024  Batches: 1  Memory Usage: 33kB
     810                                                  ->  Seq Scan on labels l  (cost=0.00..8.75 rows=375 width=31) (actual time=0.031..0.079 rows=375 loops=2)
     811                                      ->  Index Scan using idx_album_tracks_song_id on album_tracks at  (cost=0.43..7.13 rows=1 width=16) (actual time=0.006..0.006 rows=1 loops=1683)
     812                                            Index Cond: (song_id = s.id)
     813                                ->  Index Scan using albums_pkey on albums alb  (cost=0.42..0.45 rows=1 width=21) (actual time=0.004..0.004 rows=1 loops=1683)
     814                                      Index Cond: (id = at.album_id)
     815                          ->  Hash  (cost=161618.60..161618.60 rows=175916 width=16) (actual time=2982.071..2982.073 rows=635893 loops=2)
     816                                Buckets: 1048576 (originally 262144)  Batches: 1 (originally 1)  Memory Usage: 38000kB
     817                                ->  Subquery Scan on sc  (cost=158100.27..161618.60 rows=175916 width=16) (actual time=2526.994..2790.788 rows=635893 loops=2)
     818                                      ->  HashAggregate  (cost=158100.27..159859.43 rows=175916 width=16) (actual time=2526.987..2726.430 rows=635893 loops=2)
     819                                            Group Key: song_streams.song_id
     820                                            Batches: 1  Memory Usage: 65553kB
     821                                            Worker 0:  Batches: 1  Memory Usage: 65553kB
     822                                            ->  Seq Scan on song_streams  (cost=0.00..124221.85 rows=6775685 width=8) (actual time=0.049..708.679 rows=6775685 loops=2)
     823              ->  Sort  (cost=3632.91..3657.15 rows=9698 width=16) (actual time=49.155..49.933 rows=10235 loops=2)
     824                    Sort Key: pc.song_id
     825                    Sort Method: quicksort  Memory: 706kB
     826                    Worker 0:  Sort Method: quicksort  Memory: 706kB
     827                    ->  Subquery Scan on pc  (cost=2796.77..2990.73 rows=9698 width=16) (actual time=43.313..46.006 rows=10288 loops=2)
     828                          ->  HashAggregate  (cost=2796.77..2893.75 rows=9698 width=16) (actual time=43.307..44.952 rows=10288 loops=2)
     829                                Group Key: playlist_tracks.song_id
     830                                Batches: 1  Memory Usage: 1425kB
     831                                Worker 0:  Batches: 1  Memory Usage: 1425kB
     832                                ->  Seq Scan on playlist_tracks  (cost=0.00..2171.18 rows=125118 width=8) (actual time=0.039..13.544 rows=125118 loops=2)
     833        ->  Index Scan using idx_sag_mv_song_id on song_average_grade_mv sag  (cost=0.43..7.58 rows=1 width=24) (actual time=0.006..0.006 rows=1 loops=1683)
     834              Index Cond: (song_id = s.id)
     835Planning Time: 3.163 ms
     836JIT:
     837  Functions: 132
     838"  Options: Inlining false, Optimization false, Expressions true, Deforming true"
     839"  Timing: Generation 7.561 ms (Deform 3.504 ms), Inlining 0.000 ms, Optimization 2.789 ms, Emission 73.213 ms, Total 83.563 ms"
     840Execution Time: 3599.404 ms
     841}}}
     842
     843Бидејќи сепак имаме секвенцијално скенирање на табелата {{{song_streams}}} поради групирањето на слушања по песна, дополнително оптимизираме со материјализиран погледи:
     844
     845{{{
     846create materialized view song_stream_counts_mv as
     847select
     848    song_id,
     849    count(*) as streams
     850from song_streams
     851group by song_id;
     852
     853
     854create materialized view song_playlist_counts_mv as
     855select
     856    song_id,
     857    count(*) as saved_in_playlists
     858from playlist_tracks
     859group by song_id;
     860
     861
     862create or replace view song_detailed_info_view_mvs as
     863select
     864    s.title as title,
     865    a.display_name as artist_name,
     866    coalesce(l.name, 'SOLO') as label_name,
     867    coalesce(sc.streams, 0) as streams,
     868    coalesce(alb.title, 'SINGLE') as album_title,
     869    coalesce(pc.saved_in_playlists, 0) as saved_in_playlists,
     870    sag.num_reviews,
     871    ROUND(sag.avg_grade, 2) AS avg_grade
     872from songs s
     873left join artists a on a.id = s.owner_artist_id
     874left join artist_labels al on al.artist_id = a.id
     875left join labels l on l.id = al.label_id
     876left join album_tracks at on at.song_id = s.id
     877left join albums alb on alb.id = at.album_id
     878left join song_stream_counts_mv sc on sc.song_id = s.id
     879left join song_playlist_counts_mv pc on pc.song_id = s.id
     880left join song_average_grade_mv sag on sag.song_id = s.id;
     881}}}
     882
     883
     884==== Време за извршување на прашалникот по додавање на материјализирани погледи
     885
     886**333.811 ms**
     887
     888{{{
     889Gather  (cost=25188.45..57377.30 rows=2211 width=145) (actual time=113.872..333.525 rows=1683 loops=1)
     890  Workers Planned: 3
     891  Workers Launched: 3
     892  ->  Parallel Hash Right Join  (cost=24188.45..56156.20 rows=713 width=145) (actual time=84.308..207.282 rows=421 loops=4)
     893        Hash Cond: (sag.song_id = s.id)
     894        ->  Parallel Seq Scan on song_average_grade_mv sag  (cost=0.00..29616.74 rows=625674 width=24) (actual time=0.016..50.181 rows=484897 loops=4)
     895        ->  Parallel Hash  (cost=24176.94..24176.94 rows=921 width=85) (actual time=83.322..83.338 rows=421 loops=4)
     896              Buckets: 4096  Batches: 1  Memory Usage: 256kB
     897              ->  Hash Left Join  (cost=10077.11..24176.94 rows=921 width=85) (actual time=20.990..64.447 rows=421 loops=4)
     898                    Hash Cond: (s.id = pc.song_id)
     899                    ->  Nested Loop Left Join  (cost=9789.63..23885.95 rows=921 width=77) (actual time=17.300..60.507 rows=421 loops=4)
     900                          ->  Nested Loop Left Join  (cost=9789.21..23469.82 rows=921 width=72) (actual time=17.283..57.817 rows=421 loops=4)
     901                                ->  Hash Left Join  (cost=9788.78..16895.77 rows=921 width=64) (actual time=17.245..53.588 rows=421 loops=4)
     902                                      Hash Cond: (al.label_id = l.id)
     903                                      ->  Nested Loop Left Join  (cost=9775.35..16879.89 rows=921 width=49) (actual time=17.040..53.223 rows=421 loops=4)
     904                                            ->  Parallel Hash Left Join  (cost=9775.06..16578.54 rows=921 width=49) (actual time=16.984..51.378 rows=421 loops=4)
     905                                                  Hash Cond: (s.owner_artist_id = a.id)
     906                                                  ->  Parallel Hash Right Join  (cost=7083.52..13884.59 rows=921 width=37) (actual time=4.483..38.155 rows=421 loops=4)
     907                                                        Hash Cond: (sc.song_id = s.id)
     908                                                        ->  Parallel Seq Scan on song_stream_counts_mv sc  (cost=0.00..6105.55 rows=264955 width=16) (actual time=0.013..14.911 rows=158973 loops=4)
     909                                                        ->  Parallel Hash  (cost=7067.26..7067.26 rows=1301 width=29) (actual time=4.275..4.276 rows=421 loops=4)
     910                                                              Buckets: 4096  Batches: 1  Memory Usage: 160kB
     911                                                              ->  Parallel Bitmap Heap Scan on songs s  (cost=29.56..7067.26 rows=1301 width=29) (actual time=0.718..3.921 rows=421 loops=4)
     912                                                                    Recheck Cond: ((title)::text = 'Harmony'::text)
     913                                                                    Heap Blocks: exact=455
     914                                                                    ->  Bitmap Index Scan on idx_songs_title  (cost=0.00..29.01 rows=2211 width=0) (actual time=0.386..0.386 rows=1683 loops=1)
     915                                                                          Index Cond: ((title)::text = 'Harmony'::text)
     916                                                  ->  Parallel Hash  (cost=1956.24..1956.24 rows=58824 width=20) (actual time=12.017..12.018 rows=25000 loops=4)
     917                                                        Buckets: 131072  Batches: 1  Memory Usage: 6528kB
     918                                                        ->  Parallel Seq Scan on artists a  (cost=0.00..1956.24 rows=58824 width=20) (actual time=0.166..3.968 rows=25000 loops=4)
     919                                            ->  Index Scan using idx_artist_labels_artist_id on artist_labels al  (cost=0.29..0.32 rows=1 width=16) (actual time=0.004..0.004 rows=0 loops=1683)
     920                                                  Index Cond: (artist_id = a.id)
     921                                      ->  Hash  (cost=8.75..8.75 rows=375 width=31) (actual time=0.179..0.180 rows=375 loops=4)
     922                                            Buckets: 1024  Batches: 1  Memory Usage: 33kB
     923                                            ->  Seq Scan on labels l  (cost=0.00..8.75 rows=375 width=31) (actual time=0.030..0.078 rows=375 loops=4)
     924                                ->  Index Scan using idx_album_tracks_song_id on album_tracks at  (cost=0.43..7.13 rows=1 width=16) (actual time=0.009..0.009 rows=1 loops=1683)
     925                                      Index Cond: (song_id = s.id)
     926                          ->  Index Scan using albums_pkey on albums alb  (cost=0.42..0.45 rows=1 width=21) (actual time=0.006..0.006 rows=1 loops=1683)
     927                                Index Cond: (id = at.album_id)
     928                    ->  Hash  (cost=158.88..158.88 rows=10288 width=16) (actual time=3.568..3.568 rows=10288 loops=4)
     929                          Buckets: 16384  Batches: 1  Memory Usage: 611kB
     930                          ->  Seq Scan on song_playlist_counts_mv pc  (cost=0.00..158.88 rows=10288 width=16) (actual time=0.022..1.349 rows=10288 loops=4)
     931Planning Time: 135.345 ms
     932Execution Time: 333.811 ms
     933}}}
     934
     935
     936=== 8. Анализа на поглед 8, историја на слушање песни од корисниците
     937
     938Прашалник кој го тестираме:
     939
     940{{{
     941SELECT *
     942FROM streams_history
     943WHERE username='adriana_klein_511'
     944ORDER BY streamed_at DESC;
     945}}}
     946
     947==== Време за извршување без дополнителни индекси
     948
     949**0.404 ms**
     950
     951{{{
     952Sort  (cost=52.13..52.15 rows=7 width=58) (actual time=0.333..0.335 rows=15 loops=1)
     953  Sort Key: ss.streamed_at DESC
     954  Sort Method: quicksort  Memory: 26kB
     955  ->  Nested Loop  (cost=1.72..52.03 rows=7 width=58) (actual time=0.072..0.316 rows=15 loops=1)
     956        ->  Nested Loop  (cost=1.28..48.40 rows=7 width=62) (actual time=0.061..0.195 rows=15 loops=1)
     957              ->  Nested Loop  (cost=0.86..45.09 rows=7 width=49) (actual time=0.049..0.094 rows=15 loops=1)
     958                    ->  Index Scan using users_username_key on users u  (cost=0.42..8.44 rows=1 width=25) (actual time=0.029..0.030 rows=1 loops=1)
     959                          Index Cond: ((username)::text = 'adriana_klein_511'::text)
     960                    ->  Index Scan using idx_song_streams_user_id on song_streams ss  (cost=0.43..36.57 rows=8 width=32) (actual time=0.014..0.056 rows=15 loops=1)
     961                          Index Cond: (user_id = u.id)
     962              ->  Index Scan using songs_pkey on songs s  (cost=0.43..0.47 rows=1 width=21) (actual time=0.006..0.006 rows=1 loops=15)
     963                    Index Cond: (id = ss.song_id)
     964        ->  Index Scan using playback_sessions_pkey on playback_sessions ps  (cost=0.43..0.52 rows=1 width=12) (actual time=0.007..0.007 rows=1 loops=15)
     965              Index Cond: (id = ss.playback_session_id)
     966Planning Time: 91.388 ms
     967Execution Time: 0.404 ms
     968}}}
     969
     970Бидејќи се корситат индексите креирани за примарните клучеви, како и индексот {{{ idx_song_streams_user_id }}}, нема потреба за дополнителна оптимизација на прашалникот