Я продолжаю сталкиваться с ситуациями, когда пользователи RDBMS думают, что одна секунда для выполнения запроса — что-то почти быстрое. Совсем недавно, в этом вопросе переполнения стека:
Предложение Hibernate SQL In делает загрузку процессора до 100%
Первоначальный вопрос автора заключался в том, почему аналогичный запрос выполняется за одну секунду при выполнении в SQL Server Management Studio, тогда как (по-видимому) тот же запрос выполняется за 60 секунд при выполнении из Hibernate. Запрос выглядит примерно так:
1
2
3
|
select Student_Id from Student_Table where Roll_No in ( 'A101' , 'A102' , 'A103' ,..... 'A250' ); |
Там может быть много причин для этого различия. Скорее всего, где-то спрятана проблема Hibernate N + 1 . Но самое яркое сообщение здесь:
Пожалуйста, не верьте, что 1 секунда — это быстро.
Базы данных — это невероятно быстрые и простые запросы, подобные приведенному выше, которые должны выполняться практически мгновенно, даже на посредственных серверах. Даже на вашем ноутбуке! Маркус Винанд сказал, что в 80% всех проблем с производительностью все, что вам нужно сделать, это добавить этот отсутствующий индекс . И это также здесь!
Исходная таблица автора содержит только два указателя:
01
02
03
04
05
06
07
08
09
10
|
CREATE TABLE student_table ( Student_Id BIGINT NOT NULL IDENTITY , Class_Id BIGINT NOT NULL , Student_First_Name VARCHAR (100) NOT NULL , Student_Last_Name VARCHAR (100) , Roll_No VARCHAR (100) NOT NULL , PRIMARY KEY (Student_Id) , CONSTRAINT UK_StudentUnique_1 UNIQUE (Class_Id, Roll_No) ); |
Существует индекс для реализации PRIMARY KEY
, и есть еще один индекс для ограничения UNIQUE
, но оба индекса не очень полезны, так как предикат запроса фильтрует Roll_No
, который является только вторым столбцом ограничения UNIQUE
. При выполнении вышеупомянутого запроса для примерно 8 миллионов строк я получаю сканирование индекса по индексу UNIQUE
и запрос выполняется за три секунды:
Эта операция «индексного сканирования» совсем не годится. Я на самом деле бегу по всему индексу, чтобы найти все применимые значения Roll_No
в каждом узле листа индекса. Это хорошо объяснено на странице «Использование индекса» Люка о составных индексах.
Решение
Но хорошие новости в том, что SQL Server Management Studio дает вам немедленный совет по настройке. Просто щелкните правой кнопкой мыши план выполнения и выберите «Отсутствующие сведения об индексе…», чтобы получить следующий совет:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
/* Missing Index Details from SQLQuery1.sql - LUKAS-ENVY\SQLEXPRESS. test The Query Processor estimates that implementing the following index could improve the query cost by 87.5035%. */ /* USE [ test ] GO CREATE NONCLUSTERED INDEX [<Name of Missing Index>] ON [dbo].[student_table] ([Roll_No]) INCLUDE ([Student_Id]) GO */ |
Это не обязательно означает, что вышеуказанный индекс является оптимальным выбором для всех ваших запросов, но тот факт, что вы запрашиваете использование предиката для Roll_No
должен быть достаточно сильным показателем того, что у вас должен быть индекс хотя бы для этого Roll_No
колонка. Самый простой возможный индекс здесь просто:
1
2
|
CREATE INDEX i_student_roll_no ON student_table (Roll_No); |
С этим индексом мы получим операцию «Поиск индекса», которая выполняется мгновенно:
Индекс покрытия
В данном конкретном случае может быть уместен «покрывающий индекс», предложенный Владом Михалчей в его ответе . Например:
1
2
|
CREATE INDEX i_student_roll_no ON student_table (Roll_No, Student_Id); |
Преимущество покрывающего индекса состоит в том, что вся информация, необходимая для выполнения запроса, уже содержится в индексе . Это верно в данном конкретном случае, но также может быть опасно, так как:
- Индексу покрытия требуется больше места, и если таблица уже большая, пространство может стать проблемой
- Индекс покрытия добавляет значение только в том случае, если в запросе также не используются дополнительные столбцы (например, для проекций, расчетов, дополнительной фильтрации, сортировки и т. Д.). Это, вероятно, не тот случай в этом простом примере, который может быстро измениться в ближайшем будущем
Таким образом, индекс покрытия не должен быть вашим выбором по умолчанию в таких случаях. Лучше быть консервативным и добавлять только те столбцы в индексе, которые добавляют немедленное значение для фильтрации.
Вывод
Я хотел бы повторить это снова и снова:
НЕ думайте, что одна секунда — это быстро
По факту:
НЕ думайте, что что-то за 2-3 мс быстро!
Если вы не занимаетесь сложными отчетами или пакетной обработкой, где время обработки, очевидно, может занять немного больше времени, простые запросы, подобные этим, никогда не должны быть медленнее, чем мгновенные . И большую часть времени вы можете достичь этой скорости, добавив индекс.
На заметку
У автора вышеупомянутого вопроса, очевидно, есть и другие проблемы. Ничто из вышеперечисленного не объясняет разницу в скорости выполнения между Hibernate и «простым SQL». Но опять же, сообщение здесь заключается в том, что если ваш «простой SQL» уже занимает больше одной секунды, у вас есть очень низко висящий плод, который нужно исправить.
Ссылка: | Не думайте, что одна секунда — это быстрое выполнение запросов от нашего партнера по JCG Лукаса Эдера из блога JAVA, SQL и JOOQ . |