| 334 | | -> Hash Right Join |
| 335 | | -> Hash Left Join |
| 336 | | -> Seq Scan on offer o |
| 337 | | -> Seq Scan on booking bk |
| 338 | | -> Seq Scan on review r |
| 339 | | Planning Time: 1.190 ms |
| 340 | | Execution Time: 22675.664 ms |
| | 331 | Batches: 1 Memory Usage: 169kB |
| | 332 | -> Hash Right Join (cost=251298.36..999871.28 rows=20000000 width=29) (actual time=4249.553..20639.674 rows=20000595 loops=1) |
| | 333 | Hash Cond: (o.bookable_id = b.bookable_id) |
| | 334 | -> Hash Left Join (cost=251269.86..946971.95 rows=20000000 width=12) (actual time=4249.291..17174.982 rows=20000000 loops=1) |
| | 335 | Hash Cond: (o.offer_id = bk.offer_id) |
| | 336 | -> Seq Scan on offer o (cost=0.00..329899.00 rows=20000000 width=8) (actual time=0.037..2604.142 rows=20000000 loops=1) |
| | 337 | -> Hash (cost=181713.23..181713.23 rows=4001410 width=12) (actual time=4247.656..4247.659 rows=4001410 loops=1) |
| | 338 | -> Hash Left Join (cost=40503.35..181713.23 rows=4001410 width=12) (actual time=401.092..2863.158 rows=4001410 loops=1) |
| | 339 | Hash Cond: (bk.booking_id = r.booking_id) |
| | 340 | -> Seq Scan on booking bk (cost=0.00..78258.10 rows=4001410 width=8) (actual time=0.043..582.369 rows=4001410 loops=1) |
| | 341 | -> Hash (cost=20819.49..20819.49 rows=1199749 width=8) (actual time=397.674..397.675 rows=1199749 loops=1) |
| | 342 | -> Seq Scan on review r (cost=0.00..20819.49 rows=1199749 width=8) (actual time=0.022..133.592 rows=1199749 loops=1) |
| | 343 | -> Hash (cost=21.00..21.00 rows=600 width=21) (actual time=0.235..0.236 rows=600 loops=1) |
| | 344 | -> Seq Scan on bookable b (cost=0.00..21.00 rows=600 width=21) (actual time=0.029..0.119 rows=600 loops=1) |
| | 345 | Planning Time: 84.544 ms |
| | 346 | Execution Time: 23693.914 ms |
| 376 | | -> Hash Right Join |
| 377 | | -> Hash Left Join |
| 378 | | -> Seq Scan on offer o |
| 379 | | -> Seq Scan on booking bk |
| 380 | | -> Seq Scan on review r |
| 381 | | Planning Time: 1.633 ms |
| 382 | | Execution Time: 22582.888 ms |
| | 380 | Batches: 1 Memory Usage: 169kB |
| | 381 | -> Hash Right Join (cost=251298.36..999871.28 rows=20000000 width=29) (actual time=3790.821..22958.347 rows=20000595 loops=1) |
| | 382 | Hash Cond: (o.bookable_id = b.bookable_id) |
| | 383 | -> Hash Left Join (cost=251269.86..946971.95 rows=20000000 width=12) (actual time=3790.550..19482.658 rows=20000000 loops=1) |
| | 384 | Hash Cond: (o.offer_id = bk.offer_id) |
| | 385 | -> Seq Scan on offer o (cost=0.00..329899.00 rows=20000000 width=8) (actual time=0.048..2551.431 rows=20000000 loops=1) |
| | 386 | -> Hash (cost=181713.23..181713.23 rows=4001410 width=12) (actual time=3789.013..3789.016 rows=4001410 loops=1) |
| | 387 | -> Hash Left Join (cost=40503.35..181713.23 rows=4001410 width=12) (actual time=393.530..2817.254 rows=4001410 loops=1) |
| | 388 | Hash Cond: (bk.booking_id = r.booking_id) |
| | 389 | -> Seq Scan on booking bk (cost=0.00..78258.10 rows=4001410 width=8) (actual time=0.038..571.295 rows=4001410 loops=1) |
| | 390 | -> Hash (cost=20819.49..20819.49 rows=1199749 width=8) (actual time=390.800..390.801 rows=1199749 loops=1) |
| | 391 | -> Seq Scan on review r (cost=0.00..20819.49 rows=1199749 width=8) (actual time=0.021..128.328 rows=1199749 loops=1) |
| | 392 | -> Hash (cost=21.00..21.00 rows=600 width=21) (actual time=0.244..0.245 rows=600 loops=1) |
| | 393 | -> Seq Scan on bookable b (cost=0.00..21.00 rows=600 width=21) (actual time=0.035..0.128 rows=600 loops=1) |
| | 394 | Planning Time: 1.646 ms |
| | 395 | Execution Time: 26028.630 ms |
| 392 | | Во вакви ситуации planner-от проценува дека sequential processing е поефикасен од index traversal, па индексите имаат ограничено влијание врз вкупното време на извршување. |
| | 404 | Во вакви ситуации planner-от проценува дека sequential processing е поефикасен од index traversal, па индексите имаат ограничено влијание врз вкупното време на извршување. Поради ова, беше одлучено да се примени дополнителна оптимизација преку Материјализиран Поглед. |
| | 405 | |
| | 406 | === Оптимизација со Материјализиран Поглед === |
| | 407 | Бидејќи индексите не можеа значително да го подобрат времето на извршување поради природата на query-от (full-table aggregation со {{{GROUP BY}}} и {{{ORDER BY}}}), беше креиран Материјализиран Поглед ({{{MATERIALIZED VIEW}}}) кој ја зачувува пресметаната агрегација и овозможува директно пребарување врз резултатот. |
| | 408 | |
| | 409 | За разлика од обичниот поглед кој го извршува query-от секој пат, Материјализираниот Поглед ги зачувува резултатите физички на диск, со што сортирањето и броењето работат врз само 600 редици наместо врз 20 милиони. |
| | 410 | {{{ |
| | 411 | CREATE MATERIALIZED VIEW mv_trending_bookables AS |
| | 412 | SELECT |
| | 413 | b.bookable_id, |
| | 414 | b.display_name, |
| | 415 | b.bookable_type, |
| | 416 | COUNT(bk.booking_id) AS total_bookings, |
| | 417 | ROUND(AVG(r.rating), 2) AS average_rating |
| | 418 | FROM Bookable b |
| | 419 | LEFT JOIN Offer o ON b.bookable_id = o.bookable_id |
| | 420 | LEFT JOIN Booking bk ON bk.offer_id = o.offer_id |
| | 421 | LEFT JOIN Review r ON r.booking_id = bk.booking_id |
| | 422 | GROUP BY b.bookable_id, b.display_name, b.bookable_type; |
| | 423 | |
| | 424 | CREATE UNIQUE INDEX idx_mv_trending_bookable_id |
| | 425 | ON mv_trending_bookables(bookable_id); |
| | 426 | |
| | 427 | CREATE INDEX idx_mv_trending_total_bookings |
| | 428 | ON mv_trending_bookables(total_bookings DESC); |
| | 429 | |
| | 430 | CREATE INDEX idx_mv_trending_avg_rating |
| | 431 | ON mv_trending_bookables(average_rating DESC); |
| | 432 | }}} |
| | 433 | |
| | 434 | === Време на извршување со Материјализиран Поглед === |
| | 435 | '''4.1 - 0.035 ms''' |
| | 436 | {{{ |
| | 437 | Limit (cost=0.15..0.97 rows=10 width=420) (actual time=0.012..0.017 rows=10 loops=1) |
| | 438 | -> Index Scan using idx_mv_trending_total_bookings on mv_trending_bookables (cost=0.15..49.15 rows=600 width=420) (actual time=0.011..0.015 rows=10 loops=1) |
| | 439 | Planning Time: 0.339 ms |
| | 440 | Execution Time: 0.035 ms |
| | 441 | }}} |
| | 442 | |
| | 443 | По креирањето на Материјализираниот Поглед беше забележано драстично подобрување: |
| | 444 | * од ~26.0 s (со индекси) |
| | 445 | * на ~0.035 ms (со Материјализиран Поглед) |
| | 446 | |
| | 447 | Наместо да ги скенира 20 милиони редици од табелите {{{Offer}}}, {{{Booking}}} и {{{Review}}}, PostgreSQL сега користи {{{Index Scan}}} директно врз зачуваниот резултат од само 600 редици. Ова претставува подобрување од околу '''742,000 пати''' во споредба со оригиналниот query со индекси. |
| | 448 | |
| | 449 | За да останат податоците ажурирани, Материјализираниот Поглед се освежува по секоја промена на релевантните табели: |
| | 450 | {{{ |
| | 451 | REFRESH MATERIALIZED VIEW CONCURRENTLY mv_trending_bookables; |
| | 452 | }}} |