Повышение Производительности Серверной Части (Серия Из 3 Частей)
Индексы баз данных – это забота разработчиков. У них есть потенциал для повышения производительности функций поиска и фильтрации, которые используют SQL-запрос в серверной части. Во второй части этой серии статей я покажу влияние индекса базы данных на ускорение фильтров с помощью веб-приложения Java, разработанного с помощью Spring Boot и Ваадин .
Прочитайте часть 1 этой серии , если вы хотите узнать, как работает пример приложения, которое мы будем использовать здесь. Вы можете найти код на GitHub . Кроме того, и если вы предпочитаете, я записал видеоверсию этой статьи:
Требование
У нас есть веб-страница с сеткой, которая показывает список книг из базы данных MariaDB :
Мы хотим добавить фильтр, позволяющий пользователям этой страницы видеть, какие книги были опубликованы на определенную дату.
Реализация запроса и службы репозитория
Мы должны внести некоторые изменения в серверную часть, чтобы поддерживать фильтрацию данных по дате публикации. В классе репозитория мы можем добавить следующий метод:
@Repository public interface BookRepository extends JpaRepository{ Page findByPublishDate(LocalDate publishDate, Pageable pageable); }
При этом используется отложенная загрузка, как мы видели в части 1 этой серии статей. Нам не нужно реализовывать этот метод — Spring Data создаст его для нас во время выполнения.
Нам также необходимо добавить новый метод в класс обслуживания (который является классом, используемым пользовательским интерфейсом для запуска бизнес-логики). Вот как это делается:
@Service public class BookService { private final BookRepository repository; ... public StreamfindAll(LocalDate publishDate, int page, int pageSize) { return repository.findByPublishDate(publishDate, PageRequest.of(page, pageSize)).stream(); } }
Добавление фильтра на веб-страницу
С помощью серверной части, поддерживающей фильтрацию книг по дате публикации, мы можем добавить указатель даты на реализацию веб-страницы (или просмотра):
@Route("") public class BooksView extends VerticalLayout { public BooksView(BookService service) { ... var filter = new DatePicker("Filter by publish date"); filter.addValueChangeListener(event -> grid.setItems(query -> service.findAll(filter.getValue(), query.getPage(), query.getPageSize()) ) ); add(filter, grid); setSizeFull(); } ... }
Этот код просто создает новый объект DatePicker
, который прослушивает изменения его значения (через valuechangelistener). При изменении значения мы используем класс обслуживания для публикации книг в дату, выбранную пользователем. Затем соответствующие книги устанавливаются в качестве элементов Сетки
.
Тестирование медленного запроса
Мы внедрили фильтр, однако он работает крайне медленно, если у вас, например, 200 тысяч строк в таблице. Попробуй! Я написал статью , в которой объясняется, как создавать реалистичные демонстрационные данные для приложений Java. При таком количестве строк приложению потребовалось несколько секунд, чтобы отобразить данные на веб-странице на моем компьютере (MacBook Pro с четырехъядерным процессором Intel Core i5 с частотой 2,3 ГГц). Это полностью разрушает пользовательский интерфейс.
Анализ Запросов С Помощью Функции “Объяснить Запрос”
Если вы включили ведение журнала запросов, вы можете найти запрос, сгенерированный Hibernate, в журнале сервера. Скопируйте его, замените вопросительные знаки фактическими значениями и запустите его в клиенте базы данных SQL. На самом деле, я могу сэкономить вам немного времени. Вот упрощенная версия запроса:
SELECT id, author, image_data, pages, publish_date, title FROM book WHERE publish_date = '2021-09-02';
MariaDB включает в себя инструкцию EXPLAIN
, которая дает нам полезную информацию о том, как механизм оценивает, что собирается выполнить запрос. Чтобы использовать его, просто добавьте ОБЪЯСНИТЕ
перед запросом:
EXPLAIN SELECT id, author, image_data, pages, publish_date, title FROM book WHERE publish_date = '2021-09-02';
Вот результат:
В документации есть все, что вам нужно знать об этом, но важным моментом является значение в столбце тип : ВСЕ . Это значение говорит нам о том, что, по оценкам механизма, ему придется извлекать или считывать все строки в таблице. Не очень хорошая вещь.
Создание индекса
К счастью, мы можем легко исправить это, создав индекс в столбце, который мы используем для фильтрации данных: дата публикации
. Вот как это делается:
CREATE INDEX book\_publish\_date_index ON book(publish_date);
Индекс базы данных – это структура данных, созданная механизмом, обычно в виде b-дерева ( b для сбалансированного ), которая ускоряет процесс поиска определенной строки в таблице, то есть поиска строки с учетом значения в столбце, на котором построен индекс. Процесс происходит быстрее благодаря природе b-деревьев — они упорядочивают данные, уменьшая временную сложность с O(N) до O(log(N)) и даже O(log(1)) в некоторых случаях.
Тестирование улучшения
С построенным индексом мы можем снова запустить инструкцию EXPLAIN и увидеть, что в столбце тип отображается значение ref вместо ВСЕ :
Значение ref означает, что механизм будет использовать индекс при выполнении запроса. Важно, чтобы вы проверяли это при добавлении индексов в свои более сложные запросы. Всегда используйте инструкцию EXPLAIN
, чтобы дважды проверить, повышается ли производительность при введении индекса.
Если вы попробуете веб-приложение в браузере и выберете другую дату в окне выбора даты (нет необходимости перезапускать сервер), вы увидите огромную разницу! Например, данные извлекаются на моей машине менее чем за секунду, в отличие от нескольких секунд до создания индекса!
Повышение Производительности Серверной Части (Серия Из 3 Частей)
Оригинал: “https://dev.to/alejandro_du/improving-backend-performance-part-23-using-database-indexes-3eih”