wiki:Phase4_Performance_Analysis

Мерење на перформанси

Извршена е анализа на перформансите на базата film_rental во PostgreSQL. Се споредуваат неоптимизирани табели и оптимизирани табели:

  • rental_copy - непартиционирана табела, користена како почетна состојба за споредба;
  • rental - партиционирана табела според rental_date, со дополнителни индекси;
  • payment_copy - непартиционирана табела за плаќања;
  • payment - партиционирана табела според payment_date, со индекс за датум и вработен.

За оваа анализа беше користено pgbench. Секој тест се извршува со повеќе паралелни клиенти, при што се мерат просечна латентност и TPS, односно број на трансакции во секунда. Во сите тестови бројот на неуспешни трансакции беше 0.

Пример за извршување на benchmark тест:

pgbench -n -U admin -d film_rental -c 10 -j 2 -T 60 -f /tmp/movie_recommendation_rental_copy.sql  

Каде:

  • -n означува дека pgbench не ја иницијализира стандардната pgbench шема;
  • -U admin е корисникот со кој се поврзува кон базата;
  • -d film_rental е базата врз која се извршува тестот;
  • -c 10 означува 10 паралелни клиенти;
  • -j 2 означува 2 работни нишки;
  • -T 60 означува времетраење од 60 секунди;
  • -f ја задава SQL датотеката што се извршува.

Истата команда се повторуваше со различен број клиенти, на пример 10, 20, 30 и 50, и со SQL датотеки за неоптимизираната и оптимизираната верзија.

Детални резултати

Q1 - Movie Recommendation

Прашалникот прави препорака на филмови за конкретен корисник. Се анализираат претходните изнајмувања, омилените категории, рејтинзите и популарноста на филмовите во 2024 година.

WITH customer_history AS (
    SELECT DISTINCT
        f.film_id,
        f.rating,
        fc.category_id
    FROM rental_copy r
    JOIN inventory i ON r.inventory_id = i.inventory_id
    JOIN film f ON i.film_id = f.film_id
    JOIN film_category fc ON f.film_id = fc.film_id
    WHERE r.customer_id = 101
),
preferred_categories AS (
    SELECT
        category_id,
        COUNT(*) AS category_score
    FROM customer_history
    GROUP BY category_id
),
candidate_films AS (
    SELECT
        f.film_id,
        f.title,
        f.rating,
        fc.category_id,
        pc.category_score
    FROM film f
    JOIN film_category fc ON f.film_id = fc.film_id
    JOIN preferred_categories pc ON fc.category_id = pc.category_id
    WHERE NOT EXISTS (
        SELECT 1
        FROM customer_history ch
        WHERE ch.film_id = f.film_id
    )
),
film_popularity AS (
    SELECT
        i.film_id,
        COUNT(r.rental_id) AS rental_count
    FROM rental_copy r
    JOIN inventory i ON r.inventory_id = i.inventory_id
    WHERE r.rental_date >= DATE '2024-01-01'
      AND r.rental_date < DATE '2025-01-01'
    GROUP BY i.film_id
)
SELECT
    cf.film_id,
    cf.title,
    cf.rating,
    c.name AS category,
    cf.category_score,
    COALESCE(fp.rental_count, 0) AS yearly_rentals,
    (cf.category_score * 10 + COALESCE(fp.rental_count, 0)) AS recommendation_score
FROM candidate_films cf
JOIN category c ON cf.category_id = c.category_id
LEFT JOIN film_popularity fp ON cf.film_id = fp.film_id
WHERE cf.rating IN (
    SELECT rating
    FROM customer_history
)
ORDER BY recommendation_score DESC, cf.title
LIMIT 10;

За оптимизираната верзија се користи истата логика, но наместо rental_copy се користи партиционираната табела rental.

Clients Threads rental_copy latency rental_copy TPS rental latency rental TPS
10 2 2235.603 ms 4.473 1083.428 ms 9.230
20 4 4036.198 ms 4.955 2208.459 ms 9.056
50 4 9487.494 ms 5.270 5247.761 ms 9.528

Q1 покажува значително подобрување кај оптимизираната табела. Кај 10 клиенти латентноста е намалена за околу 51.5%, а TPS е зголемен за околу 106.3%. Кај 50 клиенти латентноста е намалена за околу 44.7%, а TPS е зголемен за околу 80.8%. Ова се случува бидејќи прашалникот користи customer_id, inventory_id и rental_date. Индексите помагаат при пребарување на историјата на корисникот, а партиционирањето помага при делот од прашалникот кој ја пресметува популарноста за 2024 година.

