| 4 | | - Доколку сакаме да видиме за конкретен артист извршуваме: |
| 5 | | |
| 6 | | [[Image(View1_1.png, 800px)]] |
| 7 | | |
| 8 | | - Времето потребно за извршување за овој прашалник е **~6ms**. Нема потреба од дополнителни индекси. |
| 9 | | - Доколку сакаме да извршиме некаква анализа, на пример да ги земеме најдобрите 10 корисници според број на следбеници, можеме да извршиме: |
| 10 | | |
| 11 | | [[Image(View1_2.png, 800px)]] |
| 12 | | |
| 13 | | - Потребно време за извршување на овој прашалник е **~200ms**. Нема индекс кој може да се додаде за да ги подобри перформансите на овој прашалник. |
| 14 | | |
| 15 | | - Query plan: |
| 16 | | |
| 17 | | [[Image(View1_3.png, 800px)]] |
| | 6 | |
| | 7 | Прашалниците кои ќе ги тестираме се следните: |
| | 8 | {{{ |
| | 9 | -- 1A: информации за конкретен корисник |
| | 10 | SELECT * FROM user_follow_info WHERE user_id = 5; |
| | 11 | |
| | 12 | -- 1B: топ 10 најследени корисници |
| | 13 | SELECT * 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 | {{{ |
| | 69 | CREATE 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 | }}} |
| 21 | | - Доколку сакаме да видиме кои се најактивните корисници на платформата изминатите 30 дена, извршуваме: |
| 22 | | |
| 23 | | [[Image(View2_1.png, 800px)]] |
| 24 | | |
| 25 | | - Времето потребно за пребарување: |
| 26 | | |
| 27 | | [[Image(View2_2.png, 800px)]] |
| 28 | | |
| 29 | | - **~250ms** |
| 30 | | |
| 31 | | - Можеме да додадеме индекс на streamed_at колоната со цел да избегнеме sequential scan на song_streams табелата: |
| 32 | | |
| 33 | | [[Image(View2_3.png, 800px)]] |
| 34 | | |
| 35 | | - Време на извршување по додавање на индекс: |
| 36 | | |
| 37 | | [[Image(View2_4.png, 800px)]] |
| 38 | | |
| 39 | | - **~200ms** -> ~20% подобри перформанси |
| | 127 | Прашалниците кои ќе ги тестираме се следните: |
| | 128 | {{{ |
| | 129 | -- 2A: активност на еден корисник |
| | 130 | SELECT * FROM user_activity_last_30_days WHERE user_id = 100376; |
| | 131 | |
| | 132 | -- 2B: топ 10 најактивни корисници |
| | 133 | SELECT * 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 | ``` |
| | 159 | Limit (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) |
| | 175 | Planning Time: 0.476 ms |
| | 176 | JIT: |
| | 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" |
| | 180 | Execution Time: 1976.733 ms |
| | 181 | |
| | 182 | ``` |
| | 183 | |
| | 184 | Бидејќи `song_streams` нема индекс на `user_id`, за прашалник 2А потребно е секвенцијално скенирање за да се најдат стримовите за еден корисник. Затоа, додаваме индекс на таа колона: |
| | 185 | |
| | 186 | {{{ |
| | 187 | CREATE INDEX idx_song_streams_user_id ON song_streams(user_id); |
| | 188 | }}} |
| | 189 | |
| | 190 | ==== Време за извршување по додавање на индекс |
| | 191 | |
| | 192 | **2A — 0.453 ms** (was 389.404 ms) |
| | 193 | |
| | 194 | {{{ |
| | 195 | Nested Loop (cost=0.86..45.14 rows=1 width=33) (actual time=0.295..0.297 rows=1 loops=1) |
| | 196 | -> 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) |
| | 197 | Index Cond: (id = 100376) |
| | 198 | -> GroupAggregate (cost=0.43..36.68 rows=1 width=16) (actual time=0.220..0.221 rows=1 loops=1) |
| | 199 | -> 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) |
| | 200 | Index Cond: (user_id = 100376) |
| | 201 | Filter: ((streamed_at <= now()) AND (streamed_at >= (CURRENT_DATE - 30))) |
| | 202 | Rows Removed by Filter: 8 |
| | 203 | Planning Time: 1.843 ms |
| | 204 | Execution Time: 0.453 ms |
| | 205 | }}} |
| | 206 | |
| | 207 | 2Б остана непроменето - бидејќи прашалникот треба да направи комплексна агрегација на големи табели нема баш некој конкретен индекс што може да ги подобри перформансите. Доколку овој прашалник се извршува често во апликацијата, јасно е дека тоа може да доведе до проблеми. Ова можеме да го решиме на повеќе начини: со менување на погледот во материјализиран поглед, со кеширање и слично. Првиот пристап (материјализирани погледи) како решение ќе го погледнеме понатаму во оптимизацијата на други погледи, а конкретно за овој поглед ќе одиме со вториот пристап, поточно со кеширање во самиот апликациски код. |
| | 208 | |
| 44 | | - Доколку сакаме да ги видиме најдобро оценуваните песни на платформата, извршуваме: |
| 45 | | |
| 46 | | [[Image(View3_1.png, 800px)]] |
| 47 | | |
| 48 | | - Време потребно за пребарување: |
| 49 | | |
| 50 | | [[Image(View3_2.png, 800px)]] |
| 51 | | |
| 52 | | - **~260ms** |
| 53 | | |
| 54 | | - Можеме да додадеме индекс на song_id, grade со цел да избегнеме sequential scan на songs: |
| 55 | | |
| 56 | | [[Image(View3_3.png, 800px)]] |
| 57 | | |
| 58 | | - Време на извршување по додавање на индекс: |
| 59 | | |
| 60 | | [[Image(View3_4.png, 800px)]] |
| 61 | | |
| 62 | | - **~200ms** -> ~20% подобри перформанси |
| 63 | | |
| 64 | | - Време потребно за внесување на нов запис без индекс: |
| 65 | | |
| 66 | | [[Image(View3_5.png, 800px)]] |
| 67 | | |
| 68 | | [[Image(View3_6.png, 800px)]] |
| 69 | | |
| 70 | | - Со индекс: |
| 71 | | |
| 72 | | [[Image(View3_7.png, 800px)]] |
| 73 | | |
| 74 | | - Време потребно за ажурирање на запис без индекс: |
| 75 | | |
| 76 | | [[Image(View3_8.png, 800px)]] |
| 77 | | |
| 78 | | [[Image(View3_9.png, 800px)]] |
| 79 | | |
| 80 | | - Со индекс: |
| 81 | | |
| 82 | | [[Image(View3_10.png, 800px)]] |
| 83 | | |
| 84 | | - Во двата случаи (запишување и ажурирање) нема никаков ефект врз перформансите додавање на индексот |
| 85 | | |
| | 213 | |