Плохая производительность присоединения к MySQL

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

У меня есть большая таблица (~ 2 миллиона записей), где один из столбцов является идентификатором в небольшой таблице (~ 3000 записей). В случае, если это имеет значение, идентификатор не уникален в большой таблице, но уникален в маленькой таблице. Я пробовал различные варианты следующего запроса, но ничего не работает:

SELECT big_table.*, 
       small_table.col 
  FROM big_table 
left outer join small_table on (big_table.small_id = small_table.id)

Я много анализирую данные, для которых требуются все 2 миллиона строк, хотя и не обязательно в одном запросе. Вот результаты моего «показать создание таблицы»:

'big_table', 'CREATE TABLE 'big_table' (
  'BIG_ID_1', varchar(12) NOT NULL,
  'BIG_ID_2', int(100) NOT NULL,
  'SMALL_ID' varchar(8) DEFAULT NULL,
  'TYPICAL_OTHER_COLUMN' varchar(3) DEFAULT NULL,
  ...
  PRIMARY KEY ('BIG_ID_1', 'BIG_ID_2')
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1'

'small_table', 'CREATE TABLE `small_table` (
  `id`, varchar(8) NOT NULL DEFAULT '''',
  `col`, varchar(1) DEFAULT NULL,
  ...
  PRIMARY KEY (`id`),
  KEY `inx_id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1'

Вот результат «объяснения» для одного из моих запросов-кандидатов:

id  select_type  table        type    possible_keys   key      key_len  ref                         rows     extra
1   SIMPLE       big_table    ALL     NULL            NULL     NULL     NULL                        1962193       
1   SIMPLE       small_table  eq_ref  PRIMARY,inx_id  PRIMARY  10       db_name.big_table.SMALL_ID  1             

person Michael McGowan    schedule 18.09.2010    source источник
comment
А какие у вас индексы? Если вы говорите, что нет, это, вероятно, проблема.   -  person James    schedule 18.09.2010
comment
Как вы выполняете запрос? В браузере запросов MySQL? Из языка программирования? Если последнее, то на каком языке и можете ли вы опубликовать соответствующую часть кода? Кроме того, зачем вам нужно читать 2 миллиона строк? Что вы будете делать со всеми этими данными?   -  person Mark Byers    schedule 18.09.2010
comment
И опубликуйте вывод EXPLAIN SELECT ... для вашего запроса, а также вывод SHOW CREATE TABLE big_table и SHOW CREATE TABLE small_table.   -  person Mark Byers    schedule 18.09.2010
comment
Я попытался выполнить запрос в MySQL Workbench, подключаемом модуле SQL Explorer Eclipse и прямо на Java с тем же результатом.   -  person Michael McGowan    schedule 18.09.2010
comment
Я добавил шоу и объяснил результаты в исходный пост. Как видите, у меня есть индекс по идентификатору small_table, но это, вероятно, ничего не делает, потому что это первичный ключ (правильно?). Будет ли индекс чего-то в big_table вообще что-то делать, учитывая, что мне все равно нужна каждая строка?   -  person Michael McGowan    schedule 18.09.2010
comment
@Michael McGowan: Вы правы в обоих случаях. Индекс в большой таблице был бы совершенно бесполезен для этого запроса, потому что читаются каждая строка и каждый столбец этой таблицы. Самый эффективный способ сделать это — полное сканирование таблицы. Индекс inx_id в маленькой таблице полностью избыточен, поскольку первичный ключ также может использоваться в качестве индекса — я бы рекомендовал вам удалить индекс, но сохранить первичный ключ. Однако это не улучшит производительность этого запроса.   -  person Mark Byers    schedule 19.09.2010
comment
@Michael McGowan: Вы уверены, что вам нужно выбрать все столбцы из большой таблицы? Было бы очень хорошо, если бы вы могли немного рассказать о том, что вы пытаетесь сделать. Я знаю, что прошу у вас много информации, но предоставление этой дополнительной информации поможет нам дать ответ, который будет более полезным для вас.   -  person Mark Byers    schedule 19.09.2010


Ответы (1)


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

Что вы можете попробовать:

  • Если вам не нужны все столбцы, запросите нужные вам столбцы вместо использования SELECT table.*.
  • Посмотрите, можете ли вы перенести часть (или всю) обработку в базу данных вместо извлечения данных и их обработки в клиенте.
  • Избегайте считывания всего набора результатов в память за один раз.
  • Обрабатывайте строки партиями по несколько тысяч за раз, а не извлекайте их все сразу.
person Mark Byers    schedule 18.09.2010
comment
Да, конечно, ограничение набора результатов WHERE или LIMIT ускорит его, но тогда запрос не делает то, что я хочу. Я действительно хочу обработать все 2 миллиона записей, так как это мне поможет? Или вы предлагаете мне превратить этот запрос в множество небольших запросов с LIMIT 0, 10000, LIMIT 100001, 200000 и т. д.? По своей наивности я бы предположил, что последнее не должно быть необходимым или полезным... - person Michael McGowan; 18.09.2010
comment
@Michal McGowan: Какая обработка? Рассматривали ли вы возможность обработки данных в базе данных вместо передачи их клиенту для обработки? Я также думаю, что было бы неплохо опубликовать код Java, который вы используете для выполнения запроса, и прочитать результаты. У вас может быть проблема там. Если возможно, вы хотите обрабатывать одну строку за раз, а не читать весь набор результатов за один раз. Разделение на пакеты также может быть хорошей идеей, но использование LIMIT со смещением не является оптимальным способом сделать это, так как это будет становиться все медленнее и медленнее по мере увеличения смещения. - person Mark Byers; 19.09.2010
comment
Я думаю, что обработка его в базе данных сделает многое из того, что я хотел; Благодарю. По какой-то причине я по глупости не понял, что выбор поместит все это в память. - person Michael McGowan; 19.09.2010
comment
@Michael McGowan: Я думаю, считывается ли весь набор результатов в память или нет, зависит от того, какой API вы используете и как вы его используете. Я просил вас опубликовать свой код, чтобы я мог видеть, правильно ли вы это делаете. Но если вы можете выполнить операцию в базе данных, то я думаю, что это даже лучшее решение. - person Mark Byers; 19.09.2010
comment
Спасибо за помощь. Я считаю, что создал объект java.sql.Statement и вызвал метод executeQuery. Несмотря на то, что в этом случае я считаю, что действительно могу обрабатывать в базе данных, было бы неплохо иметь возможность перебирать каждую запись в наборе записей. - person Michael McGowan; 19.09.2010