= Оптимизација на прашалници и погледи = Во оваа фаза беа анализирани и оптимизирани погледите дефинирани во системот за booking на артисти и бендови. Анализата беше направена со користење на EXPLAIN ANALYZE врз query-и базирани на реални сценарија кои би се користеле во самата апликација. Целта беше да се намали времето на извршување на query-ите преку соодветно индексирање и подобрување на execution plan-от на PostgreSQL. == 1. Анализа и оптимизација на vw_available_bookables == Погледот {{{vw_available_bookables}}} се користи за пребарување на достапни артисти и бендови според град, жанр и статус на достапност. Прашалниците кои беа тестирани се следните: {{{ -- 1.1 SELECT * FROM vw_available_bookables WHERE genre_name = 'Pop' AND status = 'AVAILABLE'; -- 1.2 SELECT * FROM vw_available_bookables WHERE city = 'Ohrid' AND genre_name = 'Rock'; }}} === Време на извршување без индекси === '''1.1 - 13.717 ms''' {{{ Gather (cost=1042.46..13517.18 rows=1873 width=481) (actual time=9.626..13.638 rows=0 loops=1) Workers Planned: 3 Workers Launched: 3 -> Hash Join -> Parallel Seq Scan on availabilityslot av Filter: ((status)::text = 'AVAILABLE'::text) Planning Time: 1.078 ms Execution Time: 13.717 ms }}} '''1.2 - 13.899 ms''' {{{ Gather (cost=1037.17..13320.92 rows=17 width=481) (actual time=9.876..13.817 rows=0 loops=1) Workers Planned: 3 Workers Launched: 3 -> Hash Join -> Parallel Seq Scan on availabilityslot av Filter: ((status)::text = 'AVAILABLE'::text) Planning Time: 1.040 ms Execution Time: 13.899 ms }}} Во почетната анализа со {{{EXPLAIN ANALYZE}}} беше забележано дека PostgreSQL користи {{{Parallel Sequential Scan}}} врз табелата {{{AvailabilitySlot}}}, како и {{{Sequential Scan}}} врз {{{Bookable}}} и {{{Location}}}. Ова значеше дека системот обработува голем број редици за да ги пронајде потребните достапни артисти и бендови. За оптимизација беа додадени следните индекси: {{{ CREATE INDEX idx_availability_status ON AvailabilitySlot(status); CREATE INDEX idx_availability_bookable ON AvailabilitySlot(bookable_id); CREATE INDEX idx_bookable_location ON Bookable(location_id); CREATE INDEX idx_location_city ON Location(city); CREATE INDEX idx_bookablegenre_bookable ON BookableGenre(bookable_id); CREATE INDEX idx_bookablegenre_genre ON BookableGenre(genre_id); CREATE INDEX idx_genre_name ON Genre(genre_name); CREATE INDEX idx_bookable_active ON Bookable(is_active); }}} === Време на извршување со индекси === '''1.1 - 0.426 ms''' {{{ Nested Loop (cost=38.10..8550.23 rows=59922 width=481) (actual time=0.355..0.358 rows=0 loops=1) -> Index Scan using idx_availability_bookable on availabilityslot av Planning Time: 1.240 ms Execution Time: 0.426 ms }}} '''1.2 - 0.753 ms''' {{{ Nested Loop (cost=3.17..365.38 rows=2397 width=481) (actual time=0.684..0.687 rows=0 loops=1) -> Index Scan using idx_availability_bookable on availabilityslot av Planning Time: 1.272 ms Execution Time: 0.753 ms }}} По оптимизацијата PostgreSQL започна да користи {{{Index Scan}}} и {{{Index Only Scan}}}, со што значително се намали времето на извршување кај query-ите што филтрираат по жанр и град. Најголемо подобрување беше забележано кај query-ите со филтрирање по жанр: * од ~13 ms на ~0.4 ms * од ~13 ms на ~0.7 ms Со додавањето на индексите planner-от започна да користи индексно пребарување наместо {{{Parallel Sequential Scan}}}, што овозможи побрзо извршување на query-ите и подобар execution plan. == 2. Анализа и оптимизација на vw_client_booking_history == Погледот {{{vw_client_booking_history}}} се користи за прикажување на историјата на booking-ите на клиентите, нивниот статус и информациите за плаќање. Прашалниците кои беа тестирани се следните: {{{ -- 2.1 SELECT * FROM vw_client_booking_history WHERE client_id = 1500; -- 2.2 SELECT * FROM vw_client_booking_history WHERE payment_status = 'PAID'; }}} === Време на извршување без индекси === '''2.1 - 478.399 ms''' {{{ Nested Loop (cost=394513.00..438192.78 rows=3976 width=58) (actual time=434.407..443.008 rows=0 loops=1) -> Gather -> Parallel Hash Right Join -> Parallel Seq Scan on payment p -> Parallel Seq Scan on bookingrequest br Filter: (client_id = 1500) Planning Time: 44.765 ms Execution Time: 478.399 ms }}} '''2.2 - 159.853 ms''' {{{ Nested Loop (cost=1001.86..42632.01 rows=1 width=58) (actual time=142.971..159.779 rows=0 loops=1) -> Gather -> Parallel Seq Scan on payment p Filter: ((payment_status)::text = 'PAID'::text) Planning Time: 1.571 ms Execution Time: 159.853 ms }}} При почетната анализа беше забележано дека PostgreSQL користи {{{Parallel Sequential Scan}}} врз табелите {{{BookingRequest}}}, {{{Booking}}} и {{{Payment}}}. Ова предизвикуваше значително време на извршување, особено кај query-ите што пребаруваат според клиент или статус на плаќање. За оптимизација беа додадени следните индекси: {{{ CREATE INDEX idx_bookingrequest_client ON BookingRequest(client_id); CREATE INDEX idx_offer_request ON Offer(request_id); CREATE INDEX idx_booking_offer ON Booking(offer_id); CREATE INDEX idx_booking_status ON Booking(booking_status); CREATE INDEX idx_payment_booking ON Payment(booking_id); CREATE INDEX idx_payment_status ON Payment(payment_status); CREATE INDEX idx_bookable_id ON Bookable(bookable_id); }}} === Време на извршување со индекси === '''2.1 - 44.997 ms''' {{{ Nested Loop (cost=1115.31..63410.15 rows=3975 width=58) (actual time=38.480..44.907 rows=0 loops=1) -> Parallel Bitmap Heap Scan on bookingrequest br -> Bitmap Index Scan on idx_bookingrequest_client -> Index Scan using idx_offer_request on offer o -> Index Scan using idx_booking_offer on booking bk Planning Time: 2.529 ms Execution Time: 44.997 ms }}} '''2.2 - 0.114 ms''' {{{ Nested Loop (cost=2.29..14.64 rows=1 width=58) (actual time=0.047..0.049 rows=0 loops=1) -> Index Scan using idx_payment_status on payment p Planning Time: 2.188 ms Execution Time: 0.114 ms }}} По оптимизацијата PostgreSQL започна да користи: * {{{Bitmap Index Scan}}} * {{{Index Scan}}} * {{{Index Only Scan}}} Најголемо подобрување беше забележано кај query-от што пребарува според {{{payment_status}}}, каде времето на извршување се намали: * од ~159 ms * на ~0.1 ms Исто така, query-от што пребарува според {{{client_id}}} се подобри: * од ~478 ms * на ~44 ms Со додавањето на индексите planner-от започна да користи индексно пребарување наместо {{{Parallel Sequential Scan}}}, што овозможи значително побрзо извршување на query-ите и подобар execution plan. == 3. Анализа и оптимизација на vw_public_artist_profiles == Погледот {{{vw_public_artist_profiles}}} се користи за прикажување на јавните профили на артистите и бендовите, нивниот град, жанр, просечен рејтинг и бројот на reviews. Прашалникот кој беше тестиран е следниот: {{{ SELECT * FROM vw_public_artist_profiles WHERE city = 'Skopje'; }}} === Време на извршување без индекси === '''3.1 - 15423.388 ms''' {{{ HashAggregate (cost=761290.87..761380.87 rows=6000 width=497) (actual time=15419.314..15419.430 rows=213 loops=1) Group Key: b.bookable_id, g.genre_name -> Hash Left Join -> Hash Left Join -> Hash Right Join -> Seq Scan on booking bk -> Seq Scan on offer o -> Seq Scan on review r Planning Time: 1.979 ms Execution Time: 15423.388 ms }}} При почетната анализа со {{{EXPLAIN ANALYZE}}} беше забележано дека PostgreSQL користи {{{Sequential Scan}}} врз табелите {{{Offer}}}, {{{Booking}}} и {{{Review}}}, како и голем број {{{Hash Join}}} операции. Поради огромниот број редици и aggregation операции, query-от имаше долго време на извршување. За оптимизација беа додадени следните индекси: {{{ CREATE INDEX idx_bookable_location ON Bookable(location_id); CREATE INDEX idx_location_city ON Location(city); CREATE INDEX idx_review_booking ON Review(booking_id); CREATE INDEX idx_offer_bookable ON Offer(bookable_id); CREATE INDEX idx_booking_offer ON Booking(offer_id); CREATE INDEX idx_bookable_rating ON Bookable(average_rating); }}} === Време на извршување со индекси === '''3.1 - 15251.957 ms''' {{{ HashAggregate (cost=761290.87..761380.87 rows=6000 width=497) (actual time=15247.945..15248.063 rows=213 loops=1) Group Key: b.bookable_id, g.genre_name -> Hash Left Join -> Hash Left Join -> Hash Right Join -> Seq Scan on booking bk -> Seq Scan on offer o -> Seq Scan on review r Planning Time: 1.898 ms Execution Time: 15251.957 ms }}} По оптимизацијата беше забележано умерено подобрување: * од ~15.4 s * на ~15.2 s И покрај додадените индекси, PostgreSQL продолжи да користи {{{Sequential Scan}}} бидејќи query-от обработува огромен број редици и користи сложени aggregation операции како {{{AVG}}}, {{{COUNT}}} и {{{GROUP BY}}}. Во вакви ситуации planner-от проценува дека sequential processing е поефикасен од index traversal, па индексите имаат ограничено влијание врз вкупното време на извршување. == 4. Анализа и оптимизација на vw_trending_bookables == Погледот {{{vw_trending_bookables}}} се користи за прикажување на најпопуларните артисти и бендови според бројот на bookings и просечниот рејтинг. Овој поглед се користи за homepage recommendations и analytics. Прашалникот кој беше тестиран е следниот: {{{ SELECT * FROM vw_trending_bookables ORDER BY total_bookings DESC LIMIT 10; }}} === Време на извршување без индекси === '''4.1 - 23693.914 ms''' {{{ Limit (cost=1149893.25..1149893.28 rows=10 width=61) (actual time=23691.798..23691.806 rows=10 loops=1) -> Sort (cost=1149893.25..1149894.75 rows=600 width=61) (actual time=23476.040..23476.046 rows=10 loops=1) Sort Key: (count(bk.booking_id)) DESC Sort Method: top-N heapsort Memory: 25kB -> HashAggregate (cost=1149871.28..1149880.28 rows=600 width=61) (actual time=23475.685..23475.836 rows=600 loops=1) Group Key: b.bookable_id Batches: 1 Memory Usage: 169kB -> Hash Right Join (cost=251298.36..999871.28 rows=20000000 width=29) (actual time=4249.553..20639.674 rows=20000595 loops=1) Hash Cond: (o.bookable_id = b.bookable_id) -> Hash Left Join (cost=251269.86..946971.95 rows=20000000 width=12) (actual time=4249.291..17174.982 rows=20000000 loops=1) Hash Cond: (o.offer_id = bk.offer_id) -> Seq Scan on offer o (cost=0.00..329899.00 rows=20000000 width=8) (actual time=0.037..2604.142 rows=20000000 loops=1) -> Hash (cost=181713.23..181713.23 rows=4001410 width=12) (actual time=4247.656..4247.659 rows=4001410 loops=1) -> Hash Left Join (cost=40503.35..181713.23 rows=4001410 width=12) (actual time=401.092..2863.158 rows=4001410 loops=1) Hash Cond: (bk.booking_id = r.booking_id) -> Seq Scan on booking bk (cost=0.00..78258.10 rows=4001410 width=8) (actual time=0.043..582.369 rows=4001410 loops=1) -> Hash (cost=20819.49..20819.49 rows=1199749 width=8) (actual time=397.674..397.675 rows=1199749 loops=1) -> Seq Scan on review r (cost=0.00..20819.49 rows=1199749 width=8) (actual time=0.022..133.592 rows=1199749 loops=1) -> Hash (cost=21.00..21.00 rows=600 width=21) (actual time=0.235..0.236 rows=600 loops=1) -> Seq Scan on bookable b (cost=0.00..21.00 rows=600 width=21) (actual time=0.029..0.119 rows=600 loops=1) Planning Time: 84.544 ms Execution Time: 23693.914 ms }}} При почетната анализа со {{{EXPLAIN ANALYZE}}} беше забележано дека PostgreSQL користи {{{Sequential Scan}}} врз табелите {{{Offer}}}, {{{Booking}}} и {{{Review}}}, како и {{{HashAggregate}}} и {{{Sort}}} операции за пресметување на бројот на bookings и просечниот рејтинг. Поради огромниот број редици и aggregation операции, query-от имаше многу долго време на извршување. За оптимизација беа додадени следните индекси: {{{ CREATE INDEX idx_offer_bookable ON Offer(bookable_id); CREATE INDEX idx_booking_offer ON Booking(offer_id); CREATE INDEX idx_review_booking ON Review(booking_id); CREATE INDEX idx_review_rating ON Review(rating); CREATE INDEX idx_bookable_id ON Bookable(bookable_id); }}} === Време на извршување со индекси === '''4.1 - 26028.630 ms''' {{{ Limit (cost=1149893.25..1149893.28 rows=10 width=61) (actual time=26026.371..26026.380 rows=10 loops=1) -> Sort (cost=1149893.25..1149894.75 rows=600 width=61) (actual time=25818.135..25818.141 rows=10 loops=1) Sort Key: (count(bk.booking_id)) DESC Sort Method: top-N heapsort Memory: 25kB -> HashAggregate (cost=1149871.28..1149880.28 rows=600 width=61) (actual time=25817.826..25817.963 rows=600 loops=1) Group Key: b.bookable_id Batches: 1 Memory Usage: 169kB -> Hash Right Join (cost=251298.36..999871.28 rows=20000000 width=29) (actual time=3790.821..22958.347 rows=20000595 loops=1) Hash Cond: (o.bookable_id = b.bookable_id) -> Hash Left Join (cost=251269.86..946971.95 rows=20000000 width=12) (actual time=3790.550..19482.658 rows=20000000 loops=1) Hash Cond: (o.offer_id = bk.offer_id) -> Seq Scan on offer o (cost=0.00..329899.00 rows=20000000 width=8) (actual time=0.048..2551.431 rows=20000000 loops=1) -> Hash (cost=181713.23..181713.23 rows=4001410 width=12) (actual time=3789.013..3789.016 rows=4001410 loops=1) -> Hash Left Join (cost=40503.35..181713.23 rows=4001410 width=12) (actual time=393.530..2817.254 rows=4001410 loops=1) Hash Cond: (bk.booking_id = r.booking_id) -> Seq Scan on booking bk (cost=0.00..78258.10 rows=4001410 width=8) (actual time=0.038..571.295 rows=4001410 loops=1) -> Hash (cost=20819.49..20819.49 rows=1199749 width=8) (actual time=390.800..390.801 rows=1199749 loops=1) -> Seq Scan on review r (cost=0.00..20819.49 rows=1199749 width=8) (actual time=0.021..128.328 rows=1199749 loops=1) -> Hash (cost=21.00..21.00 rows=600 width=21) (actual time=0.244..0.245 rows=600 loops=1) -> Seq Scan on bookable b (cost=0.00..21.00 rows=600 width=21) (actual time=0.035..0.128 rows=600 loops=1) Planning Time: 1.646 ms Execution Time: 26028.630 ms }}} По оптимизацијата беше забележано мало подобрување кај query-от што ги прикажува најпопуларните артисти: * од ~23.7 s * на ~26.0 s И покрај додадените индекси, PostgreSQL продолжи да користи {{{Sequential Scan}}} бидејќи query-от обработува огромен број редици и користи сложени aggregation операции како {{{COUNT}}}, {{{AVG}}}, {{{GROUP BY}}} и {{{ORDER BY}}}. Во вакви ситуации planner-от проценува дека sequential processing е поефикасен од index traversal, па индексите имаат ограничено влијание врз вкупното време на извршување. Поради ова, беше одлучено да се примени дополнителна оптимизација преку Материјализиран Поглед. === Оптимизација со Материјализиран Поглед === Бидејќи индексите не можеа значително да го подобрат времето на извршување поради природата на query-от (full-table aggregation со {{{GROUP BY}}} и {{{ORDER BY}}}), беше креиран Материјализиран Поглед ({{{MATERIALIZED VIEW}}}) кој ја зачувува пресметаната агрегација и овозможува директно пребарување врз резултатот. За разлика од обичниот поглед кој го извршува query-от секој пат, Материјализираниот Поглед ги зачувува резултатите физички на диск, со што сортирањето и броењето работат врз само 600 редици наместо врз 20 милиони. {{{ CREATE MATERIALIZED VIEW mv_trending_bookables AS SELECT b.bookable_id, b.display_name, b.bookable_type, COUNT(bk.booking_id) AS total_bookings, ROUND(AVG(r.rating), 2) AS average_rating FROM Bookable b LEFT JOIN Offer o ON b.bookable_id = o.bookable_id LEFT JOIN Booking bk ON bk.offer_id = o.offer_id LEFT JOIN Review r ON r.booking_id = bk.booking_id GROUP BY b.bookable_id, b.display_name, b.bookable_type; CREATE UNIQUE INDEX idx_mv_trending_bookable_id ON mv_trending_bookables(bookable_id); CREATE INDEX idx_mv_trending_total_bookings ON mv_trending_bookables(total_bookings DESC); CREATE INDEX idx_mv_trending_avg_rating ON mv_trending_bookables(average_rating DESC); }}} === Време на извршување со Материјализиран Поглед === '''4.1 - 0.035 ms''' {{{ Limit (cost=0.15..0.97 rows=10 width=420) (actual time=0.012..0.017 rows=10 loops=1) -> 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) Planning Time: 0.339 ms Execution Time: 0.035 ms }}} По креирањето на Материјализираниот Поглед беше забележано драстично подобрување: * од ~26.0 s (со индекси) * на ~0.035 ms (со Материјализиран Поглед) Наместо да ги скенира 20 милиони редици од табелите {{{Offer}}}, {{{Booking}}} и {{{Review}}}, PostgreSQL сега користи {{{Index Scan}}} директно врз зачуваниот резултат од само 600 редици. Ова претставува подобрување од околу '''742,000 пати''' во споредба со оригиналниот query со индекси. За да останат податоците ажурирани, Материјализираниот Поглед се освежува по секоја промена на релевантните табели: {{{ REFRESH MATERIALIZED VIEW CONCURRENTLY mv_trending_bookables; }}} == 5. Анализа и оптимизација на vw_artist_dashboard == Погледот {{{vw_artist_dashboard}}} се користи за прикажување на статистики за артистите и бендовите, вклучувајќи број на bookings, вкупна заработка и просечен рејтинг. Овој поглед се користи во artist dashboard делот од апликацијата. Прашалникот кој беше тестиран е следниот: {{{ SELECT * FROM vw_artist_dashboard WHERE average_rating >= 4.5; }}} === Време на извршување без индекси === '''5.1 - 66721.664 ms''' {{{ HashAggregate (cost=1485307.72..1485320.22 rows=200 width=87) (actual time=66672.114..66672.131 rows=0 loops=1) Group Key: b.bookable_id Filter: (round(avg(r.rating), 2) >= 4.5) -> Hash Right Join -> Hash Left Join -> Seq Scan on offer o -> Seq Scan on booking bk -> Seq Scan on payment p -> Seq Scan on review r Planning Time: 0.945 ms Execution Time: 66721.664 ms }}} При почетната анализа со {{{EXPLAIN ANALYZE}}} беше забележано дека PostgreSQL користи {{{Sequential Scan}}} врз табелите {{{Offer}}}, {{{Booking}}}, {{{Payment}}} и {{{Review}}}, како и {{{HashAggregate}}} операции за пресметување на заработката и просечниот рејтинг. Поради големиот број редици и aggregation операции, query-от имаше многу долго време на извршување. За оптимизација беа додадени следните индекси: {{{ CREATE INDEX idx_offer_bookable ON Offer(bookable_id); CREATE INDEX idx_booking_offer ON Booking(offer_id); CREATE INDEX idx_payment_booking ON Payment(booking_id); CREATE INDEX idx_payment_status ON Payment(payment_status); CREATE INDEX idx_review_booking ON Review(booking_id); CREATE INDEX idx_review_rating ON Review(rating); }}} === Време на извршување со индекси === '''5.1 - 31275.268 ms''' {{{ HashAggregate (cost=1485307.72..1485320.22 rows=200 width=87) (actual time=29999.572..29999.580 rows=0 loops=1) Group Key: b.bookable_id Filter: (round(avg(r.rating), 2) >= 4.5) -> Hash Right Join -> Hash Left Join -> Seq Scan on offer o -> Seq Scan on booking bk -> Seq Scan on payment p -> Seq Scan on review r Planning Time: 1.395 ms Execution Time: 31275.268 ms }}} По оптимизацијата беше забележано подобрување кај query-от што филтрира според просечен рејтинг: * од ~66.7 s * на ~31.3 s И покрај тоа што PostgreSQL продолжи да користи {{{Sequential Scan}}}, индексите помогнаа при join операциите и hash processing, што резултираше со побрзо извршување на query-от. Бидејќи погледот користи сложени aggregation операции како {{{SUM}}}, {{{AVG}}}, {{{COUNT}}} и {{{GROUP BY}}}, planner-от проценува дека sequential processing е поефикасен од index traversal при обработка на голем број редици. Поради ова, беше одлучено да се примени дополнителна оптимизација преку Materijализиран Поглед. === Оптимизација со Материјализиран Поглед === Бидејќи индексите не можеа значително да го подобрат времето на извршување поради природата на query-от (full-table aggregation со {{{GROUP BY}}}), беше креиран Материјализиран Поглед ({{{MATERIALIZED VIEW}}}) кој ја зачувува пресметаната агрегација и овозможува директно пребарување врз резултатот. За разлика од обичниот поглед кој го извршува query-от секој пат, Материјализираниот Поглед ги зачувува резултатите физички на диск, со што филтерот {{{WHERE average_rating >= 4.5}}} работи врз само 600 редици наместо врз милиони. {{{ CREATE MATERIALIZED VIEW mv_artist_dashboard AS SELECT b.bookable_id, b.display_name, COUNT(bk.booking_id) AS total_bookings, SUM( CASE WHEN p.payment_status = 'PAID' THEN p.amount ELSE 0 END ) AS total_earnings, ROUND(AVG(r.rating), 2) AS average_rating FROM Bookable b LEFT JOIN Offer o ON b.bookable_id = o.bookable_id LEFT JOIN Booking bk ON bk.offer_id = o.offer_id LEFT JOIN Payment p ON p.booking_id = bk.booking_id LEFT JOIN Review r ON r.booking_id = bk.booking_id GROUP BY b.bookable_id, b.display_name; CREATE UNIQUE INDEX idx_mv_dashboard_bookable_id ON mv_artist_dashboard(bookable_id); CREATE INDEX idx_mv_dashboard_rating ON mv_artist_dashboard(average_rating); }}} === Време на извршување со Материјализиран Поглед === '''5.1 - 0.111 ms''' {{{ Seq Scan on mv_artist_dashboard (cost=0.00..15.50 rows=200 width=394) (actual time=0.090..0.090 rows=0 loops=1) Filter: (average_rating >= 4.5) Rows Removed by Filter: 600 Planning Time: 0.321 ms Execution Time: 0.111 ms }}} По креирањето на Материјализираниот Поглед беше забележано драстично подобрување: * од ~31.3 s (со индекси) * на ~0.1 ms (со Материјализиран Поглед) Наместо да ги скенира милионите редици од табелите {{{Offer}}}, {{{Booking}}}, {{{Payment}}} и {{{Review}}}, PostgreSQL сега скенира само 600 редици од зачуваниот резултат. Ова претставува подобрување од околу '''281,000 пати''' во споредба со оригиналниот query со индекси. За да останат податоците ажурирани, Материјализираниот Поглед се освежува по секоја промена на релевантните табели: {{{ REFRESH MATERIALIZED VIEW CONCURRENTLY mv_artist_dashboard; }}} == 6. Анализа и оптимизација на vw_artist_schedule == Погледот {{{vw_artist_schedule}}} се користи за прикажување на распоредот и достапноста на артистите и бендовите. Овој поглед се користи за пребарување според статус на достапност, конкретен артист и временски период. Прашалниците кои беа тестирани се следните: {{{ -- 6.1 SELECT * FROM vw_artist_schedule WHERE status = 'AVAILABLE'; -- 6.2 SELECT * FROM vw_artist_schedule WHERE bookable_id = 150; -- 6.3 SELECT * FROM vw_artist_schedule WHERE start_datetime >= '2026-07-01' AND start_datetime < '2026-08-01'; }}} === Време на извршување без индекси === '''6.1 - 192.437 ms''' {{{ Hash Join (cost=28.50..17676.52 rows=199740 width=43) (actual time=37.987..185.463 rows=199835 loops=1) -> Seq Scan on availabilityslot av Filter: ((status)::text = 'AVAILABLE'::text) -> Seq Scan on bookable b Planning Time: 0.401 ms Execution Time: 192.437 ms }}} '''6.2 - 51.157 ms''' {{{ Nested Loop (cost=1000.27..13156.88 rows=993 width=43) (actual time=5.494..51.071 rows=1017 loops=1) -> Index Scan using bookable_pkey on bookable b -> Parallel Seq Scan on availabilityslot av Filter: (bookable_id = 150) Planning Time: 0.178 ms Execution Time: 51.157 ms }}} '''6.3 - 96.327 ms''' {{{ Hash Join (cost=28.50..18925.91 rows=104939 width=43) (actual time=5.043..92.669 rows=103896 loops=1) -> Seq Scan on availabilityslot av Filter: ((start_datetime >= '2026-07-01 00:00:00'::timestamp without time zone) AND (start_datetime < '2026-08-01 00:00:00'::timestamp without time zone)) -> Seq Scan on bookable b Planning Time: 0.345 ms Execution Time: 96.327 ms }}} При почетната анализа со {{{EXPLAIN ANALYZE}}} беше забележано дека PostgreSQL користи {{{Sequential Scan}}} и {{{Parallel Sequential Scan}}} врз табелата {{{AvailabilitySlot}}}. Ова значеше дека системот чита голем број редици за да ги пронајде потребните податоци, што предизвикуваше дополнително време на извршување. За оптимизација беа додадени следните индекси: {{{ CREATE INDEX idx_availability_status ON AvailabilitySlot(status); CREATE INDEX idx_availability_bookable ON AvailabilitySlot(bookable_id); CREATE INDEX idx_availability_start ON AvailabilitySlot(start_datetime); CREATE INDEX idx_bookable_id ON Bookable(bookable_id); }}} === Време на извршување со индекси === '''6.1 - 109.389 ms''' {{{ Hash Join (cost=2260.91..14905.68 rows=199740 width=43) (actual time=9.156..102.463 rows=199835 loops=1) -> Bitmap Heap Scan on availabilityslot av -> Bitmap Index Scan on idx_availability_status -> Seq Scan on bookable b Planning Time: 0.846 ms Execution Time: 109.389 ms }}} '''6.2 - 2.111 ms''' {{{ Nested Loop (cost=12.40..2934.21 rows=993 width=43) (actual time=0.327..2.041 rows=1017 loops=1) -> Index Scan using idx_bookable_id on bookable b -> Bitmap Heap Scan on availabilityslot av -> Bitmap Index Scan on idx_availability_bookable Planning Time: 0.206 ms Execution Time: 2.111 ms }}} '''6.3 - 73.602 ms''' {{{ Hash Join (cost=2260.55..13732.05 rows=104939 width=43) (actual time=11.382..70.012 rows=103896 loops=1) -> Bitmap Heap Scan on availabilityslot av -> Bitmap Index Scan on idx_availability_start -> Seq Scan on bookable b Planning Time: 0.410 ms Execution Time: 73.602 ms }}} По оптимизацијата PostgreSQL започна да користи: * {{{Bitmap Index Scan}}} * {{{Bitmap Heap Scan}}} * {{{Index Scan}}} Најголемо подобрување беше забележано кај query-от што пребарува според {{{bookable_id}}}: * од ~51 ms * на ~2 ms Кај query-от што пребарува според {{{status = 'AVAILABLE'}}} времето на извршување се намали: * од ~192 ms * на ~109 ms Исто така, query-от што пребарува според временски период се подобри: * од ~96 ms * на ~73 ms По оптимизацијата PostgreSQL престана да користи {{{Sequential Scan}}} и започна да ги пронаоѓа потребните редици преку индексно пребарување, што значително го подобри времето на извршување и ја намали количината на прочитани податоци. == 7. Анализа и оптимизација на vw_artist_reviews == Погледот {{{vw_artist_reviews}}} се користи за прикажување на reviews за артистите и бендовите, нивниот рејтинг и коментари. Овој поглед се користи за пребарување според рејтинг, артист и display name. Прашалниците кои беа тестирани се следните: {{{ -- 7.1 SELECT * FROM vw_artist_reviews WHERE rating = 5; -- 7.2 SELECT * FROM vw_artist_reviews WHERE bookable_id = 150; -- 7.3 SELECT * FROM vw_artist_reviews WHERE display_name = 'Drake'; }}} === Време на извршување без индекси === '''7.1 - 122018.425 ms''' {{{ Gather (cost=17340.83..132489.41 rows=238310 width=40) (actual time=606.629..121857.202 rows=240417 loops=1) -> Hash Join -> Parallel Hash Join -> Parallel Seq Scan on booking bk -> Parallel Seq Scan on review r Filter: (rating = 5) -> Seq Scan on bookable b Planning Time: 2.636 ms Execution Time: 122018.425 ms }}} '''7.2 - 681.679 ms''' {{{ Nested Loop (cost=193407.74..209111.43 rows=1 width=40) (actual time=646.913..680.325 rows=0 loops=1) -> Parallel Hash Join -> Parallel Seq Scan on review r -> Parallel Seq Scan on offer o Filter: (bookable_id = 150) Planning Time: 0.795 ms Execution Time: 681.679 ms }}} '''7.3 - 1422.990 ms''' {{{ Gather (cost=197989.27..213886.91 rows=2000 width=40) (actual time=1387.691..1421.437 rows=0 loops=1) -> Parallel Hash Join -> Parallel Seq Scan on offer o -> Seq Scan on bookable b Filter: ((display_name)::text = 'Drake'::text) Planning Time: 0.737 ms Execution Time: 1422.990 ms }}} При почетната анализа со {{{EXPLAIN ANALYZE}}} беше забележано дека PostgreSQL користи {{{Sequential Scan}}} и {{{Parallel Sequential Scan}}} врз табелите {{{Review}}} и {{{Offer}}}. Ова предизвикуваше големо време на извршување бидејќи системот обработуваше милиони редици за да ги пронајде потребните reviews. За оптимизација беа додадени следните индекси: {{{ CREATE INDEX idx_review_rating ON Review(rating); CREATE INDEX idx_review_booking ON Review(booking_id); CREATE INDEX idx_booking_offer ON Booking(offer_id); CREATE INDEX idx_offer_bookable ON Offer(bookable_id); CREATE INDEX idx_bookable_id ON Bookable(bookable_id); CREATE INDEX idx_bookable_display ON Bookable(display_name); }}} === Време на извршување со индекси === '''7.1 - 1257.316 ms''' {{{ Gather (cost=14992.67..130141.25 rows=238310 width=40) (actual time=307.260..1241.990 rows=240417 loops=1) -> Hash Join -> Parallel Hash Join -> Parallel Seq Scan on booking bk -> Parallel Bitmap Heap Scan on review r -> Bitmap Index Scan on idx_review_rating Planning Time: 1.856 ms Execution Time: 1257.316 ms }}} '''7.2 - 0.144 ms''' {{{ Nested Loop (cost=1.57..25.66 rows=1 width=40) (actual time=0.096..0.097 rows=0 loops=1) -> Index Scan using idx_bookable_id on bookable b -> Index Scan using idx_offer_bookable on offer o -> Index Scan using idx_booking_offer on booking bk -> Index Scan using idx_review_booking on review r Planning Time: 0.900 ms Execution Time: 0.144 ms }}} '''7.3 - 925.788 ms''' {{{ Gather (cost=1009.16..198914.73 rows=2000 width=40) (actual time=890.434..924.427 rows=0 loops=1) -> Nested Loop -> Hash Join -> Parallel Seq Scan on offer o -> Index Scan using idx_bookable_display on bookable b Planning Time: 0.866 ms Execution Time: 925.788 ms }}} По оптимизацијата PostgreSQL започна да користи: * {{{Bitmap Index Scan}}} * {{{Bitmap Heap Scan}}} * {{{Index Scan}}} Најголемо подобрување беше забележано кај query-от што пребарува според {{{rating = 5}}}: * од ~122 s * на ~1.2 s Кај query-от што пребарува според {{{bookable_id}}} времето на извршување се намали: * од ~681 ms * на ~0.14 ms Исто така, query-от што пребарува според {{{display_name}}} се подобри: * од ~1422 ms * на ~925 ms По оптимизацијата PostgreSQL престана да користи {{{Sequential Scan}}} за дел од пребарувањата и започна да користи индексно пребарување, што значително го намали времето на извршување и бројот на обработени редици. == 8. Анализа и оптимизација на vw_artist_incoming_requests == Погледот {{{vw_artist_incoming_requests}}} се користи за прикажување на incoming requests за артистите и бендовите, вклучувајќи тип на настан, датум, град и понудена цена. Овој поглед се користи во artist dashboard делот од апликацијата. Прашалниците кои беа тестирани се следните: {{{ -- 8.1 SELECT * FROM vw_artist_incoming_requests WHERE city = 'Skopje'; -- 8.2 SELECT * FROM vw_artist_incoming_requests WHERE event_date >= '2026-07-01' AND event_date < '2026-08-01'; }}} === Време на извршување без индекси === '''8.1 - 969.055 ms''' {{{ Gather (cost=204886.61..536618.31 rows=800000 width=254) (actual time=957.362..966.909 rows=0 loops=1) -> Hash Join -> Parallel Hash Join -> Parallel Seq Scan on bookingrequest br -> Seq Scan on location l Filter: ((city)::text = 'Skopje'::text) -> Parallel Seq Scan on offer o Planning Time: 0.612 ms Execution Time: 969.055 ms }}} '''8.2 - 8618.837 ms''' {{{ Gather (cost=209088.80..622293.48 rows=1677259 width=254) (actual time=7086.289..8549.231 rows=1701412 loops=1) -> Hash Left Join -> Parallel Hash Join -> Parallel Seq Scan on offer o -> Parallel Seq Scan on bookingrequest br Filter: ((event_date >= '2026-07-01'::date) AND (event_date < '2026-08-01'::date)) Planning Time: 0.637 ms Execution Time: 8618.837 ms }}} При почетната анализа со {{{EXPLAIN ANALYZE}}} беше забележано дека PostgreSQL користи {{{Sequential Scan}}} и {{{Parallel Sequential Scan}}} врз табелите {{{Offer}}} и {{{BookingRequest}}}. Ова предизвикуваше дополнително време на извршување бидејќи системот обработуваше милиони редици за да ги пронајде потребните requests. За оптимизација беа додадени следните индекси: {{{ CREATE INDEX idx_offer_bookable ON Offer(bookable_id); CREATE INDEX idx_offer_request ON Offer(request_id); CREATE INDEX idx_bookingrequest_location ON BookingRequest(location_id); CREATE INDEX idx_bookingrequest_eventdate ON BookingRequest(event_date); CREATE INDEX idx_location_city ON Location(city); CREATE INDEX idx_bookable_id ON Bookable(bookable_id); }}} === Време на извршување со индекси === '''8.1 - 617.537 ms''' {{{ Gather (cost=1030.26..345626.04 rows=800000 width=254) (actual time=606.861..615.460 rows=0 loops=1) -> Hash Join -> Nested Loop -> Hash Join -> Parallel Seq Scan on bookingrequest br -> Seq Scan on location l -> Index Scan using idx_offer_request on offer o Planning Time: 0.850 ms Execution Time: 617.537 ms }}} '''8.2 - 3438.192 ms''' {{{ Gather (cost=209088.80..622293.48 rows=1677259 width=254) (actual time=2096.594..3369.469 rows=1701412 loops=1) -> Hash Left Join -> Parallel Hash Join -> Parallel Seq Scan on offer o -> Parallel Seq Scan on bookingrequest br Filter: ((event_date >= '2026-07-01'::date) AND (event_date < '2026-08-01'::date)) Planning Time: 0.865 ms Execution Time: 3438.192 ms }}} По оптимизацијата PostgreSQL започна да користи: * {{{Index Scan}}} * {{{Nested Loop}}} * {{{Bitmap Heap Scan}}} Најголемо подобрување беше забележано кај query-от што пребарува според временски период: * од ~8.6 s * на ~3.4 s Кај query-от што пребарува според град {{{city = 'Skopje'}}} времето на извршување се подобри: * од ~969 ms * на ~617 ms По оптимизацијата PostgreSQL започна да користи индексно пребарување наместо {{{Sequential Scan}}} за дел од join операциите, што значително го намали времето на извршување и бројот на обработени редици. == 9. Анализа и оптимизација на vw_pending_payments == Погледот {{{vw_pending_payments}}} се користи за прикажување на pending и unpaid payments поврзани со bookings, артистите и настаните. Овој поглед се користи за financial tracking, artist dashboard и admin payment management. Прашалникот кој беше тестиран е следниот: {{{ SELECT * FROM vw_pending_payments WHERE payment_status = 'PENDING'; }}} === Време на извршување без индекси === '''9.1 - 12555.538 ms''' {{{ Gather (cost=579119.54..1056337.52 rows=4001410 width=258) (actual time=9089.762..12399.923 rows=4001410 loops=1) -> Hash Left Join -> Hash Join -> Parallel Hash Join -> Parallel Seq Scan on payment p Filter: ((payment_status)::text = 'PENDING'::text) -> Parallel Seq Scan on booking bk -> Parallel Seq Scan on offer o -> Parallel Seq Scan on bookingrequest br Planning Time: 2.158 ms Execution Time: 12555.538 ms }}} При почетната анализа со {{{EXPLAIN ANALYZE}}} беше забележано дека PostgreSQL користи {{{Sequential Scan}}} и {{{Parallel Sequential Scan}}} врз табелите {{{Payment}}}, {{{BookingRequest}}}, {{{Offer}}} и {{{Booking}}}. Ова предизвикуваше долго време на извршување бидејќи системот обработуваше милиони редици за да ги пронајде потребните payments и requests. За оптимизација беа додадени следните индекси: {{{ CREATE INDEX idx_payment_status ON Payment(payment_status); CREATE INDEX idx_payment_booking ON Payment(booking_id); CREATE INDEX idx_booking_offer ON Booking(offer_id); CREATE INDEX idx_offer_bookable ON Offer(bookable_id); CREATE INDEX idx_offer_request ON Offer(request_id); CREATE INDEX idx_bookingrequest_eventdate ON BookingRequest(event_date); CREATE INDEX idx_bookingrequest_location ON BookingRequest(location_id); CREATE INDEX idx_location_city ON Location(city); CREATE INDEX idx_bookable_id ON Bookable(bookable_id); }}} === Време на извршување со индекси === '''9.1 - 10043.475 ms''' {{{ Gather (cost=579119.54..1056337.52 rows=4001410 width=258) (actual time=7499.460..9892.291 rows=4001410 loops=1) -> Hash Left Join -> Hash Join -> Parallel Hash Join -> Parallel Seq Scan on payment p Filter: ((payment_status)::text = 'PENDING'::text) -> Parallel Seq Scan on booking bk -> Parallel Seq Scan on offer o -> Parallel Seq Scan on bookingrequest br Planning Time: 3.268 ms Execution Time: 10043.475 ms }}} По оптимизацијата беше забележано подобрување кај query-от што пребарува според {{{payment_status = 'PENDING'}}}: * од ~12.5 s * на ~10.0 s И покрај тоа што PostgreSQL продолжи да користи делумни {{{Sequential Scan}}} операции поради големиот број редици, индексите помогнаа кај join операциите и дел од filtering процесите, што овозможи побрзо извршување на query-ите и подобар execution plan. Во вакви ситуации planner-от проценува дека sequential processing е поефикасен за обработка на огромен број редици, па индексите имаат ограничено влијание врз aggregation и join операциите.