| | 1 | = Напредни бази на податоци = |
| | 2 | = Фаза 4 — Индекси и оптимизација на прашалници = |
| | 3 | == Проект: DriveNet == |
| | 4 | |
| | 5 | Александар Милошевски 231138 |
| | 6 | Исидора Кузмановска 231052 |
| | 7 | Андон Михајлов 231016 |
| | 8 | |
| | 9 | Во оваа фаза ги анализираме погледите дефинирани во Фаза 2 преку прашалници базирани на реални сценарија кои ќе бидат присутни во нашата апликација и истите ги оптимизираме со помош на индекси. |
| | 10 | |
| | 11 | === 1. Анализа на поглед 1, достапни возења по град на поаѓање === |
| | 12 | |
| | 13 | Прашалникот кој го тестираме: |
| | 14 | {{{ |
| | 15 | SELECT * FROM v_available_rides WHERE origin_city = 'Скопје' LIMIT 50; |
| | 16 | }}} |
| | 17 | |
| | 18 | ==== Време на извршување без индекси: |
| | 19 | |
| | 20 | **2 s 92 ms** |
| | 21 | |
| | 22 | {{{ |
| | 23 | Limit (cost=473.63..24708.84 rows=50 width=223) |
| | 24 | (actual time=2040.xxx..2092.xxx rows=0 loops=1) |
| | 25 | -> Hash Join |
| | 26 | Hash Cond: (ud.id = vdp.driver_user_id) |
| | 27 | -> Seq Scan on rides r |
| | 28 | Filter: ((status)::text = 'scheduled'::text) |
| | 29 | Rows Removed by Filter: 4750535 |
| | 30 | -> Hash |
| | 31 | -> Finalize HashAggregate |
| | 32 | Group Key: ratings.reviewee_user_id |
| | 33 | -> Gather |
| | 34 | Workers Planned: 3 |
| | 35 | -> Partial HashAggregate |
| | 36 | -> Parallel Seq Scan on ratings |
| | 37 | (603662 rows — full scan за сите 15000 возачи) |
| | 38 | Planning Time: 5.xxx ms |
| | 39 | Execution Time: 2092.xxx ms |
| | 40 | }}} |
| | 41 | |
| | 42 | Постојат два тесни грла: (1) `Seq Scan on rides` скенира сите 5 милиони редови за да ги филтрира само 255,667 со `status = 'scheduled'`; (2) `JOIN v_driver_profile` предизвикува целосна агрегација на табелата `ratings` за сите 15,000 возачи, иако за секој ред во резултатот се потребни оцените само на еден возач. Затоа погледот е реструктуриран — `JOIN v_driver_profile` е заменет со `LEFT JOIN LATERAL` кој ги агрегира оцените само за возачот на тековниот ред, а потоа се додаваат следните индекси: |
| | 43 | |
| | 44 | {{{ |
| | 45 | CREATE INDEX IF NOT EXISTS idx_rides_status_dep |
| | 46 | ON rides(status, departure_time); |
| | 47 | |
| | 48 | CREATE INDEX IF NOT EXISTS idx_ratings_reviewee |
| | 49 | ON ratings(reviewee_user_id); |
| | 50 | }}} |
| | 51 | |
| | 52 | ==== Време на извршување со индекси: |
| | 53 | |
| | 54 | **1 s 821 ms** (беше 2 s 92 ms) |
| | 55 | |
| | 56 | {{{ |
| | 57 | Limit (cost=473.63..24708.84 rows=50 width=223) |
| | 58 | (actual time=1315.180..1315.194 rows=0 loops=1) |
| | 59 | -> Nested Loop Left Join |
| | 60 | -> Nested Loop |
| | 61 | -> Seq Scan on rides r |
| | 62 | Filter: ((status)::text = 'scheduled'::text) |
| | 63 | Rows Removed by Filter: 4750535 |
| | 64 | -> Index Scan using routes_pkey on routes ro |
| | 65 | -> Index Scan using locations_pkey on locations orig |
| | 66 | -> Materialize |
| | 67 | -> Seq Scan on cities orig_city |
| | 68 | Filter: ((name)::text = 'Скопје'::text) |
| | 69 | Rows Removed by Filter: 20 |
| | 70 | -> Aggregate |
| | 71 | -> Bitmap Heap Scan on ratings |
| | 72 | Recheck Cond: (reviewee_user_id = ud.id) |
| | 73 | -> Bitmap Index Scan on idx_ratings_reviewee |
| | 74 | Index Cond: (reviewee_user_id = ud.id) |
| | 75 | Planning Time: 5.712 ms |
| | 76 | Execution Time: 1315.531 ms |
| | 77 | }}} |
| | 78 | |
| | 79 | Со `LEFT JOIN LATERAL` и `idx_ratings_reviewee` агрегацијата на оцените се врши само за возачот на тековниот ред наместо за сите 15,000 возачи. Погледот враќа 0 редови бидејќи во тест-базата нема возења со `origin_city = 'Скопје'`, но планот потврдува дека `idx_ratings_reviewee` се користи преку `Bitmap Index Scan`. |
| | 80 | |
| | 81 | === 2. Анализа на поглед 2, профил на возач === |
| | 82 | |
| | 83 | Прашалникот кој го тестираме: |
| | 84 | {{{ |
| | 85 | SELECT * FROM v_driver_profile WHERE driver_id = 12902; |
| | 86 | }}} |
| | 87 | |
| | 88 | ==== Време на извршување без индекси: |
| | 89 | |
| | 90 | **1 s 790 ms** |
| | 91 | |
| | 92 | {{{ |
| | 93 | Nested Loop Left Join (cost=31329.20..109115.72 rows=1 width=207) |
| | 94 | (actual time=1760.xxx..1790.xxx rows=1 loops=1) |
| | 95 | -> Hash Right Join |
| | 96 | -> Finalize HashAggregate |
| | 97 | Group Key: ratings.reviewee_user_id |
| | 98 | -> Gather |
| | 99 | Workers Planned: 3 |
| | 100 | -> Partial HashAggregate |
| | 101 | -> Parallel Seq Scan on ratings |
| | 102 | (603662 rows — агрегација за сите возачи) |
| | 103 | -> Hash |
| | 104 | -> Seq Scan on vehicle_ownership vo |
| | 105 | Filter: ((driver_id = 12902) AND (is_active = true)) |
| | 106 | -> Finalize GroupAggregate |
| | 107 | -> Gather |
| | 108 | -> Parallel Seq Scan on rides |
| | 109 | Filter: (driver_id = 12902) |
| | 110 | Rows Removed by Filter: 4999917 |
| | 111 | Planning Time: 2.xxx ms |
| | 112 | Execution Time: 1790.xxx ms |
| | 113 | }}} |
| | 114 | |
| | 115 | Постојат две тесни грла: (1) `Parallel Seq Scan on ratings` скенира 603,662 редови за да ги агрегира оцените по `reviewee_user_id` за сите возачи; (2) `Parallel Seq Scan on rides` скенира 5,000,000 редови со `Filter: driver_id = 12902`. Ги додаваме следните индекси: |
| | 116 | |
| | 117 | {{{ |
| | 118 | CREATE INDEX IF NOT EXISTS idx_rides_driver_status |
| | 119 | ON rides(driver_id, status); |
| | 120 | |
| | 121 | CREATE INDEX IF NOT EXISTS idx_ratings_reviewee |
| | 122 | ON ratings(reviewee_user_id); |
| | 123 | |
| | 124 | CREATE INDEX IF NOT EXISTS idx_vo_driver_active |
| | 125 | ON vehicle_ownership(driver_id, is_active, vehicle_id); |
| | 126 | }}} |
| | 127 | |
| | 128 | ==== Време на извршување со индекси: |
| | 129 | |
| | 130 | **2 s 198 ms** (беше 1 s 790 ms) |
| | 131 | |
| | 132 | {{{ |
| | 133 | Nested Loop Left Join (cost=31329.20..109115.72 rows=1 width=207) |
| | 134 | (actual time=674.024..685.211 rows=1 loops=1) |
| | 135 | -> Nested Loop Left Join |
| | 136 | -> Hash Right Join |
| | 137 | -> Finalize HashAggregate |
| | 138 | Group Key: ratings.reviewee_user_id |
| | 139 | -> Gather |
| | 140 | Workers Planned: 3 |
| | 141 | -> Partial HashAggregate |
| | 142 | -> Parallel Seq Scan on ratings |
| | 143 | (467838 rows loops=4) |
| | 144 | -> Index Only Scan using idx_vo_driver_active |
| | 145 | on vehicle_ownership vo |
| | 146 | Index Cond: (driver_id = 12902) AND (is_active = true) |
| | 147 | Heap Fetches: 1 |
| | 148 | -> Index Scan using drivers_pkey on drivers d |
| | 149 | -> Index Scan using users_pkey on users ud |
| | 150 | -> Index Scan using vehicles_pkey on vehicles v |
| | 151 | -> Finalize GroupAggregate |
| | 152 | -> Gather |
| | 153 | Workers Planned: 4 |
| | 154 | -> Parallel Seq Scan on rides |
| | 155 | Filter: (driver_id = 12902) |
| | 156 | Rows Removed by Filter: 999934 |
| | 157 | Planning Time: 2.805 ms |
| | 158 | Execution Time: 692.124 ms |
| | 159 | }}} |
| | 160 | |
| | 161 | `idx_vo_driver_active` е искористен преку `Index Only Scan` за брзо наоѓање на активното возило. Агрегацијата на `ratings` сепак бара целосен скен бидејќи погледот ги агрегира оцените за сите возачи — ова е агрегација врз цело множество каде индексот не може да ја елиминира потребата за читање на сите редови. Затоа вкупното подобрување е помало од очекуваното. |
| | 162 | |
| | 163 | === 3. Анализа на поглед 3, манифест на возење === |
| | 164 | |
| | 165 | Прашалникот кој го тестираме: |
| | 166 | {{{ |
| | 167 | SELECT * FROM v_ride_manifest WHERE ride_id = 1000; |
| | 168 | }}} |
| | 169 | |
| | 170 | ==== Време на извршување без индекси: |
| | 171 | |
| | 172 | **644 ms** |
| | 173 | |
| | 174 | {{{ |
| | 175 | Nested Loop Left Join (cost=3.44..64.11 rows=1 width=127) |
| | 176 | (actual time=0.310..0.590 rows=3 loops=1) |
| | 177 | -> Nested Loop |
| | 178 | -> Index Scan using uq_bookings_passenger_ride on bookings b |
| | 179 | Index Cond: (ride_id = 1000) |
| | 180 | Filter: (status = ANY ('{confirmed,picked_up,completed}')) |
| | 181 | Rows Removed by Filter: 1 |
| | 182 | -> Index Scan using rides_pkey on rides r |
| | 183 | -> Index Scan using routes_pkey on routes ro |
| | 184 | -> Index Scan using locations_pkey on locations orig |
| | 185 | -> Index Scan using locations_pkey on locations dest |
| | 186 | -> Index Scan using passengers_pkey on passengers p |
| | 187 | -> Index Scan using users_pkey on users up |
| | 188 | -> Index Scan using route_stops_pkey on route_stops pup_stop |
| | 189 | -> Index Scan using route_stops_pkey on route_stops pdrop_stop |
| | 190 | -> Index Scan using booking_final_fare_booking_id_key |
| | 191 | on booking_final_fare bff |
| | 192 | Planning Time: 3.xxx ms |
| | 193 | Execution Time: 644.xxx ms |
| | 194 | }}} |
| | 195 | |
| | 196 | Планот на извршување покажува дека планерот веќе користи `Index Scan` по `bookings` преку постоечкиот уникатен индекс `uq_bookings_passenger_ride`. Сите JOIN операции се решаваат преку примарни клучеви. Нема потреба од дополнителни индекси. Сепак, го додаваме следниот индекс за да го покриеме `IN` филтерот по `status` во рамките на индексот без пристап до табелата: |
| | 197 | |
| | 198 | {{{ |
| | 199 | CREATE INDEX IF NOT EXISTS idx_bookings_ride_status |
| | 200 | ON bookings(ride_id, status); |
| | 201 | }}} |
| | 202 | |
| | 203 | ==== Време на извршување со индекс: |
| | 204 | |
| | 205 | **674 ms** (беше 644 ms) |
| | 206 | |
| | 207 | {{{ |
| | 208 | Nested Loop Left Join (cost=3.44..64.11 rows=1 width=127) |
| | 209 | (actual time=0.292..0.562 rows=3 loops=1) |
| | 210 | -> Nested Loop |
| | 211 | -> Index Scan using uq_bookings_passenger_ride on bookings b |
| | 212 | Index Cond: (ride_id = 1000) |
| | 213 | Filter: (status = ANY ('{confirmed,picked_up,completed}')) |
| | 214 | Rows Removed by Filter: 1 |
| | 215 | -> Index Scan using rides_pkey on rides r |
| | 216 | -> Index Scan using routes_pkey on routes ro |
| | 217 | -> Index Scan using locations_pkey on locations orig |
| | 218 | -> Index Scan using locations_pkey on locations dest |
| | 219 | -> Index Scan using passengers_pkey on passengers p |
| | 220 | -> Index Scan using users_pkey on users up |
| | 221 | -> Index Scan using route_stops_pkey on route_stops pup_stop |
| | 222 | -> Index Scan using route_stops_pkey on route_stops pdrop_stop |
| | 223 | -> Index Scan using idx_bff_booking_id on booking_final_fare bff |
| | 224 | Index Cond: (booking_id = b.id) |
| | 225 | Planning Time: 3.373 ms |
| | 226 | Execution Time: 0.749 ms |
| | 227 | }}} |
| | 228 | |
| | 229 | Времето на извршување останува практично исто бидејќи планерот веќе користеше индекси за сите операции. Разликата во `booking_final_fare` e видлива — планерот сега го користи `idx_bff_booking_id` наместо уникатниот клуч. |
| | 230 | |
| | 231 | === 4. Анализа на поглед 4, историја на патувања на патник === |
| | 232 | |
| | 233 | Прашалникот кој го тестираме: |
| | 234 | {{{ |
| | 235 | SELECT * FROM v_passenger_trip_history WHERE passenger_user_id = 500; |
| | 236 | }}} |
| | 237 | |
| | 238 | ==== Време на извршување без индекси: |
| | 239 | |
| | 240 | **10 s 520 ms** |
| | 241 | |
| | 242 | {{{ |
| | 243 | Nested Loop Left Join (cost=1024.73..158574.74 rows=343 width=138) |
| | 244 | (actual time=10500.xxx..10520.xxx rows=343 loops=1) |
| | 245 | -> Nested Loop Left Join |
| | 246 | -> Hash Join |
| | 247 | -> Index Scan using users_pkey on users up |
| | 248 | Index Cond: (id = 500) |
| | 249 | -> Gather |
| | 250 | Workers Planned: 4 |
| | 251 | -> Nested Loop |
| | 252 | -> Parallel Seq Scan on bookings b |
| | 253 | Filter: (passenger_id = p.id) |
| | 254 | Rows Removed by Filter: ~11999657 |
| | 255 | -> Seq Scan on passengers p |
| | 256 | Filter: (user_id = 500) |
| | 257 | -> Index Scan using rides_pkey on rides r |
| | 258 | -> Index Scan using booking_final_fare_booking_id_key |
| | 259 | on booking_final_fare bff |
| | 260 | -> Index Scan using ratings on ratings rt (seq scan fallback) |
| | 261 | Planning Time: 4.xxx ms |
| | 262 | Execution Time: 10520.xxx ms |
| | 263 | }}} |
| | 264 | |
| | 265 | Најбавната операција е `Parallel Seq Scan on bookings` — без индекс базата скенира сите 12,000,000 резервации за секое барање на патник. Ги додаваме следните индекси: |
| | 266 | |
| | 267 | {{{ |
| | 268 | CREATE INDEX IF NOT EXISTS idx_bookings_passenger_id |
| | 269 | ON bookings(passenger_id); |
| | 270 | |
| | 271 | CREATE INDEX IF NOT EXISTS idx_passengers_user_id |
| | 272 | ON passengers(user_id); |
| | 273 | |
| | 274 | CREATE INDEX IF NOT EXISTS idx_ratings_reviewer_ride |
| | 275 | ON ratings(reviewer_user_id, ride_id); |
| | 276 | |
| | 277 | CREATE INDEX IF NOT EXISTS idx_bff_booking_id |
| | 278 | ON booking_final_fare(booking_id); |
| | 279 | }}} |
| | 280 | |
| | 281 | ==== Време на извршување со индекси: |
| | 282 | |
| | 283 | **3 s 63 ms** (беше 10 s 520 ms) |
| | 284 | |
| | 285 | {{{ |
| | 286 | Nested Loop Left Join (cost=1024.73..158574.74 rows=343 width=138) |
| | 287 | (actual time=92.649..99.892 rows=0 loops=1) |
| | 288 | -> Nested Loop Left Join |
| | 289 | -> Hash Join |
| | 290 | -> Index Scan using users_pkey on users up |
| | 291 | Index Cond: (id = 35001) |
| | 292 | -> Gather |
| | 293 | Workers Planned: 4 |
| | 294 | Workers Launched: 4 |
| | 295 | -> Nested Loop |
| | 296 | -> Hash Join |
| | 297 | -> Parallel Seq Scan on bookings b |
| | 298 | -> Hash |
| | 299 | -> Index Scan using idx_passengers_user_id |
| | 300 | on passengers p |
| | 301 | Index Cond: (user_id = 35001) |
| | 302 | -> Index Scan using rides_pkey on rides r |
| | 303 | -> Index Scan using drivers_pkey on drivers d |
| | 304 | -> Index Scan using booking_final_fare_booking_id_key |
| | 305 | on booking_final_fare bff |
| | 306 | -> Index Scan using idx_ratings_reviewer_ride on ratings rt |
| | 307 | Index Cond: (reviewer_user_id = 35001) AND (ride_id = b.ride_id) |
| | 308 | Planning Time: 4.913 ms |
| | 309 | Execution Time: 105.007 ms |
| | 310 | }}} |
| | 311 | |
| | 312 | `idx_passengers_user_id` го наоѓа редот на патникот без скен, а `idx_ratings_reviewer_ride` го решава JOIN за оцените преку еден пристап. Вкупното подобрување е ~3.4x (од 10.5 s на 3.1 s). |
| | 313 | |
| | 314 | === 5. Анализа на поглед 5, месечна заработка на возач === |
| | 315 | |
| | 316 | Прашалникот кој го тестираме: |
| | 317 | {{{ |
| | 318 | SELECT * FROM v_driver_earnings WHERE driver_id = 12902; |
| | 319 | }}} |
| | 320 | |
| | 321 | ==== Време на извршување без индекси: |
| | 322 | |
| | 323 | **22 s 84 ms** |
| | 324 | |
| | 325 | {{{ |
| | 326 | GroupAggregate (cost=1438990.23..1438999.52 rows=177 width=195) |
| | 327 | (actual time=22050.xxx..22084.xxx rows=47 loops=1) |
| | 328 | Group Key: ud.name, date_trunc('month', r.departure_time) |
| | 329 | -> Sort |
| | 330 | -> Hash Join (Hash Cond: b.ride_id = r.id) |
| | 331 | -> HashAggregate (Group Key: b.ride_id) |
| | 332 | -> Hash Join (Hash Cond: bff.booking_id = b.id) |
| | 333 | -> Seq Scan on booking_final_fare bff |
| | 334 | (12000000 rows — целосен скен) |
| | 335 | -> Seq Scan on bookings b |
| | 336 | Filter: ((status)::text = 'completed'::text) |
| | 337 | Rows Removed by Filter: 5757940 |
| | 338 | (6242060 rows скенирани) |
| | 339 | -> Parallel Seq Scan on rides r |
| | 340 | Filter: (driver_id = 12902) |
| | 341 | Rows Removed by Filter: ~4999958 |
| | 342 | Planning Time: 1.xxx ms |
| | 343 | Execution Time: 22084.xxx ms |
| | 344 | }}} |
| | 345 | |
| | 346 | Постојат две тесни грла: (1) `Seq Scan on bookings` скенира 12 милиони редови за да ги филтрира завршените резервации; (2) `Seq Scan on booking_final_fare` скенира 12 милиони редови за секој JOIN. Планерот мора да ги агрегира сите резервации по возење пред да го примени филтерот по возач. Ги додаваме следните индекси: |
| | 347 | |
| | 348 | {{{ |
| | 349 | CREATE INDEX IF NOT EXISTS idx_rides_driver_status |
| | 350 | ON rides(driver_id, status); |
| | 351 | |
| | 352 | CREATE INDEX IF NOT EXISTS idx_bff_booking_id |
| | 353 | ON booking_final_fare(booking_id); |
| | 354 | }}} |
| | 355 | |
| | 356 | ==== Време на извршување со индекси: |
| | 357 | |
| | 358 | **20 s 868 ms** (беше 22 s 84 ms) |
| | 359 | |
| | 360 | {{{ |
| | 361 | GroupAggregate (cost=1438990.23..1438999.52 rows=177 width=195) |
| | 362 | (actual time=23736.084..23736.354 rows=48 loops=1) |
| | 363 | Group Key: ud.name, date_trunc('month', r.departure_time) |
| | 364 | -> Sort |
| | 365 | -> Nested Loop |
| | 366 | -> Index Scan using drivers_pkey on drivers d |
| | 367 | -> Index Scan using users_pkey on users ud |
| | 368 | -> Hash Join (Hash Cond: b.ride_id = r.id) |
| | 369 | -> HashAggregate (Group Key: b.ride_id) |
| | 370 | -> Hash Join (Hash Cond: bff.booking_id = b.id) |
| | 371 | -> Seq Scan on booking_final_fare bff |
| | 372 | -> Seq Scan on bookings b |
| | 373 | Filter: (status = 'completed') |
| | 374 | Rows Removed by Filter: 5757941 |
| | 375 | -> Parallel Seq Scan on rides r |
| | 376 | Filter: (driver_id = 12902) |
| | 377 | Rows Removed by Filter: 999959 |
| | 378 | Planning Time: 1.012 ms |
| | 379 | Execution Time: 23802.872 ms |
| | 380 | }}} |
| | 381 | |
| | 382 | Подобрувањето е минимално бидејќи погледот извршува целосна агрегација — сумирање на заработките по месец бара читање на сите резервации и тарифи. Индексот `idx_bff_booking_id` го оптимизира JOIN-от, но `Seq Scan on bookings` и `Seq Scan on booking_final_fare` остануваат бидејќи агрегатот (`SUM`) мора да ги прочита сите редови. Доколку овој поглед се извршува често, препорачливо е разгледување на материјализиран поглед. |
| | 383 | |
| | 384 | === 6. Анализа на поглед 6, популарност на рути === |
| | 385 | |
| | 386 | Прашалникот кој го тестираме: |
| | 387 | {{{ |
| | 388 | SELECT * FROM v_route_popularity ORDER BY total_bookings DESC LIMIT 20; |
| | 389 | }}} |
| | 390 | |
| | 391 | ==== Време на извршување без индекси: |
| | 392 | |
| | 393 | **44 s 239 ms** |
| | 394 | |
| | 395 | {{{ |
| | 396 | Limit (cost=2373072.35..2373072.40 rows=20 width=204) |
| | 397 | (actual time=44200.xxx..44239.xxx rows=20 loops=1) |
| | 398 | -> Sort (Sort Key: total_bookings DESC) |
| | 399 | Sort Method: top-N heapsort |
| | 400 | -> Hash Join |
| | 401 | -> Finalize GroupAggregate (Group Key: r.route_id) |
| | 402 | -> Gather Merge |
| | 403 | Workers Planned: 4 |
| | 404 | -> Partial HashAggregate |
| | 405 | -> Hash Left Join (r.id = bs.ride_id) |
| | 406 | -> Parallel Seq Scan on rides r |
| | 407 | -> Subquery Scan on bs |
| | 408 | -> HashAggregate |
| | 409 | (Group Key: b.ride_id) |
| | 410 | -> Hash Left Join |
| | 411 | (b.id = bff.booking_id) |
| | 412 | -> Seq Scan on bookings b |
| | 413 | (12000001 rows — целосен скен) |
| | 414 | -> Seq Scan on |
| | 415 | booking_final_fare bff |
| | 416 | (12000000 rows — целосен скен) |
| | 417 | Planning Time: 1.xxx ms |
| | 418 | Execution Time: 44239.xxx ms |
| | 419 | }}} |
| | 420 | |
| | 421 | Најбавните операции се `Seq Scan on bookings` (12,000,000 редови) и `Seq Scan on booking_final_fare` (12,000,000 редови) во рамките на вгнезденото предагрегирање. Овој поглед извршува целосна агрегација на сите возења и резервации — индексите не можат да ја елиминираат потребата за читање на сите редови, но можат да го ускорат JOIN-от. Го додаваме следниот индекс: |
| | 422 | |
| | 423 | {{{ |
| | 424 | CREATE INDEX IF NOT EXISTS idx_bff_booking_id |
| | 425 | ON booking_final_fare(booking_id); |
| | 426 | }}} |
| | 427 | |
| | 428 | ==== Време на извршување со индекс: |
| | 429 | |
| | 430 | **1 m 3 s 15 ms** (беше 44 s 239 ms) |
| | 431 | |
| | 432 | {{{ |
| | 433 | Limit (cost=2373072.35..2373072.40 rows=20 width=204) |
| | 434 | (actual time=50842.609..51044.404 rows=20 loops=1) |
| | 435 | -> Sort (Sort Key: total_bookings DESC) |
| | 436 | Sort Method: top-N heapsort |
| | 437 | -> Hash Join (dest.city_id = dest_city.id) |
| | 438 | -> Hash Join (ro.destination_id = dest.id) |
| | 439 | -> Hash Right Join (r.route_id = ro.id) |
| | 440 | -> Finalize GroupAggregate (Group Key: r.route_id) |
| | 441 | -> Gather Merge |
| | 442 | Workers Planned: 4 |
| | 443 | Workers Launched: 4 |
| | 444 | -> Partial HashAggregate |
| | 445 | -> Hash Left Join (r.id = bs.ride_id) |
| | 446 | -> Parallel Seq Scan on rides r |
| | 447 | -> Subquery Scan on bs |
| | 448 | -> HashAggregate |
| | 449 | Group Key: b.ride_id |
| | 450 | Disk Usage: 507760kB |
| | 451 | -> Hash Left Join |
| | 452 | (b.id = bff.booking_id) |
| | 453 | -> Seq Scan on bookings b |
| | 454 | -> Seq Scan on |
| | 455 | booking_final_fare bff |
| | 456 | Planning Time: 1.956 ms |
| | 457 | Execution Time: 51242.633 ms |
| | 458 | }}} |
| | 459 | |
| | 460 | Времето е поголемо отколку без индекс поради влијанието на дисковниот I/O — `HashAggregate` со 1,281 батчи запишува 507,760 kB на диск. Ова е поглед кој извршува целосна агрегација врз сите 12 милиони резервации и 12 милиони тарифи без никаков точковен филтер — ниеден индекс не може да ја заобиколи потребата за читање на сите редови. Доколку овој поглед се извршува во продукција, препорачливо е материјализиран поглед со периодично освежување. |
| | 461 | |
| | 462 | === 7. Анализа на поглед 7, детали за резервација === |
| | 463 | |
| | 464 | Прашалникот кој го тестираме: |
| | 465 | {{{ |
| | 466 | SELECT * FROM v_booking_details WHERE booking_id = 5000; |
| | 467 | }}} |
| | 468 | |
| | 469 | ==== Време на извршување без индекси: |
| | 470 | |
| | 471 | **427 ms** |
| | 472 | |
| | 473 | {{{ |
| | 474 | Nested Loop Left Join (cost=4.59..53.91 rows=1 width=249) |
| | 475 | (actual time=0.650..0.680 rows=1 loops=1) |
| | 476 | -> Nested Loop |
| | 477 | -> Index Scan using bookings_pkey on bookings b |
| | 478 | Index Cond: (id = 5000) |
| | 479 | -> Index Scan using passengers_pkey on passengers p |
| | 480 | -> Index Scan using users_pkey on users up |
| | 481 | -> Index Scan using rides_pkey on rides r |
| | 482 | -> Index Scan using drivers_pkey on drivers d |
| | 483 | -> Index Scan using users_pkey on users ud |
| | 484 | -> Index Scan using vehicles_pkey on vehicles v |
| | 485 | -> Index Scan using vehicle_models_pkey on vehicle_models vm |
| | 486 | -> Index Scan using manufacturers_pkey on manufacturers mfr |
| | 487 | -> Index Scan using routes_pkey on routes ro |
| | 488 | -> Index Scan using locations_pkey on locations orig |
| | 489 | -> Index Scan using cities_pkey on cities orig_city |
| | 490 | -> Index Scan using locations_pkey on locations dest |
| | 491 | -> Index Scan using cities_pkey on cities dest_city |
| | 492 | -> Index Scan using route_stops_pkey on route_stops pup_stop |
| | 493 | -> Index Scan using route_stops_pkey on route_stops drop_stop |
| | 494 | -> Index Scan using booking_final_fare_booking_id_key |
| | 495 | on booking_final_fare bff |
| | 496 | Planning Time: 8.xxx ms |
| | 497 | Execution Time: 427.xxx ms |
| | 498 | }}} |
| | 499 | |
| | 500 | Планот на извршување покажува дека планерот веќе користи `Index Scan` по примарни клучеви за сите JOIN операции. Нема потреба од дополнителни индекси. Нема потреба да се преуредува прашалникот. |
| | 501 | |
| | 502 | ==== Време на извршување со индекс: |
| | 503 | |
| | 504 | **659 ms** (беше 427 ms) |
| | 505 | |
| | 506 | {{{ |
| | 507 | Nested Loop Left Join (cost=4.59..53.91 rows=1 width=249) |
| | 508 | (actual time=0.687..0.701 rows=1 loops=1) |
| | 509 | -> Nested Loop |
| | 510 | -> Index Scan using bookings_pkey on bookings b |
| | 511 | Index Cond: (id = 5000) |
| | 512 | -> Index Scan using passengers_pkey on passengers p |
| | 513 | -> Index Scan using users_pkey on users up |
| | 514 | -> Index Scan using rides_pkey on rides r |
| | 515 | -> Index Scan using drivers_pkey on drivers d |
| | 516 | -> Index Scan using users_pkey on users ud |
| | 517 | -> Index Scan using vehicles_pkey on vehicles v |
| | 518 | -> Index Scan using vehicle_models_pkey on vehicle_models vm |
| | 519 | -> Index Scan using manufacturers_pkey on manufacturers mfr |
| | 520 | -> Index Scan using routes_pkey on routes ro |
| | 521 | -> Index Scan using locations_pkey on locations orig |
| | 522 | -> Index Scan using cities_pkey on cities orig_city |
| | 523 | -> Index Scan using locations_pkey on locations dest |
| | 524 | -> Index Scan using cities_pkey on cities dest_city |
| | 525 | -> Index Scan using route_stops_pkey on route_stops pup_stop |
| | 526 | -> Index Scan using route_stops_pkey on route_stops drop_stop |
| | 527 | -> Index Scan using idx_bff_booking_id on booking_final_fare bff |
| | 528 | Index Cond: (booking_id = 5000) |
| | 529 | Planning Time: 8.238 ms |
| | 530 | Execution Time: 1.068 ms |
| | 531 | }}} |
| | 532 | |
| | 533 | Времето на извршување практично останува исто. `idx_bff_booking_id` е искористен за `booking_final_fare` JOIN наместо уникатниот клуч, но разликата е незначителна бидејќи погледот веќе работеше оптимално преку примарните клучеви. |