2012-04-21 3 views
2

У меня есть две таблицы в MySQL, «приложения» и «значки», каждая из которых содержит около 750 тыс. Строк. В Hibernate я моделировал им нравится:MySQL и Hibernate: производительность левого внешнего соединения против нескольких выборок

public class App { 
    @Basic 
    private String title; 

    @OneToOne(mappedBy = "app") 
    private Icon icon; 
    // etc... 
} 

public class Icon { 
    @Basic 
    private String name; 

    @OneToOne 
    private App app; 
    // etc... 
} 

Когда я добавил это соотношение я быстро побежал в чтение производительности проблемна в одном приложении везли> 1 секунду. Я исследовал SQL, что спящий режим был продуцирующей и обнаружил, что это присоединение, как это:

select 
    apps.id as app_id, 
    apps.title as app_title, 
    icons.id as icon_id, 
    icons.name as icon_name 
from 
    apps 
left outer join 
    icons 
     on apps.id=icons.app_id 
where 
    apps.id="zyz"; 

Я обнаружил, что добавление @Fetch(FetchMode.SELECT) к аннотациям значительно ускорило работу, доведя его до около 30ms для эффективного и тому же результата. Вот полученный SQL с @Fetch(FetchMode.SELECT) аннотацию:

select 
    apps.id as app_id, 
    apps.title as title 
from 
    apps 
where 
    apps.id="xyz"; 


select 
    icons.id as icon_id, 
    icons.name as icon_name 
from 
    icons 
where 
    icons.app_id="xyz"; 

Почему левое внешнее соединение так намного медленнее? «Объяснить» на присоединился запрос показывает:

+----+-------------+-------+-------+---------------+---------+---------+-------+--------+-------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra | 
+----+-------------+-------+-------+---------------+---------+---------+-------+--------+-------+ 
| 1 | SIMPLE  | apps | const | PRIMARY  | PRIMARY | 767  | const |  1 |  | 
| 1 | SIMPLE  | icons | ALL | NULL   | NULL | NULL | NULL | 783556 |  | 
+----+-------------+-------+-------+---------------+---------+---------+-------+--------+-------+ 

Так это, по-видимому посещения каждой строки, против одной строки для множественного выбора запроса. Не может ли соединение использовать индекс, который у меня есть на icons.app_id?

PS: Да, я использовал «RESET QUERY CACHE» между сеансами синхронизации.

Update: переехал на первичный ключ bigint, использовал это, чтобы присоединиться к таблицам в вместо VARCHAR и производительности объединения в настоящее время на одном уровне с методом «несколько выбирает».

ответ

2

Согласно вашему объяснению, я считаю, что проблема связана с вашей схемой на уровне базы данных, а не на уровне приложения.

Тот факт, что соединение с таблицей значков не имеет записи для possible_keys, заставляет меня полагать, что вы используете механизм хранения MyISAM или движок хранения InnoDB без ограничений FK. Кроме того, длина ключа 767 ударяет меня как необычные, я только когда-либо видел это значение < 10.

  • Если двигатель MyISAM: Добавить индекс в icons.app_id колонки. И подумайте об использовании механизма InnoDB, чтобы вы могли установить ограничения FK, чтобы вы не попали в сиротские строки.

  • Если двигатель InnoDB: добавьте ограничение FK к icons.app_id, которое ссылается на apps.id. Добавляя ограничение FK, вы не только гарантируете, что ваши данные не становятся сиротами, но и оптимизируете соединения между таблицами, потому что вы вынуждены создавать индекс для обоих столбцов.

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

Вы можете прочитать более подробную информацию о некоторых обсуждаемых тем с помощью этих ссылок:

- Обновление -

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

Для таблицы приложений:

ALTER TABLE apps 
    ADD COLUMN idi int(11) UNSIGNED auto_increment FIRST, 
    DROP PRIMARY KEY, 
    ADD PRIMARY KEY(idi); 

Для таблицы иконок:

ALTER TABLE icons 
    ADD COLUMN app_idi int(11) UNSIGNED auto_increment AFTER app_id, 
    ADD INDEX (app_idi), 
    ADD FOREIGN KEY (app_idi) REFERENCES apps(app_idi) ON DELETE CASCADE; 

Эти изменяет является демонстративным только, но должен быть достаточно, чтобы вы начали в правильном направлении. Вы можете прочитать в документах MySQL, которые я опубликовал о внешних ограничениях ключей для получения дополнительной информации. Теперь с установкой ограничений FK между приложениями и значками, если какие-либо приложения будут удалены, а также строки с значками, соответствующими app.id, также будут удалены, гарантируя, что вы не потеряете никаких данных. Если вы не хотите удалять связанные строки в таблице icons, вы можете изменить ON DELETE CASCADE на ON DELETE NULL, и они будут отсоединены от таблицы apps, но все еще находятся в таблице icons.

+0

Действительно, это MyISAM, автогенерированный из устаревшего приложения Hibernate. На самом деле уже есть индекс на icons.app_id. Я заинтересован в переходе на InnoDB, особенно если производительность, скорее всего, улучшится, но я прочитал обратное (возможно, дезинформировал?) Ключами являются VARCHAR (255) (опять же, бит Hibernate autogenerated mess). Изменил бы PK на более традиционный целочисленный тип, а не на нашу эффективность бизнес-ключа? Большое спасибо за ваше время, спасибо. –

+0

Я всегда обнаружил, что с использованием типов int datatypes работают лучше, чем не-int, когда дело доходит до индексирования. Одна вещь, которую вы можете сделать, это создать int PK в новой таблице, но при этом сохранить старый ключ varchar для устаревших ссылок. Затем при обновлении приложения попробуйте вместо этого использовать новый столбец PK. Можете ли вы опубликовать схемы таблиц? Возможно, вы сможете предложить более глубокое понимание. –

+0

Уверен: http://pastebin.com/BUKWszxP –

Смежные вопросы