Q2 - Popular Films 2024

Прашалникот ги пресметува најизнајмуваните филмови во 2024 година. Се користи филтер по датум, спојување со inventory и film, групирање по филм и сортирање според бројот на изнајмувања.

SELECT
    f.film_id,
    f.title,
    f.rating,
    COUNT(r.rental_id) AS rental_count
FROM rental_copy r
JOIN inventory i ON r.inventory_id = i.inventory_id
JOIN film f ON i.film_id = f.film_id
WHERE r.rental_date >= DATE '2024-01-01'
  AND r.rental_date < DATE '2025-01-01'
GROUP BY f.film_id, f.title, f.rating
ORDER BY rental_count DESC
LIMIT 20;

За оптимизираната верзија се користи истата логика, но наместо rental_copy се користи партиционираната табела rental.

Clients rental_copy latency rental_copy TPS rental latency rental TPS Промена
10 2222.438 ms 4.500 1628.696 ms 6.140 ~26.7% побрзо
20 4205.239 ms 4.756 3828.958 ms 5.223 ~8.9% побрзо
30 6545.865 ms 4.583 6648.265 ms 4.512 ~1.6% побавно
50 11077.453 ms 4.514 11290.484 ms 4.429 ~1.9% побавно

Q2 има мешани резултати. Кај 10 и 20 клиенти партиционираната табела е побрза, но кај 30 и 50 клиенти резултатот е речиси ист или малку полош. Ова покажува дека партиционирањето не секогаш гарантира подобрување. Кај овој прашалник, освен читање од rental, има и GROUP BY, ORDER BY и LIMIT. При поголем број паралелни клиенти главен трошок може да станат агрегацијата и сортирањето, а не само читањето на редови од табелата.

Q3 - Monthly Revenue

Прашалникот пресметува месечен приход по продавница за 2024 година. Се пресметуваат број на плаќања, вкупен приход, просечна, минимална и максимална уплата.

SELECT
    date_trunc('month', p.payment_date) AS month,
    s.store_id,
    COUNT(p.payment_id) AS payments_count,
    SUM(p.amount) AS total_revenue,
    AVG(p.amount) AS avg_payment,
    MIN(p.amount) AS min_payment,
    MAX(p.amount) AS max_payment
FROM payment_copy p
JOIN staff s ON p.staff_id = s.staff_id
WHERE p.payment_date >= DATE '2024-01-01'
  AND p.payment_date < DATE '2025-01-01'
GROUP BY date_trunc('month', p.payment_date), s.store_id
ORDER BY month, s.store_id;

За оптимизираната верзија се користи истата логика, но наместо payment_copy се користи партиционираната табела payment.

Clients payment_copy latency payment_copy TPS payment latency payment TPS Промена
10 2627.300 ms 3.806 1055.953 ms 9.470 ~59.8% побрзо
30 8545.741 ms 3.511 3325.437 ms 9.021 ~61.1% побрзо
50 16138.221 ms 3.098 5321.565 ms 9.396 ~67.0% побрзо

Q3 покажува најстабилно подобрување кај аналитичките прашалници. Прашалникот пресметува месечен приход по продавница за 2024 година, а табелата payment е партиционирана според payment_date. Бидејќи условот користи временски интервал за 2024 година, PostgreSQL може да ја чита партицијата за 2024 година наместо целата табела. Затоа латентноста е намалена за околу 60-67%, а TPS е зголемен за околу 149-203%.

Q4 - INSERT rental

Овој тест мери внесување нови редови во табелата за изнајмувања. За benchmark редовите се користи посебен датум 2026-03-07 12:00:00, за да се разликуваат од оригиналните податоци.

INSERT INTO rental_copy (rental_date, inventory_id, customer_id, staff_id)
VALUES (
    TIMESTAMP '2026-03-07 12:00:00',
    floor(random() * 50000 + 1)::int,
    floor(random() * 1000000 + 1)::int,
    floor(random() * 50 + 1)::int
);

За оптимизираната верзија се користи истата логика, но внесувањето се прави во партиционираната табела rental.

Clients rental_copy latency rental_copy TPS rental latency rental TPS Промена
10 3.121 ms 3204.533 2.855 ms 3502.623 ~8.5% побрзо
30 2.949 ms 10173.818 3.147 ms 9533.949 ~6.7% побавно
50 6.756 ms 7400.777 3.088 ms 16190.222 ~54.3% побрзо

