Высокая загрузка процессора запросом mysql

У меня возникают проблемы с этим запросом, каждый раз, когда я его использую, загрузка процессора увеличивается с 5% до 67%-100%.

Я запускаю сервер mysql в ubuntu с помощью службы java, но даже если я выполняю запрос через любой ide mysql, результаты те же.

Я сделал некоторый поиск в Интернете по этому поводу, поэтому я публикую файл конфигурации mysql. Я добавляю некоторые атрибуты, которые я нашел в каком-то посте, но я думаю, что только усугубил ситуацию.

Ну, это мой файл my.cnf:

[mysqld]
innodb_file_per_table=1
innodb_buffer_pool_size = 256M
wait_timeout = 1800
local-infile=0
open_files_limit=10192
query_cache_size=128M
join_buffer_size=128K
thread_cache_size=4
table_cache=64
key_buffer_size=128M
user        = mysql
pid-file    = /var/run/mysqld/mysqld.pid
socket      = /var/run/mysqld/mysqld.sock
port        = 1336
basedir     = /usr
datadir     = /var/lib/mysql
tmpdir      = /tmp
lc-messages-dir = /usr/share/mysql
skip-external-locking
bind-address        = 0.0.0.0

key_buffer      = 2014M
max_allowed_packet  = 2014M
thread_stack        = 512K
thread_cache_size       = 1024
myisam-recover         = BACKUP
max_connections        = 200

query_cache_limit   = 2048M

log_error = /var/log/mysql/error.log

expire_logs_days    = 10
max_binlog_size         = 100M

[mysqldump]
quick
quote-names
max_allowed_packet  = 16M

[mysql]

[isamchk]
key_buffer      = 16M

!includedir /etc/mysql/conf.d/

Я использую этот запрос:

select regPosition.deviceId, count(regPosition.speed), max(regPosition.speed) from regPosition where (TIMESTAMPDIFF(MINUTE, lastPositionTime,now()) <= '5') and regPosition.speed >= '10' group by regPosition.deviceId;

Тип таблицы - Myisam, у нее около 2 МБ реестра и idPosition в качестве индекса. Это запрос на создание таблицы:

    CREATE TABLE `regPosition` (
  `idPosition` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Id autoincremental.',
  `deviceId` int(5) NOT NULL COMMENT 'Id numérico del equipo. Identificador único para cada vehículo.',
  `lastPositionTime` datetime NOT NULL COMMENT 'Fecha/hora en que se registra la marca de posición (realizada por el dvr).',
  `divisionew` varchar(2) DEFAULT NULL COMMENT 'Orientación  Este u Oeste.',
  `longitude` int(11) NOT NULL COMMENT 'longitud.',
  `divisionns` varchar(2) DEFAULT NULL COMMENT 'Orientación Norte o Sur.',
  `latitude` int(11) NOT NULL COMMENT 'Latitud.',
  `direction` int(11) DEFAULT NULL COMMENT 'Dirección en que apunta el dispositivo.',
  `gradeLon` varchar(100) DEFAULT NULL COMMENT 'Longitud transformada a grados (en decimal).',
  `gradeLat` varchar(100) DEFAULT NULL COMMENT 'Latitud transformada a grados (en decimal).',
  `speed` int(11) NOT NULL COMMENT 'Velocidad del vehículo. Registrada por el dvr',
  PRIMARY KEY (`idPosition`),
  KEY `index` (`idPosition`) USING HASH
) ENGINE=MyISAM AUTO_INCREMENT=6562682 DEFAULT CHARSET=latin1;

[РЕДАКТИРОВАТЬ]

Цель запроса - получить идентификатор устройства и количество раз, превышающее скорость, превышающую 10 (это просто пример, это может быть больше), и получить максимальную скорость, записанную базой данных.

Идея этого заключается в следующем: если скорость превышает 60 км/ч 5 раз за 5 минут, мне нужно знать идентификатор устройства, максимальную скорость и количество превышений ограничения скорости.

Если вы можете дать мне любую помощь, я был бы очень рад :).

Спасибо за помощь.


