Как ускорить агрегатный выбор PostgreSQL с помощью подзапросов и инструкций case - PullRequest
0 голосов
/ 03 января 2019

Справочная информация : у меня есть таблица, содержащая записи финансовых транзакций.Таблица содержит несколько десятков миллионов строк для десятков тысяч пользователей.Мне нужно получить сумму транзакций для отображения балансов и других аспектов сайта.

Мой текущий запрос может быть очень медленным и часто задерживается.Я пытался оптимизировать запрос, но не могу заставить его работать эффективно.

Среда : Мое приложение работает на Heroku с использованием плана Postgres Standard-2 (оперативная память 8 ГБ, 400максимальное количество подключений, 256 ГБ разрешенного хранилища).Максимальное количество подключений в любой момент времени - около 20, а мой текущий размер БД - 35 ГБ.Согласно статистике, этот запрос выполняется в среднем около 1000 мс и используется очень часто, что сильно влияет на производительность сайта.

Для базы данных частота обращений к кешу индекса составляет 99%, а частота обращений к кешу таблицысоставляет 97%.Автовакуум запускается примерно через день на основе текущих порогов.

Вот мои текущие настройки таблицы транзакций:

CREATE TABLE transactions (
id bigint DEFAULT nextval('transactions_id_seq'::regclass) NOT NULL,
user_id integer NOT NULL,
date timestamp without time zone NOT NULL,
amount numeric(15,2) NOT NULL,
transaction_type integer DEFAULT 0 NOT NULL,
account_id integer DEFAULT 0,
reconciled integer DEFAULT 0,
parent integer DEFAULT 0,
ccparent integer DEFAULT 0,
created_at timestamp without time zone DEFAULT now() NOT NULL
);
CREATE INDEX transactions_user_id_key ON transactions USING btree (user_id);
CREATE INDEX transactions_user_date_idx ON transactions (user_id, date);
CREATE INDEX transactions_user_ccparent_idx ON transactions (user_id, ccparent) WHERE ccparent >0;

А вот мой текущий запрос:

SELECT account_id,
 sum(deposit) - sum(withdrawal) AS balance,
 sum(r_deposit)-sum(r_withdrawal) AS r_balance,
 sum(deposit) AS o_deposit,
 sum(withdrawal) AS o_withdrawal,
 sum(r_deposit) AS r_deposit,
 sum(r_withdrawal) AS r_withdrawal
FROM 
(SELECT t.account_id,
    CASE
        WHEN transaction_type > 0 THEN sum(amount)
        ELSE 0
    END AS deposit,
    CASE
        WHEN transaction_type = 0 THEN sum(amount)
        ELSE 0
    END AS withdrawal,
    CASE
        WHEN transaction_type > 0 AND reconciled=0 THEN sum(amount)
        ELSE 0
    END AS r_deposit,
    CASE
        WHEN transaction_type = 0 AND reconciled=0 THEN sum(amount)
        ELSE 0
    END AS r_withdrawal
FROM transactions AS t
WHERE user_id = $1 AND parent=0 AND ccparent=0
GROUP BY  transaction_type, account_id, reconciled ) AS t0
GROUP BY  account_id;

запрос состоит из нескольких частей.Я должен получить следующее для каждой учетной записи пользователя:

1) общий остаток на счете

2) баланс для всех выверенных транзакций

3) отдельно,сумма всех депозитов, снятий, сверенных депозитов и согласованных снятий.

Вот один план запроса, когда я запускаю explain analyze для запроса:

QUERY PLAN                                                                           
---------------------------------------------------------------------------------------------------------------------------------------------------------------
 HashAggregate  (cost=13179.85..13180.14 rows=36 width=132) (actual time=1326.200..1326.204 rows=6 loops=1)
   Group Key: t.account_id
   ->  HashAggregate  (cost=13179.29..13179.58 rows=36 width=18) (actual time=1326.163..1326.171 rows=16 loops=1)
         Group Key: t.transaction_type, t.account_id, t.reconciled
         ->  Bitmap Heap Scan on transactions t  (cost=73.96..13132.07 rows=13491 width=18) (actual time=17.410..1317.863 rows=12310 loops=1)
               Recheck Cond: (user_id = 1)
               Filter: ((parent = 0) AND (ccparent = 0))
               Rows Removed by Filter: 2
               Heap Blocks: exact=6291
               ->  Bitmap Index Scan on transactions_user_id_key  (cost=0.00..73.29 rows=13601 width=0) (actual time=15.901..15.901 rows=12343 loops=1)
                     Index Cond: (user_id = 1)
 Planning time: 0.895 ms
 Execution time: 1326.424 ms

Есть ли у кого-нибудь какие-либо предложения о том, какускорить этот запрос?Как я уже сказал, это самый запущенный запрос в моем приложении, а также один из самых требовательных к БД.Если бы я мог оптимизировать это, это имело бы огромные преимущества для приложения в целом.

1 Ответ

0 голосов
/ 03 января 2019

Попробуйте, если он подберет индекс для transactions (user_id, parent, ccparent, transaction_type, account_id, reconciled).

CREATE INDEX transactions_u_p_ccp_tt_a_r_idx
             ON transactions
                (user_id,
                 parent,
                 ccparent,
                 transaction_type,
                 account_id,
                 reconciled);

Может быть, вы даже можете включить amount в индекс.

CREATE INDEX transactions_u_p_ccp_tt_a_r_a_idx
             ON transactions
                (user_id,
                 parent,
                 ccparent,
                 transaction_type,
                 account_id,
                 reconciled,
                 amount);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...