INSERT операциите имаат мешани резултати. Кај 10 и 50 клиенти партиционираната табела е побрза, но кај 30 клиенти непартиционираната табела има малку подобар резултат. Причината е тоа што при внесување PostgreSQL мора да одреди во која партиција припаѓа новиот ред и мора да ги ажурира индексите. Тоа додава мал overhead, но при поголемо конкурентно оптоварување партиционираната структура може да помогне затоа што записите се насочуваат кон конкретна партиција.

Q5 - UPDATE rental

Овој тест мери ажурирање на изнајмувања. Се избира еден ред со benchmark датумот и со return_date IS NULL, па потоа се поставува return_date = now().

UPDATE rental_copy
SET return_date = now()
WHERE rental_id = (
    SELECT rental_id
    FROM rental_copy
    WHERE rental_date = TIMESTAMP '2026-03-07 12:00:00'
      AND return_date IS NULL
    LIMIT 1
);

За оптимизираната верзија се користи истата логика, но ажурирањето се прави врз партиционираната табела rental.

Clients rental_copy latency rental_copy TPS rental latency rental TPS Промена
10 590.026 ms 16.948 7.044 ms 1419.709 ~98.8% побрзо
30 1483.097 ms 20.228 12.318 ms 2435.437 ~99.2% побрзо
50 2646.029 ms 18.896 16.689 ms 2996.003 ~99.4% побрзо

UPDATE операцијата покажува најголемо подобрување. Условот користи конкретен rental_date, па кај партиционираната табела PostgreSQL може да пребарува само во релевантната партиција. Кај rental_copy системот мора да пребарува низ голема непартиционирана табела. Затоа латентноста е многу поголема, а TPS е многу помал.

Вкупно подобрување

Партиционираните табели и индексите покажуваат значително подобрување кај прашалници кои се временски ограничени и кај операции кои можат да се насочат кон конкретна партиција. Најголеми придобивки:

  • Q3 - Monthly Revenue: околу 64.5% просечно намалување на латентноста.
  • Q5 - UPDATE rental: околу 99.2% просечно намалување на латентноста.
  • Q1 - Movie Recommendation: околу 45.8% просечно намалување на латентноста.

Мешани резултати:

  • Q2 - Popular Films има подобрување кај 10 и 20 клиенти, но мало влошување кај 30 и 50 клиенти.
  • Q4 - INSERT има подобрување кај 10 и 50 клиенти, но мало влошување кај 30 клиенти.

Ова покажува дека оптимизацијата мора да се мери. Партиционирањето не е автоматско решение за сите прашалници. Најмногу помага кога WHERE условот ја користи колоната по која е направено партиционирањето.

Операции на читање со мешани резултати

  • Q1 - Movie Recommendation: јасно подобрување кај сите нивоа на конкурентност.
  • Q2 - Popular Films: подобро кај помал број клиенти, но речиси исто или малку полошо кај поголем број клиенти.
  • Q3 - Monthly Revenue: најдобар пример каде партиционирањето директно го намалува бројот на редови што треба да се читаат.

Кај читањата кои содржат големи агрегации и сортирања, како Q2, главниот проблем не е секогаш табелата од која се чита. Понекогаш главниот трошок е обработката по читањето, односно GROUP BY и ORDER BY.

Операции на запишување

  • INSERT: просечно подобрување, но со варијации.
  • UPDATE: многу големо подобрување поради филтрирање по rental_date.

Кај INSERT, партиционирањето може да додаде overhead затоа што секој ред мора да се насочи во соодветна партиција. Кај UPDATE, партиционирањето е многу корисно кога условот ја користи партициската колона, бидејќи пребарувањето се ограничува на мала партиција наместо на целата табела.

Заклучок

За ова специфично работно оптоварување:

  • Партиционирањето е многу корисно за временски извештаи, особено кај payment и rental.
  • Индексите значително помагаат кај прашалници со WHERE услови и JOIN операции врз често користени колони.
  • Q3 и Q5 покажуваат најголемо подобрување, затоа што директно ја користат колоната по која е направено партиционирањето.
  • Q2 покажува дека партиционирањето не го решава секој проблем, бидејќи агрегацијата и сортирањето можат да станат главен bottleneck.
  • INSERT операциите треба да се мерат внимателно, бидејќи партиционирањето и индексите можат да додадат мал overhead.

Најсоодветен пристап за оваа база е да се задржи партиционирањето по датум за големите трансакциски табели rental и payment, да се користат индекси само за најчестите филтри и спојувања, и редовно да се проверуваат резултатите со EXPLAIN ANALYZE и pgbench.

Last modified 2 weeks ago Last modified on 05/09/26 20:03:51
Note: See TracWiki for help on using the wiki.