Соответствующий запрос и индексы для таблицы журналирования в SQL - PullRequest
1 голос
/ 16 августа 2010

Предположим, таблица с именем 'log', в ней огромные записи.

Приложение обычно получает данные простым SQL:

SELECT * 
FROM log 
WHERE logLevel=2 AND (creationData BETWEEN ? AND ?)

logLevel и creationData имеют индексы, но количество записей делает получение данных более длительным

Как мы можем это исправить?

Ответы [ 8 ]

5 голосов
/ 16 августа 2010

Посмотрите на ваш план выполнения / результат "EXPLAIN PLAN" - если вы извлекаете большие объемы данных, вы можете сделать очень мало для повышения производительности - вы можете попробовать изменить оператор SELECT, включив в него только те столбцы, которые вы заинтересованы, однако это не изменит количество логических операций чтения, которые вы делаете, и поэтому я подозреваю, что это окажет лишь незначительное влияние на производительность.

Если вы извлекаете только небольшое количество записей, то индекс LogLevel и индекс CreationDate должны помочь.

ОБНОВЛЕНИЕ: SQL-сервер в основном ориентирован на запросы небольших подмножеств массивных баз данных (например, возвращает одну запись клиента из базы данных из миллионов). На самом деле он не предназначен для возврата действительно больших наборов данных. Если объем возвращаемых вами данных действительно велик * * * * *, то вы можете сделать только определенную сумму, поэтому я должен спросить:

Что вы на самом деле пытаетесь достичь ?

  • Если вы отображаете сообщения журнала для пользователя, тогда они будут интересоваться только небольшим подмножеством за раз, и поэтому вам также может понадобиться изучить эффективные методы подкачки данных SQL - если вы возвращают только, скажем, 500 или около того записей одновременно, это все равно должно быть очень быстрым.

  • Если вы пытаетесь выполнить какой-либо статистический анализ, то вы можете скопировать свои данные в хранилище данных, более подходящее для статистического анализа. (Не уверен, что, однако, это не моя область знаний)

4 голосов
/ 16 августа 2010

1: никогда не использовать Select *
2: убедитесь, что ваши индексы верны, а статистика актуальна.
3: (Необязательно) Если вы обнаружите, что не просматриваете данные журнала за определенное время (по моему опыту, если это произошло более недели назад, мне, вероятно, не понадобится журнал для этого), настройте задание архивировать это в какую-нибудь резервную копию, а затем удалить неиспользуемые записи. Это уменьшит размер таблицы и сократит время, затрачиваемое на поиск в таблице.

2 голосов
/ 16 августа 2010

В зависимости от того, какую базу данных SQL вы используете, вы можете посмотреть Горизонтальное разбиение . Часто это может быть сделано полностью на стороне базы данных, поэтому вам не нужно будет менять свой код.

1 голос
/ 16 августа 2010

Вам нужны все столбцы?Первый шаг должен состоять в том, чтобы выбрать только те, которые вам действительно нужны для извлечения.

Другой аспект - это то, что вы делаете с данными после того, как они поступят в ваше приложение (заполните набор данных / прочитайте их последовательно /?).

Может быть некоторый потенциал для улучшения со стороны приложения обработки.

Вы должны ответить себе на следующие вопросы:

Вам нужно хранить все возвращенные данные в памяти водин раз?Сколько памяти вы выделяете на строку на извлекаемой стороне?Сколько памяти вам нужно сразу?Можете ли вы использовать немного памяти?

0 голосов
/ 17 августа 2010

Я действительно надеюсь, что под creationData вы подразумеваете creationDate.

Прежде всего, недостаточно иметь индексов на logLevel и creationData.Если у вас есть 2 отдельных индекса, Oracle сможет использовать только 1. Вам понадобится одиночный индекс для обоих полей:

CREATE INDEX i_log_1 ON log (creationData, logLevel);

Обратите внимание, что я сначала ставлю creationData.Таким образом, если вы поместите это поле только в предложение WHERE, оно все равно сможет использовать индекс.(Фильтрация только по дате кажется более вероятным сценарием, чем по уровню журнала).

