У меня есть таблица с 36,64 миллионами записей.
Определение таблицы следующим образом:
id integer, PK
attribute, varchar 255
value, varchar 255
store_id, integer
timestamp, timestamp without timezone
mac_address, varchar 255
плюс, столбец mac_address и timestamp имеет индекс.
запрос:
select count(*) from table where mac_address = $1 and timestamp between $2 and $3
select * from table where mac_address = $1 and timestamp between $2 and $3
Если я запускаю это в pgAdmin, это заняло всего 10 секунд.
Если я запускаю это с использованием JPA, это заняло более 40 секунд. Загрузка EAGER отсутствует.
Я посмотрел на SimpleJpaRepository
код. это именно эти два запроса, а count()
и getResultList()
вопросы:
1. похоже, что временная метка не используется ни в pgAdmin, ни в JPA. Я проверил это с АНАЛИЗОМ и ОБЪЯСНЕНИЕМ. Но почему?
2. Почему JPA нужно в 10 раз больше времени? ORM добавляет накладные расходы, но в 10 раз?
3. Как мне его улучшить?
РЕДАКТИРОВАТЬ 1:
Возможно, count()
из JPA не использует индексное сканирование, оно использует последовательный = медленный. моя версия postgresql - 9.5.
РЕДАКТИРОВАТЬ 2:
в JPA он использует setFirstResult()
и setMaxResult()
для получения 100 записей. Из общего числа 259242
Я пытаюсь имитировать его с помощью LIMIT и OFFSET , но я не увидел эти ключевые слова в запросе JPA. Может быть, JPA получает весь результат, а затем выполняет подкачку в памяти, что, в свою очередь, приводит к снижению производительности?
Первое выполнение запроса count()
занимает от 19 до 55 секунд с использованием pgAdmin.
ОБЪЯСНЕНИЕ двух запросов.
* * Количество тысячи сорок-одиной () * +1042 *
Aggregate (cost=761166.10..761166.11 rows=1 width=4) (actual time=1273.871..1273.871 rows=1 loops=1)
Output: count(id)
Buffers: shared read=92986 written=56
-> Bitmap Heap Scan on public.device_messages playerstat0_ (cost=11165.36..760309.47 rows=342650 width=4) (actual time=76.217..1258.389 rows=259242 loops=1)
Output: id, attributecode, attributevalue, store_id, "timestamp", mac_address
Recheck Cond: (((playerstat0_.mac_address)::text = '0011E004CA34'::text) AND (playerstat0_."timestamp" >= '2018-04-04 00:00:00'::timestamp without time zone) AND (playerstat0_."timestamp" <= '2018-05-04 00:00:00'::timestamp without time zone))
Rows Removed by Index Recheck: 6281401
Heap Blocks: exact=36622 lossy=55083
Buffers: shared read=92986 written=56
-> Bitmap Index Scan on device_messages_mac_address_timestamp_idx (cost=0.00..11079.70 rows=342650 width=0) (actual time=69.636..69.636 rows=259242 loops=1)
Index Cond: (((playerstat0_.mac_address)::text = '0011E004CA34'::text) AND (playerstat0_."timestamp" >= '2018-04-04 00:00:00'::timestamp without time zone) AND (playerstat0_."timestamp" <= '2018-05-04 00:00:00'::timestamp without time zone))
Buffers: shared read=1281
Planning time: 0.138 ms
Execution time: 1274.275 ms
выберите
Limit (cost=3362.52..5043.49 rows=100 width=34) (actual time=30.291..42.846 rows=100 loops=1)
Output: id, attributecode, attributevalue, mac_address, store_id, "timestamp"
Buffers: shared hit=15447 read=1676"
-> Index Scan Backward using device_messages_pkey on public.device_messages playerstat0_ (cost=0.57..5759855.56 rows=342650 width=34) (actual time=2.597..42.834 rows=300 loops=1)
Output: id, attributecode, attributevalue, mac_address, store_id, "timestamp"
Filter: ((playerstat0_."timestamp" >= '2018-04-04 00:00:00'::timestamp without time zone) AND (playerstat0_."timestamp" <= '2018-05-04 00:00:00'::timestamp without time zone) AND ((playerstat0_.mac_address)::text = '0011E004CA34'::text))
Rows Removed by Filter: 154833
Buffers: shared hit=15447 read=1676
Planning time: 0.180 ms
Execution time: 42.878 ms
РЕДАКТИРОВАТЬ 3:
После дополнительного тестирования подтверждается, что причиной является count (). выберите с ограничением и смещение довольно быстро. Только count () может занять до минуты.
упоминается здесь postgresql медленный подсчет
Пока работает функция подсчета (ROWS из плана запроса), я не могу вызвать это из JPA.
РЕДАКТИРОВАТЬ 3:
Я вроде решаю проблему, но не полностью.
Что касается select, после создания индекса, соответствующего запросу, он на самом деле работает довольно быстро, 2 ~ 5 секунд. Но это без сортировки. Сортировка добавляет еще один шаг процесса к запросу.
Счетчик () медленный и подтверждается документом postgresql. MVCC заставляет count () выполнять сканирование кучи, аналогично сканированию последовательности всей таблицы.
Последняя проблема, в которой я до сих пор не уверен, что запрос на производственном сервере медленнее, чем тестирование сервера. 60 секунд на производство и 5 секунд на тестирование сервера. С тем же размером таблицы и данных. Но большая разница в том, что рабочий сервер имеет более 20 операций вставки в секунду. На тестовом сервере не выполняется операция вставки. Я предполагаю, может быть, операция вставки нуждается в блокировке записи, и поэтому запрос медленный, потому что он должен ждать блокировки?