person Juan Enrique Riquelme    schedule 29.11.2013    source источник
comment
опубликуйте объяснение для вашего запроса   -  person iamkrillin    schedule 29.11.2013
comment
Выполнение запросов с WHERE к вычисляемым значениям практически гарантированно приведет к сканированию таблицы, и это плохая новость, если у вас много данных.   -  person tadman    schedule 29.11.2013
comment
Итак, что я мог сделать в этом случае? Я не могу изменить структуру таблицы, и мне нужно проверять ее каждые 5 минут или реже, чтобы включить сигнал тревоги, информирующий о нарушении ограничения скорости.   -  person Juan Enrique Riquelme    schedule 29.11.2013
comment
@JuanEnriqueRiquelme Вы говорите, что не можете изменить структуру таблицы. Но можно ли добавить в таблицу еще один индекс? Индекс на lastPositionTime должен сделать ваше предложение WHERE намного быстрее. Кроме того, если вы делаете это в другой программе, вы можете отслеживать максимальное количество idPosition, присутствующих в таблице на каждой итерации, а затем добавлять AND idPosition > {previousMaxIdPosition} к вашему запросу.   -  person dg99    schedule 29.11.2013
comment
Хороший вариант @ dg99. Попробую и расскажу, как он.   -  person Juan Enrique Riquelme    schedule 29.11.2013
comment
Вы также можете реорганизовать предложение WHERE. Используйте WHERE lastPositionTime > #foo# и замените #foo# в вашем языке сценариев (PHP, Java и т. д.) текущим временем меньше 5 минут (т. е. #foo# становится константой в этом запросе).   -  person AgRizzo    schedule 29.11.2013


Ответы (3)


Добавьте индекс:

 ... ON (deviceId, lastPositionTime, speed) 

и изменить запрос. Замените этот предикат:

TIMESTAMPDIFF(MINUTE, lastPositionTime,now()) <= '5'

с этим:

lastPositionTime >= NOW() + INTERVAL -5 MINUTE

Это даст вам покрывающий индекс для запроса (EXPLAIN покажет «Использование индекса»), что означает, что запрос может быть удовлетворен из индекса.

Вам нужен столбец deviceId первым, потому что MySQL может оптимизировать операцию GROUP BY, используя индекс с этим столбцом в качестве ведущего столбца (избегает операции сортировки).

Вам нужен предикат для голого столбца lastPositionTime, чтобы MySQL мог выполнить сканирование диапазона индекса. Когда этот столбец скрыт внутри функции (например, TIMESTAMPDIFF), MySQL должен оценивать эту функцию для каждой отдельной строки. Сканирование диапазона индексов выполняется быстрее, поскольку оно может исключить вычисление функции для целого ряда строк.

Поскольку для столбца speed также есть предикат, вы также хотите, чтобы он был в индексе.

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

person spencer7593    schedule 29.11.2013
comment
хорошо, я уже добавил индекс, но я не вижу никаких улучшений в использовании процессора. Возможно, я не использовал правильные настройки в файле index. Я использовал следующие настройки: Тип индекса: Нормальный Метод индекса: BTREE Я не знаю, подходят ли эти настройки. - person Juan Enrique Riquelme; 29.11.2013
comment
Я предлагаю вам взглянуть на вывод EXPLAIN SELECT ..., чтобы увидеть план выполнения запроса. Использование ЦП не обязательно является признаком того, что что-то не так... выполнение запроса будет потреблять некоторое количество ЦП, что действительно важно, так это то, что на самом деле делает ЦП, и как быстро выполняется запрос. - person spencer7593; 30.11.2013

Вот мой подход, и он должен пройти через таблицу ОДИН РАЗ без соединений с использованием переменных MySQL. Посылка, которую я применяю здесь, заключается в следующем. Упорядоченные результаты должны соответствовать идентификатору устройства и указанному времени. Переменные MySQL будут отслеживать... Работаю ли я на том же устройстве, что и последняя запись? И является ли текущая запись в пределах 5 минут от последней записи, которая либо запустила устройство, либо в последний раз превышала заданное ограничение скорости. и вот почему. Допустим, у меня есть отчет об определенном устройстве за 15-минутный период, начиная с 8:00, и отчет каждую минуту для упрощения. Скорости следующие:

8:00   58  -- Start group 8:00, set max time to still consider as 8:05
8:01   60  -- speeding... within the 8:00-8:05 range.  NEW end 5 minutes from now 8:06
8:02   58  -- not speeding
8:03   58  -- not speeding
8:04   59  -- not speeding
8:05   58  -- not speeding
8:06   59  -- not speeding... end of the 8:06 range, 1 over limit, ignore this
8:07   60  -- NEW cycle for device, start at 8:07 set ending time to 8:12 -- SPEEDING 1
8:08   61  -- STILL speeding max 61, extend ending time from 8:08 + 5 minutes to 8:13
8:09   62  -- STILL speeding max 62, extend ending time from 8:09 + 5 minutes to 8:14
8:10   59  -- not speeding
8:11   59  -- not speeding
8:12   60  -- SPEEDING AGAIN, within the 8:14 cutoff... reset 5 minutes from now 8:17
8:13   62  -- speeding still, extend to 8:18
8:14   64  -- speeding still, new max 64 and extend to 8:19
8:15   62  -- still speeding... total times 7 with max of 64 (so far)

если это не считается в вышеуказанном формате, вам придется считать все записи из

8:00 to 8:05
8:01 to 8:06
8:02 to 8:07, etc.