Затем убедитесь, что таблица заполнена данными (столько данных, сколько вы будете использовать в работе), и обновите статистику в таблице..

Если таблица большая (не менее нескольких сотен тысяч строк), используйте следующий код для обновления статистики:

DECLARE
  l_ownname          VARCHAR2(255) := 'owner'; -- Owner (schema) of table to analyze
  l_tabname          VARCHAR2(255) := 'log'; -- Table to analyze
  l_estimate_percent NUMBER(3) := 5;  -- Percentage of rows to estimate (NULL means compute)
BEGIN
  dbms_stats.gather_table_stats (
     ownname => l_ownname ,
      tabname => l_tabname,
      estimate_percent => l_estimate_percent,
      method_opt => 'FOR ALL INDEXED COLUMNS',
      cascade => TRUE
  );
END;

В противном случае, если таблица небольшая, используйте * 1021.*

ANALYZE TABLE log COMPUTE STATISTICS FOR ALL INDEXED COLUMNS;

Кроме того, если таблица становится большой, вы должны разделить ее по диапазону в столбце creationDate.См. Эти ссылки для получения подробной информации:

0 голосов
/ 16 августа 2010

Как и в других ответах, не используйте «select *», если вам не нужны все поля.

logLevel и creationData имеют индексы

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

Обратите внимание, что оптимально индекс уменьшит стоимость запроса на запись (N), т. Е. Он будет медленнее по мере увеличения количества записей.

С

0 голосов
/ 16 августа 2010

Для меня есть две вещи, которые вы можете сделать,

  1. Горизонтальное разбиение таблицы на основе столбца даты

  2. Используйте концепцию предварительной агрегации.

Pre-агрегация: В preagg у вас будет таблица «logs», таблица «logs_temp», таблица «logs_summary» и таблица «logs_archive». Структура журналов и таблицы logs_temp идентична. Поток приложений будет таким: все журналы заносятся в таблицу журналов, а затем каждый час запускается задание cron, которое выполняет следующие действия:

а. Скопируйте данные из таблицы журналов в таблицу «logs_temp» и очистите таблицу журналов. Это можно сделать с помощью трюка с Shadow Table.

б. Объедините журналы за определенный час из таблицы logs_temp

с. Сохранить агрегированные результаты в сводной таблице

* * 1 022 д. Скопируйте записи из таблицы logs_temp в таблицу logs_archive, а затем очистите таблицу logs_temp.

Таким образом, результаты предварительно агрегируются в сводной таблице.

Всякий раз, когда вы хотите выбрать результат, вы должны выбрать его из сводной таблицы.

Таким образом, выбор выполняется очень быстро, потому что количество записей намного меньше, поскольку данные предварительно агрегированы в час. Вы можете даже увеличить порог с часа до дня. Все зависит от ваших потребностей.

Теперь вставки тоже будут быстрыми, поскольку объем данных в таблице журналов невелик, поскольку он содержит данные только за последний час, поэтому восстановление индекса на вставках займет очень меньше времени по сравнению с очень большими данными. -set, следовательно, делает вставки быстрыми.

Вы можете узнать больше о трюке с Shadow Table здесь

Я использовал метод предварительной агрегации на новостном сайте, построенном на WordPress. Мне пришлось разработать плагин для новостного сайта, который бы отображал недавно популярные (популярные за последние 3 дня) новостные выпуски, и их было около 100 тыс. Обращений в день, и эта предварительная агрегация действительно очень помогла нам. Время запроса сократилось с более чем 2 секунд до менее секунды. Я собираюсь сделать плагин общедоступным в ближайшее время.

0 голосов
/ 16 августа 2010

Пара вещей

нужны ли вам все столбцы, люди обычно делают SELECT *, потому что им лень перечислять 5 столбцов из 15, которые есть в таблице.

ПолучитьЧем больше ОЗУ, тем больше ОЗУ, тем больше данных может храниться в кеше, что в 1000 раз быстрее, чем чтение с диска

...