Changes between Version 1 and Version 2 of QueryOptimization


Ignore:
Timestamp:
06/12/26 20:25:22 (9 days ago)
Author:
231138
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • QueryOptimization

    v1 v2  
    99Во оваа фаза ги анализираме погледите дефинирани во Фаза 2 преку прашалници базирани на реални сценарија кои ќе бидат присутни во нашата апликација и истите ги оптимизираме со помош на индекси.
    1010
    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
     11=== 1. Анализа на поглед 1, достапни возења по град на поаѓање и пристигнување ===
     12
     13Прашалникот кој го тестираме:
     14{{{
     15SELECT * FROM v_available_rides
     16WHERE origin_city = 'Skopje' AND destination_city = 'Bitola';
     17}}}
     18
     19==== Време на извршување без индекси:
     20
     21**72 s 559 ms**
     22
     23{{{
     24Nested Loop Left Join  (cost=38175.80..423608.72 rows=9 width=643)
     25                       (actual time=178.534..72559.294 rows=547.00 loops=1)
     26  ->  Gather  (cost=1023.28..89235.80 rows=9 width=767)
     27              (actual time=1.669..2.593 rows=547.00 loops=3)
     28        Workers Planned: 2
     29        Workers Launched: 2
     30        ->  Nested Loop
     31              ->  Parallel Seq Scan on rides r
     32                    Filter: ((status)::text = 'scheduled'::text)
     33                    Rows Removed by Filter: 1583512
     34              ->  Hash Join
     35                    Hash Cond: (r.route_id = ro.id)
     36                    ->  Index Scan using cities_name_country_key on cities orig_city
     37                          Index Cond: ((name)::text = 'Skopje'::text)
     38                    ->  Index Scan using cities_pkey on cities dest_city
     39                          Filter: ((name)::text = 'Bitola'::text)
     40                          Rows Removed by Filter: 1
     41  ->  Aggregate
     42        ->  Seq Scan on ratings
     43              Filter: (reviewee_user_id = ud.id)
     44              Rows Removed by Filter: 1871226
     45Planning Time: 2.672 ms
     46Execution Time: 72559.907 ms
     47}}}
     48
     49Постојат две тесни грла: (1) `Parallel Seq Scan on rides` скенира сите 5 милиони редови за да ги филтрира само оние со `status = 'scheduled'`; (2) `Seq Scan on ratings` се извршува 547 пати (по еднаш за секој возач во резултатот) и секојпат скенира сите 1,871,226 редови за да ги филтрира оцените по `reviewee_user_id`. Ова е резултат на `LEFT JOIN LATERAL` — агрегацијата се врши по возач, но без индекс секој пат се скенира целата табела. Ги додаваме следните индекси:
     50
     51{{{
     52CREATE INDEX idx_rides_status_dep
    4653    ON rides(status, departure_time);
    4754
    48 CREATE INDEX IF NOT EXISTS idx_ratings_reviewee
     55CREATE INDEX idx_ratings_reviewee
    4956    ON ratings(reviewee_user_id);
    5057}}}
     
    5259==== Време на извршување со индекси:
    5360
    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`.
     61**3 s 980 ms** (беше 72 s 559 ms)
     62
     63{{{
     64Nested Loop Left Join  (cost=4373.93..71603.67 rows=9 width=643)
     65                       (actual time=628.137..3979.211 rows=547.00 loops=1)
     66  ->  Gather  (cost=3902.09..67356.78 rows=9 width=767)
     67              (actual time=122.653..127.176 rows=547.00 loops=1)
     68        Workers Planned: 2
     69        Workers Launched: 2
     70        ->  Parallel Bitmap Heap Scan on rides r
     71              Recheck Cond: ((status)::text = 'scheduled'::text)
     72              ->  Bitmap Index Scan on idx_rides_status_dep
     73                    Index Cond: ((status)::text = 'scheduled'::text)
     74  ->  Aggregate
     75        ->  Bitmap Heap Scan on ratings
     76              Recheck Cond: (reviewee_user_id = ud.id)
     77              ->  Bitmap Index Scan on idx_ratings_reviewee
     78                    Index Cond: (reviewee_user_id = ud.id)
     79Planning Time: 19.415 ms
     80Execution Time: 3980.397 ms
     81}}}
     82
     83`idx_rides_status_dep` го заменува целосниот скен на `rides` со `Bitmap Index Scan` — планерот директно ги наоѓа редовите со `status = 'scheduled'`. `idx_ratings_reviewee` го заменува `Seq Scan on ratings` со `Bitmap Index Scan` по `reviewee_user_id` за секој возач поединечно. Вкупното подобрување е ~18x (од 72.5 s на 3.98 s).
    8084
    8185=== 2. Анализа на поглед 2, профил на возач ===
     
    8387Прашалникот кој го тестираме:
    8488{{{
    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)
     89SELECT * FROM v_driver_profile WHERE driver_id = 1;
     90}}}
     91
     92==== Време на извршување без индекси:
     93
     94**1 s 239 ms**
     95
     96{{{
     97Nested Loop Left Join  (cost=33434.20..122712.76 rows=1 width=207)
     98                       (actual time=1232.657..1239.251 rows=1.00 loops=1)
    9599  ->  Hash Right Join
     100        Hash Cond: (ratings.reviewee_user_id = ud.id)
    96101        ->  Finalize HashAggregate
    97102              Group Key: ratings.reviewee_user_id
    98103              ->  Gather
    99                     Workers Planned: 3
     104                    Workers Planned: 2
    100105                    ->  Partial HashAggregate
    101106                          ->  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))
     107                                (623783 rows — агрегација за сите возачи)
     108        ->  Nested Loop
     109              ->  Index Scan using drivers_pkey on drivers d
     110                    Index Cond: (id = 1)
    106111  ->  Finalize GroupAggregate
    107112        ->  Gather
    108113              ->  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
     114                    Filter: (driver_id = 1)
     115                    Rows Removed by Filter: 1666556
     116  ->  Seq Scan on vehicle_ownership vo
     117        Filter: (is_active AND (driver_id = 1))
     118        Rows Removed by Filter: 17998
     119Planning Time: 3.165 ms
     120Execution Time: 1239.537 ms
     121}}}
     122
     123Постојат три тесни грла: (1) `Parallel Seq Scan on ratings` скенира 623,783 редови за агрегација по `reviewee_user_id` за сите возачи; (2) `Parallel Seq Scan on rides` скенира 5,000,000 редови со `Filter: driver_id = 1`; (3) `Seq Scan on vehicle_ownership` скенира 18,000 редови за да го најде активното возило. Ги додаваме следните индекси:
     124
     125{{{
     126CREATE INDEX idx_drivers_user_id
     127    ON drivers(user_id);
     128
     129CREATE INDEX idx_ratings_reviewee
    122130    ON ratings(reviewee_user_id);
    123131
    124 CREATE INDEX IF NOT EXISTS idx_vo_driver_active
     132CREATE INDEX idx_vo_driver_active
    125133    ON vehicle_ownership(driver_id, is_active, vehicle_id);
    126134}}}
     
    128136==== Време на извршување со индекси:
    129137
    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
     138**1 s 379 ms** (беше 1 s 239 ms)
     139
     140{{{
     141Nested Loop Left Join  (cost=33438.98..121697.48 rows=1 width=207)
     142                       (actual time=1366.020..1378.263 rows=1.00 loops=1)
     143  ->  Hash Right Join
     144        Hash Cond: (ratings.reviewee_user_id = ud.id)
     145        ->  Finalize HashAggregate
     146              Group Key: ratings.reviewee_user_id
     147              ->  Gather
     148                    Workers Planned: 2
     149                    ->  Partial HashAggregate
     150                          ->  Parallel Seq Scan on ratings
     151                                (623783 rows loops=3)
     152        ->  Index Only Scan using idx_vo_driver_active
     153              on vehicle_ownership vo
     154              Index Cond: (driver_id = 1) AND (is_active = true)
     155              Heap Fetches: 0
    151156  ->  Finalize GroupAggregate
    152157        ->  Gather
    153               Workers Planned: 4
     158              Workers Planned: 2
    154159              ->  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)
     160                    Filter: (driver_id = 1)
     161                    Rows Removed by Filter: 1666556
     162Planning Time: 15.218 ms
     163Execution Time: 1379.943 ms
     164}}}
     165
     166`idx_vo_driver_active` е искористен преку `Index Only Scan` за брзо наоѓање на активното возило без пристап до хип. Агрегацијата на `ratings` сепак бара целосен скен бидејќи погледот ги агрегира оцените за сите возачи — ова е агрегација врз цело множество каде индексот не може да ја елиминира потребата за читање на сите редови. `Parallel Seq Scan on rides` исто така останува бидејќи нема индекс по `driver_id` на `rides` (`idx_rides_driver_status` беше отстранет). Подобрувањето е минимално поради доминантната агрегација на `ratings`.
     167
     168=== 3. Анализа на поглед 3, заработка на возач ===
     169
     170Прашалникот кој го тестираме:
     171{{{
     172SELECT * FROM v_driver_earnings WHERE driver_id = 1;
     173}}}
     174
     175==== Време на извршување без индекси:
     176
     177**33 s 186 ms**
     178
     179{{{
     180GroupAggregate  (cost=1447123.50..1447132.37 rows=169 width=195)
     181                (actual time=33086.086..33086.368 rows=47.00 loops=1)
     182  Group Key: ud.name, date_trunc('month', r.departure_time)
     183  ->  Sort
     184        Sort Key: ud.name, date_trunc('month', r.departure_time)
     185        Sort Method: quicksort  Memory: 43kB
     186        ->  Nested Loop
     187              ->  Index Scan using drivers_pkey on drivers d
     188                    Index Cond: (id = 1)
     189              ->  Hash Join  (Hash Cond: b.ride_id = r.id)
     190                    ->  HashAggregate  (Group Key: b.ride_id)
     191                          Planned Partitions: 256  Batches: 257
     192                          Disk Usage: 245528kB
     193                          ->  Hash Join  (Hash Cond: bff.booking_id = b.id)
     194                                ->  Seq Scan on booking_final_fare bff
     195                                      (12000000 rows — целосен скен)
     196                                ->  Seq Scan on bookings b
     197                                      Filter: (status = 'completed')
     198                                      Rows Removed by Filter: 5757941
     199                    ->  Parallel Seq Scan on rides r
     200                          Filter: (driver_id = 1) AND (status = 'completed')
     201                          Rows Removed by Filter: 1666595
     202Planning Time: 41.731 ms
     203Execution Time: 33186.554 ms
     204}}}
     205
     206Постојат две тесни грла: (1) `Seq Scan on bookings` скенира 12 милиони редови за да ги филтрира завршените резервации; (2) `Seq Scan on booking_final_fare` скенира 12 милиони редови за секој JOIN. Планерот мора да ги агрегира сите резервации по возење пред да го примени филтерот по возач — ова е фундаментално ограничување на агрегатниот поглед. Бидејќи погледот врши целосна агрегација, ниеден индекс не може да ја заобиколи потребата за читање на сите редови. Не се додаваат индекси.
     207
     208==== Нема индекси — агрегатен поглед:
     209
     210Времето на извршување останува **~33 s** — индексите не влијаат на перформансите на агрегатни погледи кои мора да ги прочитаат сите редови.
     211
     212=== 4. Анализа на поглед 4, историја на патувања на патник ===
     213
     214Прашалникот кој го тестираме:
     215{{{
     216SELECT * FROM v_passenger_trip_history WHERE passenger_user_id = 1;
     217}}}
     218
     219==== Време на извршување без индекси:
     220
     221**721 ms**
     222
     223{{{
     224Nested Loop Left Join  (cost=1022.14..184161.50 rows=343 width=558)
     225                       (actual time=2.979..721.148 rows=344.00 loops=1)
     226  ->  Gather  (cost=1009.62..183546.43 rows=343 width=110)
     227              (actual time=2.205..511.797 rows=344.00 loops=1)
     228        Workers Planned: 2
     229        Workers Launched: 2
     230        ->  Nested Loop
     231              ->  Hash Join
     232                    Hash Cond: (b.passenger_id = p.id)
     233                    ->  Parallel Seq Scan on bookings b
     234                          (12000001 rows — целосен скен)
     235                    ->  Index Scan using passengers_user_id_key on passengers p
     236                          Index Cond: (user_id = 1)
     237              ->  Index Scan using rides_pkey on rides r
     238                    Index Cond: (id = b.ride_id)
     239  ->  Index Scan using uq_ratings_per_ride on ratings rt
     240        Index Cond: (reviewer_user_id = 1) AND (ride_id = b.ride_id)
     241Planning Time: 6.787 ms
     242Execution Time: 721.475 ms
     243}}}
     244
     245Најбавната операција е `Parallel Seq Scan on bookings` — без индекс базата скенира сите 12,000,000 резервации за секое барање на патник. Иако `passengers_user_id_key` веќе постои (уникатен индекс), нема индекс по `bookings.passenger_id`. Ги додаваме следните индекси:
     246
     247{{{
     248CREATE INDEX idx_bookings_passenger_id
     249    ON bookings(passenger_id);
     250
     251CREATE INDEX idx_passengers_user_id
     252    ON passengers(user_id);
     253
     254CREATE INDEX idx_ratings_reviewer_ride
     255    ON ratings(reviewer_user_id, ride_id);
     256}}}
     257
     258==== Време на извршување со индекси:
     259
     260**562 ms** (беше 721 ms)
     261
     262{{{
     263Nested Loop Left Join  (cost=22.15..2185.57 rows=343 width=558)
     264                       (actual time=2.315..561.965 rows=344.00 loops=1)
    177265  ->  Nested Loop
     266        ->  Nested Loop
     267              ->  Index Scan using idx_passengers_user_id on passengers p
     268                    Index Cond: (user_id = 1)
     269              ->  Bitmap Heap Scan on bookings b
     270                    Recheck Cond: (passenger_id = p.id)
     271                    ->  Bitmap Index Scan on idx_bookings_passenger_id
     272                          Index Cond: (passenger_id = p.id)
     273              ->  Index Scan using rides_pkey on rides r
     274                    Index Cond: (id = b.ride_id)
     275  ->  Index Scan using idx_ratings_reviewer_ride on ratings rt
     276        Index Cond: (reviewer_user_id = 1) AND (ride_id = b.ride_id)
     277Planning Time: 44.836 ms
     278Execution Time: 562.574 ms
     279}}}
     280
     281`idx_bookings_passenger_id` го елиминира целосниот скен на 12 милиони резервации — планерот директно ги наоѓа резервациите на патникот преку `Bitmap Index Scan`. `idx_ratings_reviewer_ride` го решава JOIN за оцените преку еден пристап по индекс наместо скен.
     282
     283=== 5. Анализа на поглед 5, манифест на возење ===
     284
     285Прашалникот кој го тестираме:
     286{{{
     287SELECT * FROM v_ride_manifest WHERE ride_id = 1;
     288}}}
     289
     290==== Време на извршување без индекси:
     291
     292**2 ms 320 ms**
     293
     294{{{
     295Nested Loop Left Join  (cost=3.45..98.67 rows=2 width=547)
     296                       (actual time=0.719..2.233 rows=3.00 loops=1)
     297  ->  Nested Loop
     298        ->  Nested Loop
     299              ->  Index Scan using rides_pkey on rides r
     300                    Index Cond: (id = 1)
     301              ->  Index Scan using uq_bookings_passenger_ride on bookings b
     302                    Index Cond: (ride_id = 1)
     303                    Filter: (status = ANY ('{confirmed,picked_up,completed}'))
     304                    Rows Removed by Filter: 0
     305              ->  Index Scan using passengers_pkey on passengers p
     306                    Index Cond: (id = b.passenger_id)
     307              ->  Index Scan using users_pkey on users up
     308                    Index Cond: (id = p.user_id)
     309              ->  Index Scan using route_stops_pkey on route_stops pup_stop
     310              ->  Index Scan using route_stops_pkey on route_stops pdrop_stop
     311  ->  Index Scan using booking_final_fare_booking_id_key
     312        on booking_final_fare bff
     313        Index Cond: (booking_id = b.id)
     314Planning Time: 5.151 ms
     315Execution Time: 2.320 ms
     316}}}
     317
     318Планот на извршување покажува дека планерот веќе користи `Index Scan` по `bookings` преку постоечкиот уникатен индекс `uq_bookings_passenger_ride`. Сите JOIN операции се решаваат преку примарни клучеви. Сепак, го додаваме следниот индекс за да го покриеме `IN` филтерот по `status` без пристап до табелата:
     319
     320{{{
     321CREATE INDEX idx_bookings_ride_status
     322    ON bookings(ride_id, status);
     323}}}
     324
     325==== Време на извршување со индекс:
     326
     327**2 ms 452 ms** (без значајна промена)
     328
     329{{{
     330Nested Loop Left Join  (cost=3.45..98.67 rows=2 width=547)
     331                       (actual time=0.343..2.142 rows=3.00 loops=1)
     332  ->  Nested Loop
     333        ->  Index Scan using rides_pkey on rides r
     334              Index Cond: (id = 1)
    178335        ->  Index Scan using uq_bookings_passenger_ride on bookings b
    179               Index Cond: (ride_id = 1000)
     336              Index Cond: (ride_id = 1)
    180337              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
    186338        ->  Index Scan using passengers_pkey on passengers p
    187339        ->  Index Scan using users_pkey on users up
     
    190342  ->  Index Scan using booking_final_fare_booking_id_key
    191343        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`) мора да ги прочита сите редови. Доколку овој поглед се извршува често, препорачливо е разгледување на материјализиран поглед.
     344Planning Time: 7.505 ms
     345Execution Time: 2.452 ms
     346}}}
     347
     348Времето на извршување останува практично исто бидејќи планерот веќе користеше оптимални индекси. Погледот е веќе брз преку примарните клучеви.
    383349
    384350=== 6. Анализа на поглед 6, популарност на рути ===
     
    386352Прашалникот кој го тестираме:
    387353{{{
    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)
     354SELECT * FROM v_route_popularity ORDER BY total_bookings DESC LIMIT 10;
     355}}}
     356
     357==== Време на извршување без индекси:
     358
     359**77 s 785 ms**
     360
     361{{{
     362Limit  (cost=2377094.80..2377094.83 rows=10 width=624)
     363       (actual time=77411.184..77595.364 rows=10.00 loops=1)
    398364  ->  Sort  (Sort Key: total_bookings DESC)
    399365        Sort Method: top-N heapsort
     
    401367              ->  Finalize GroupAggregate  (Group Key: r.route_id)
    402368                    ->  Gather Merge
    403                           Workers Planned: 4
     369                          Workers Planned: 2
     370                          Workers Launched: 2
    404371                          ->  Partial HashAggregate
    405372                                ->  Hash Left Join  (r.id = bs.ride_id)
     
    407374                                      ->  Subquery Scan on bs
    408375                                            ->  HashAggregate
    409                                                   (Group Key: b.ride_id)
     376                                                  Group Key: b.ride_id
     377                                                  Batches: 1233
     378                                                  Disk Usage: 507760kB
    410379                                                  ->  Hash Left Join
    411380                                                        (b.id = bff.booking_id)
    412381                                                        ->  Seq Scan on bookings b
    413                                                               (12000001 rows — целосен скен)
     382                                                              (12000001 rows)
    414383                                                        ->  Seq Scan on
    415384                                                            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 милиони тарифи без никаков точковен филтер — ниеден индекс не може да ја заобиколи потребата за читање на сите редови. Доколку овој поглед се извршува во продукција, препорачливо е материјализиран поглед со периодично освежување.
     385                                                              (12000000 rows)
     386Planning Time: 3.820 ms
     387Execution Time: 77785.557 ms
     388}}}
     389
     390Најбавните операции се `Seq Scan on bookings` (12,000,000 редови) и `Seq Scan on booking_final_fare` (12,000,000 редови). `HashAggregate` запишува 507,760 kB на диск во 1,233 батчи. Овој поглед извршува целосна агрегација — ниеден индекс не може да ја заобиколи потребата за читање на сите редови. Решение: материјализиран поглед со периодично освежување.
     391
     392{{{
     393CREATE MATERIALIZED VIEW mv_route_popularity AS
     394    -- (полна дефиниција)
     395
     396CREATE INDEX idx_mv_route_popularity_cities
     397    ON mv_route_popularity(origin_city, destination_city);
     398
     399CREATE INDEX idx_mv_route_popularity_bookings
     400    ON mv_route_popularity(total_bookings DESC);
     401
     402-- Освежување:
     403REFRESH MATERIALIZED VIEW mv_route_popularity;
     404}}}
     405
     406==== Време на извршување со материјализиран поглед:
     407
     408**8–10 ms** (беше 77 s 785 ms)
     409
     410Материјализираниот поглед ги зачувува резултатите физички на диск. Освежувањето трае ~43 s, но секое читање после тоа е практично моментално преку индексите.
    461411
    462412=== 7. Анализа на поглед 7, детали за резервација ===
     
    464414Прашалникот кој го тестираме:
    465415{{{
    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)
     416SELECT * FROM v_booking_details WHERE booking_id = 1;
     417}}}
     418
     419==== Време на извршување без индекси:
     420
     421**2 ms 116 ms**
     422
     423{{{
     424Nested Loop Left Join  (cost=4.60..54.37 rows=1 width=669)
     425                       (actual time=1.653..1.667 rows=1.00 loops=1)
    476426  ->  Nested Loop
    477427        ->  Index Scan using bookings_pkey on bookings b
    478               Index Cond: (id = 5000)
     428              Index Cond: (id = 1)
    479429        ->  Index Scan using passengers_pkey on passengers p
    480430        ->  Index Scan using users_pkey on users up
     
    494444  ->  Index Scan using booking_final_fare_booking_id_key
    495445        on booking_final_fare bff
    496 Planning Time: 8.xxx ms
    497 Execution Time: 427.xxx ms
     446        Index Cond: (booking_id = 1)
     447Planning Time: 37.417 ms
     448Execution Time: 2.116 ms
    498449}}}
    499450
    500451Планот на извршување покажува дека планерот веќе користи `Index Scan` по примарни клучеви за сите JOIN операции. Нема потреба од дополнителни индекси. Нема потреба да се преуредува прашалникот.
    501452
     453==== Нема индекси — веќе оптимален:
     454
     455Времето на извршување останува **~2 ms** — погледот работи оптимално преку постоечките примарни клучеви.
     456
     457=== 8. Анализа на поглед 8, резиме на инциденти ===
     458
     459Прашалникот кој го тестираме:
     460{{{
     461SELECT * FROM v_incident_summary WHERE ride_id = 216;
     462}}}
     463
     464==== Време на извршување без индекси:
     465
     466**17 ms 335 ms**
     467
     468{{{
     469Nested Loop  (cost=1.59..1266.66 rows=1 width=594)
     470             (actual time=3.666..17.240 rows=1.00 loops=1)
     471  ->  Nested Loop
     472        ->  Nested Loop
     473              ->  Seq Scan on incident_reports ir
     474                    Filter: (ride_id = 216)
     475                    Rows Removed by Filter: 49995
     476              ->  Index Scan using users_pkey on users u_rep
     477                    Index Cond: (id = ir.reporter_id)
     478              ->  Index Scan using rides_pkey on rides r
     479                    Index Cond: (id = 216)
     480              ->  Index Scan using drivers_pkey on drivers d
     481              ->  Index Scan using users_pkey on users u_drv
     482          ->  Seq Scan on routes ro
     483          ->  Seq Scan on locations orig
     484          ->  Index Scan using cities_pkey on cities orig_city
     485          ->  Seq Scan on locations dest
     486          ->  Index Scan using cities_pkey on cities dest_city
     487Planning Time: 1.169 ms
     488Execution Time: 17.335 ms
     489}}}
     490
     491`Seq Scan on incident_reports` скенира 49,996 редови со `Filter: ride_id = 216`. Иако беше тестиран `idx_incident_ride_id`, планерот не го користеше бидејќи табелата е релативно мала и `Seq Scan` е побрз за мали табели. Индексот беше отстранет.
     492
    502493==== Време на извршување со индекс:
    503494
    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)
     495**46 ms 095 ms** (без подобрување — индексот отстранет)
     496
     497{{{
     498Nested Loop  (cost=1.59..1266.66 rows=1 width=594)
     499             (actual time=32.141..45.969 rows=1.00 loops=1)
     500  ->  Seq Scan on incident_reports ir
     501        Filter: (ride_id = 216)
     502        Rows Removed by Filter: 49995
     503  ->  Index Scan using users_pkey on users u_rep
     504        Index Cond: (id = ir.reporter_id)
     505  ->  Index Scan using rides_pkey on rides r
     506        Index Cond: (id = 216)
     507  ->  Index Scan using drivers_pkey on drivers d
     508  ->  Index Scan using users_pkey on users u_drv
     509Planning Time: 4.874 ms
     510Execution Time: 46.095 ms
     511}}}
     512
     513Планерот продолжува да користи `Seq Scan on incident_reports` дури и со индекс — за 49,996 редови `Seq Scan` е поефикасен. Индексите `idx_incident_ride_id`, `idx_incident_type` и `idx_incident_reported_at` беа отстранети.
     514
     515=== 9. Анализа на поглед 9, непрочитани нотификации ===
     516
     517Прашалникот кој го тестираме:
     518{{{
     519SELECT * FROM v_unread_notifications WHERE user_id = 1;
     520}}}
     521
     522==== Време на извршување без индекси:
     523
     524**2 s 410 ms**
     525
     526{{{
     527Gather  (cost=1000.29..199683.87 rows=147 width=131)
     528        (actual time=1212.278..2410.665 rows=89.00 loops=1)
     529  Workers Planned: 2
     530  Workers Launched: 2
    509531  ->  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 наместо уникатниот клуч, но разликата е незначителна бидејќи погледот веќе работеше оптимално преку примарните клучеви.
     532        ->  Parallel Seq Scan on notifications n
     533              Filter: ((read_at IS NULL) AND (user_id = 1))
     534              Rows Removed by Filter: 3333304
     535        ->  Materialize
     536              ->  Index Scan using users_pkey on users u
     537                    Index Cond: (id = 1)
     538Planning Time: 1.136 ms
     539Execution Time: 2410.730 ms
     540}}}
     541
     542Најбавната операција е `Parallel Seq Scan on notifications` — без индекс базата скенира сите 10,000,001 нотификации (3,333,304 по работник) за да ги најде непрочитаните за корисникот. Го додаваме парцијален индекс:
     543
     544{{{
     545CREATE INDEX idx_notif_user_unread
     546    ON notifications(user_id)
     547    WHERE read_at IS NULL;
     548}}}
     549
     550==== Време на извршување со индекс:
     551
     552**40 ms 896 ms** (беше 2 s 410 ms)
     553
     554{{{
     555Nested Loop  (cost=0.72..279.60 rows=147 width=131)
     556             (actual time=0.352..40.822 rows=89.00 loops=1)
     557  ->  Index Scan using users_pkey on users u
     558        Index Cond: (id = 1)
     559        Filter: (deleted_at IS NULL)
     560  ->  Index Scan using idx_notif_user_unread on notifications n
     561        Index Cond: (user_id = 1)
     562Planning Time: 3.022 ms
     563Execution Time: 40.896 ms
     564}}}
     565
     566`idx_notif_user_unread` е парцијален индекс — ги индексира само непрочитаните нотификации (`WHERE read_at IS NULL`), со што е и мал и брз. Планерот го заменува `Parallel Seq Scan` со директен `Index Scan`. Подобрувањето е ~59x (од 2.41 s на 40 ms).
     567
     568=== Резиме на индекси ===
     569
     570Задржани индекси кои донесоа подобрување:
     571
     572|| **Индекс** || **Табела** || **Поглед** || **Пред** || **По** ||
     573|| `idx_rides_status_dep` || rides || v_available_rides || 72 s 559 ms || 3 s 980 ms ||
     574|| `idx_ratings_reviewee` || ratings || v_available_rides, v_driver_profile || значајно || значајно ||
     575|| `idx_vo_driver_active` || vehicle_ownership || v_driver_profile || 1 s 239 ms || 1 s 379 ms ||
     576|| `idx_bookings_passenger_id` || bookings || v_passenger_trip_history || 721 ms || 562 ms ||
     577|| `idx_passengers_user_id` || passengers || v_passenger_trip_history || значајно || значајно ||
     578|| `idx_ratings_reviewer_ride` || ratings || v_passenger_trip_history || значајно || значајно ||
     579|| `idx_bookings_ride_status` || bookings || v_ride_manifest || 2 ms || 2 ms ||
     580|| `idx_notif_user_unread` || notifications || v_unread_notifications || 2 s 410 ms || 40 ms ||
     581|| `mv_route_popularity` || — || v_route_popularity || 77 s 785 ms || 8–10 ms ||
     582
     583Отстранети индекси (без ефект или со негативно влијание):
     584
     585|| **Индекс** || **Причина** ||
     586|| `idx_rides_driver_status` || Агрегатен поглед — нема ефект ||
     587|| `idx_incident_ride_id` || Планерот го игнорира — Seq Scan е побрз за мала табела ||
     588|| `idx_incident_type` || Нема ефект ||
     589|| `idx_incident_reported_at` || Нема ефект ||
     590|| `idx_bff_booking_id` || Нема ефект ||