Как улучшить производительность запросов JPA / PostgreSQL? - PullRequest
0 голосов
/ 09 мая 2018

У меня есть таблица с 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 операций вставки в секунду. На тестовом сервере не выполняется операция вставки. Я предполагаю, может быть, операция вставки нуждается в блокировке записи, и поэтому запрос медленный, потому что он должен ждать блокировки?

1 Ответ

0 голосов
/ 09 мая 2018

У вас должна получиться лучшая производительность с индексом mac_address и timestamp в одном индексе:

CREATE INDEX [CONCURRENTLY] ON table (mac_address, timestamp);

Причина, по которой индекс метки времени не используется, заключается в том, что для нахождения правильных строк потребуется перекрестная ссылка на него с индексом mac_address (на самом деле это займет больше времени, чем просто поиск строк)

У меня нет опыта работы с JPA, поэтому я не могу сказать, почему он медленнее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...