Принуждение MySQL к использованию неинклюзивного индекса, чтобы избежать сканирования таблицы? - PullRequest
1 голос
/ 07 марта 2011

Справочная информация: Огромная таблица с полем ID AUTO INCREMENT PRIMARY KEY и другой меткой времени столбца, в которой хранится метка времени UNIX в момент операции вставки строки. Этот столбец отметки времени отсутствует в каком-либо индексе, и я не могу его проиндексировать по соображениям производительности.

Ситуация: нам нужно запросить в этой огромной таблице строки в прошлом до определенного значения метки времени; мы можем выполнить оператор SELECT и указать это условие в предложении WHERE, но это приведет к полному сканированию таблицы, поскольку столбец отметки времени не проиндексирован.

Предложение. Характер этих двух столбцов увеличивается с каждой вставленной строкой: столбец AUTO INCREMENT увеличивается, а столбец отметки времени также увеличивается. Я могу разделить таблицу на два равных числа строк каждый раз и проверять временную метку на каждом из границ и так далее, пока не достигну одной строки, а затем выполнить нормальную SELECT для нее, используя этот идентификатор.

Проблема с этим решением: это сложно, и для выполнения этой операции требуется около 25 запросов, и число увеличивается с ростом таблицы.

Итак, вопрос: может ли MySQL получить указание на выполнение этой операции в атомарном контексте?

Ответы [ 3 ]

3 голосов
/ 07 марта 2011

Я бы попытался создать дополнительную таблицу для вашей основной, чтобы, по крайней мере, действовать как ограничивающая основа запроса. Заполните таблицу чем-то вроде ... (и, очевидно, поместите indexe по основанию даты)

create table DailyStartKey as 
SELECT 
      DATE( FROM_UNIXTIME( YourTimeStampColumn ) ) AS DateBasis,
      min( YourAutoIncColumn ) as FirstPKForDay
   from 
      YourTable
   group by 
      1

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

Такой запрос может быть использован ...

select
      YourTable.*
   from 
      ( select FirstPKForDay
            from DailyStartKey
            where DateBasis = "2011-02-12" ) StartDate,
      ( select FirstPKForDay
            from DailyStartKey
            where DateBasis = "2011-02-25" ) LastDate,
      YourTable
   where
         YourTable.YourAutoIncColumn >= StartDate.FirstPKForDay
     and YourTable.YourAutoIncColumn <= LastDate.FirstPKForDay

РЕДАКТИРОВАТЬ, чтобы уточнить другую реализацию TRIGGER.

Чтобы избежать необходимости продолжать запросы к таблице «DailyStartKey», вы можете создать другую таблицу, которая всегда будет содержать только одну запись с последним днем, для которого была создана запись. (или день / час в зависимости от желаемой степени детализации).

Затем в вашем триггере, чтобы узнать, нужна ли новая запись, просто

Select * from LastDateEntryTable where LastDate = CurrentDate

Если он уже найден, игнорируйте .. в противном случае триггер должен выполнить вставку в таблицу ежедневных ключей и обновить «LastDateEntryTable».

1 голос
/ 17 марта 2011

Импровизация по идее Дрэппа: создайте справочную таблицу и триггер, который добавляет 1 строку в эту таблицу на каждые 100 в большой. Эта таблица будет эквивалентна следующему представлению, но будет иметь индекс для datetimeBasis.

CREATE VIEW HundredRowsStartKey AS
  SELECT 
      YourTimeStampColumn AS datetimeBasis,
      YourAutoIncColumn AS id
    FROM 
      YourTable
    WHERE
      YourAutoIncColumn % 100 = 0
;

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

  • 2 поиска в справочной таблице (HundredRowsStartKey) и
  • 3 диапазон проверяет индекс большой таблицы.

Таким образом, запрос диапазона даты и времени:

SELECT *
  FROM 
    YourTable
  WHERE 
    YourTimeStampColumn BETWEEN "2011-02-12-01.00.23" 
                            AND "2011-03-15-12.00.00"
;

станет:

WITH starting AS
  SELECT
      max(id) AS startLow
    FROM
      HundredRowsStartKey h
    WHERE datetimeBasis <= "2011-02-12-01.00.23"
;

WITH ending AS
  SELECT
      max(id) AS endLow
    FROM
      HundredRowsStartKey h
    WHERE datetimeBasis <= "2011-03-15-12.00.00"
;

SELECT *
  FROM 
    YourTable
  WHERE
    -- these are guaranteed
    ( YourAutoIncColumn >= starting.startLow+100
    AND YourAutoIncColumn <= ending.endLow-1
    ) 
    -- and these 200 we have to filter
    OR
    ( ( YourAutoIncColumn BETWEEN starting.startLow 
                              AND starting.startLow+99
        OR
        YourAutoIncColumn BETWEEN ending.endLow 
                              AND ending.endLow+99
      )
      -- with the original filter
      AND
      ( YourTimeStampColumn BETWEEN "2011-02-12-01.00.23" 
                                AND "2011-03-15-12.00.00"
      )
    )
;

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

0 голосов
/ 17 марта 2011

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

http://dev.mysql.com/doc/refman/5.1/en/partitioning-overview.html

http://dev.mysql.com/tech-resources/articles/mysql_5.1_partitions.html

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