У меня есть такая таблица:
Table "public.transactions"
Column | Type | Nullable | Default | Storage |
---------------------+--------------------------+----------+--------------------|
id | integer | not null | nextval | plain |
ticket | integer | | | plain |
pay_station | character varying(50) | | extended| |
stall | character varying(50) | | | extended |
license_plate | character varying(8) | | | extended |
purchased_date | timestamp with time zone | not null | | plain |
expiry_date | timestamp with time zone | | | plain |
payment_type | character varying(50) | | | extended |
total_collections | numeric(10,2) | | | main |
revenue | numeric(10,2) | | | main |
rate_name | character varying(50) | | | extended |
hours_paid | numeric(4,2) | | | main |
validation_revenue | numeric(10,2) | | | main |
transaction_fee | numeric(10,2) | | | main |
method | character varying(50) | | | extended |
Indexes:
"transactions_pkey" PRIMARY KEY, btree (id)
"transactions_expiry_date_idx" btree (expiry_date)
"transactions_purchased_date_idx" btree (purchased_date)
"transactions_stall_idx" btree (stall)
И еще ~ 20 столбцов, для краткости опущенных.
В этой таблице ~ 2,5 миллиона строк.
Теперь мой код Python во Flask, который обслуживает API, выглядит следующим образом для примера запроса:
filters = [
datetime_range['start'] < Transactions.expiry_date,
datetime_range['end'] > Transactions.purchased_date
]
if 'parking_spaces' in params:
spaces = params['parking_spaces'] # array
filters.append(Transactions.stall.in_(spaces))
results = Transactions.query.with_entities(
Transactions.stall, Transactions.purchased_date, Transactions.expiry_date
).filter(*filters).order_by(Transactions.purchased_date).all()
Где даты datetime
объекты. Теперь, если я не предоставляю никаких входных данных в теле POST, я по умолчанию устанавливаю минимальное / максимальное время, без пробелов WHERE IN, и запрос выглядит так:
datetime_range:
{'start': datetime.datetime(2016, 1, 1, 0, 0, tzinfo=datetime.timezone.utc), 'end': datetime.datetime(2019, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)}
Query:
SELECT transactions.stall AS transactions_stall, transactions.purchased_date AS transactions_purchased_date, transactions.expiry_date AS transactions_expiry_date
FROM transactions
WHERE transactions.expiry_date > %(expiry_date_1)s AND transactions.purchased_date < %(purchased_date_1)s ORDER BY transactions.purchased_date
Теперь, если я сделаю запрос непосредственно в psql
:
SELECT transactions.stall, transactions.purchased_date, transactions.expiry_date FROM transactions WHERE transactions.expiry_date > '2016-01-01 00:00:00.000Z' AND transactions.purchased_date < '2019-01-01 00:00:00.000Z';
Время: 2724,326 мс (00: 02,724)
Однако, выполняя тот же запрос в SQLAlchemy через Flask, используя Postman для тестирования, я получаю ответ через 866946 мс (14: 26,946) , возвращая 13,4 КБ данных.
Что, очевидно, огромная разница. Когда я настраиваю диапазон, время отклика увеличивается экспоненциально - некоторые образцы:
{
"datetime_range": {
"start": "2017-01-01T14:30:00.000Z",
"end": "2017-01-04T18:00:00.000Z"
}
}
Время отклика: 13387 мс (00: 13,39)
Тот же запрос в psql:
SELECT transactions.stall, transactions.purchased_date, transactions.expiry_date FROM transactions WHERE transactions.expiry_date > '2017-01-01T14:30:00.000Z' AND transactions.purchased_date < '2017-01-04T18:00:00.000Z';
Время: 580.603 мс
{
"datetime_range": {
"start": "2017-01-01T14:30:00.000Z",
"end": "2017-03-01T18:00:00.000Z"
}
}
Время ответа SQLAlchemy: 41878 мс
SELECT transactions.stall, transactions.purchased_date, transactions.expiry_date FROM transactions WHERE transactions.expiry_date > '2017-01-01T14:30:00.000Z' AND transactions.purchased_date < '2017-03-01T18:00:00.000Z';
Время отклика PostgreSQL: 1170,169 мс (00: 01,170)
Почему здесь такая огромная разница, и как я могу заставить SQLAlchemy работать быстрее и сохранять время ответа моего Flask порядка секунд, а не минут?