Для демонстрационных данных вы получите ускорение от

8:05 to 8:09
8:06 to 8:10
8:07 to 8:11, etc 

но этот способ продолжает смотреть на бегущую 5-минутную отсечку от последнего времени SPEEDING. если, например, между 8:20 и 8:26 человек сокращается до МЕНЬШЕ 60, то новый цикл начнется в следующий раз, когда он столкнется с превышением скорости. В противном случае посчитайте, сколько записей было бы возвращено, если бы человек превышал скорость с 8:00 до 10:00. У вас было бы 2 * 60 записей, ЕСЛИ бы отчеты были 1 в минуту ... если бы отчеты были более частыми, их было бы еще больше.

По мере изменения идентификатора устройства «первый раз» определяет начало следующего цикла рассмотрения превышения скорости. После обработки каждой записи этот идентификатор устройства помещается в переменную @lastDevice для следующего цикла сравнения. @nextTimeCutoff будет либо таким же, как в предыдущей строке, если в текущей записи не обнаружено превышение скорости, либо ДОБАВИТЬ 5 МИНУТ независимо от устройства... если устройство меняется, так как оно является последним для записи, следующая строка будет начинаться новый "LimitSequence" в любом случае увеличивается на 1.

Теперь, все сказанное, этот запрос упростит вам элементы. Если вы ЗНАЕТЕ ограничение скорости, о котором вы беспокоитесь (60 км / ч), мой внутренний запрос предварительно запрашивает ТОЛЬКО те записи, которые устройство помечено как превышение скорости. Если у вас есть 2 миллиона записей, и БОЛЬШИНСТВО из них НЕ ускоряются, то зачем вообще с ними возиться. Таким образом, внутренний запрос определяет ТОЛЬКО те, которые ускоряются, и помещает их в правильный порядок для обработки переменных mysql. Он будет обрабатывать период времени с момента первого превышения скорости, как описано выше.

select
      r1.DeviceID,
      @overLimitSeq := if( r1.DeviceID = @lastDevice 
                        AND r1.lastPositionTime <= @nextTimeCutoff,
                        @overLimitSeq, @overLimitSeq +1 ) as LimitSequence,
      min( r1.lastPositionTime ) as SpeedingTimeStart,
      max( r1.lastPositionTime ) as SpeedingTimeEnd,
      max( r1.speed ) as MaxSpeed,
      count(*) as TimesOverLimit,
      @lastDevice := r1.DeviceID as CompareForNextID,
      @nextTimeCutoff := r1.lastPositionTime + interval 5 minute as NextCutoff
   from
      ( select rp.deviceID,
               rp.lastPositionTime,
               rp.speed
           from
              regPosition rp
           where
              rp.speed >= 60
           order by
              rp.deviceID,
              rp.lastPositionTime ) r1,
      ( select @lastDevice := 0,
               @overLimitSeq := 0,
               @nextTimeCutoff := now() ) sqlvars
   group by
      r1.DeviceID,
      LimitSequence
   having
      TimesOverLimit > 2
   order by
      r1.deviceID,
      r1.lastPositionTime
person DRapp    schedule 30.11.2013

Я действительно нашел проблему с высокой загрузкой процессора.

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

Когда я использовал «объяснить» в запросе, я получил, что поиск был сделан в миллионах реестров, даже с прямым фильтром (даже это произошло с ограничением 100).

Причиной были индексы, поэтому я изменил порядок индексов, таким образом объяснение показывает, что поиск был выполнен менее чем в 400 регистрах, а загрузка процессора была идеальной.

Например этот sql:

EXPLAIN
SELECT d.deviceId, r.divisionew 
FROM 
device d, regPosition r 
WHERE 
d.enabled = 1 
AND d.deviceId = r.deviceId 
AND (DATE_SUB(now(),INTERVAL 8 MINUTE)) < r.lastPositionTime 
ORDER BY d.deviceId DESC

Этот выполнял поиск без использования индексов, поэтому запрос был выполнен с поиском около 8 миллионов реестров.

С переназначением индексов и правильным порядком этот запрос выполнялся для поиска менее чем в 400 строках.

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

Я просто меняю порядок индексов. У меня был до deviceId, lastPositionTime и, возможно, больше. С таким порядком индекс не работал, поэтому после изменения, добавления разделов, изменения порядка и удаления бесполезных параметров индекс работает как часы.

Поэтому, когда у вас возникли проблемы с высокой загрузкой процессора с помощью mysql, вы должны проверить порядок индексов, потому что, если у вас есть разработанные индексы, которые должны выполнять работу по ускорению запроса, и если это не так, вероятно, проблема с назначением индексов.

По крайней мере, это решило мою проблему.

Спасибо за вашу помощь.

person Juan Enrique Riquelme    schedule 15.09.2014