バックエンド性能の改善パート2/3データベースインデックスの使用


データベースインデックスは開発者の関心です.バックエンドでSQLクエリを使用する検索機能とフィルタ機能のパフォーマンスを向上させる可能性があります.このシリーズの第2部では、データベースインデックスがJava Webアプリケーションを使用してフィルタを高速化しているという影響を示しますSpring Boot and Vaadin .
読めるpart 1 of this series ここで使用する例のアプリケーションがどのように動作するかを学びたい場合.あなたはcode on GitHub . また、あなたが好むならば、私はこの記事のビデオ版を録画しました:

要件
我々は、Aからの本のリストを示すグリッドでウェブページを持っていますMariaDB データベース

私たちは、このページのユーザーが、どの本が特定の日付に出版されたかを見るのを許すためにフィルタを加えたいです.

リポジトリクエリとサービスの実装
我々は、発行日によってデータをフィルタリングするのをサポートするためにバックエンドで若干の変化をしなければなりません.リポジトリクラスでは、次のメソッドを追加できます.
@Repository
public interface BookRepository extends JpaRepository<Book, Integer> {

    Page<Book> findByPublishDate(LocalDate publishDate, Pageable pageable);

}
これは、このシリーズの第1部で見たように怠惰な負荷を使用します.このメソッドを実装する必要はありません.Spring Data 実行時に私たちのためにそれを作成します.
また、サービスクラス(ビジネスロジックを実行するUIを使用するクラス)に新しいメソッドを追加する必要があります.以下に
@Service
public class BookService {

    private final BookRepository repository;

    ...

    public Stream<Book> findAll(LocalDate publishDate, int page, int pageSize) {
        return repository.findByPublishDate(publishDate, PageRequest.of(page, pageSize)).stream();
    }

}

Webページへのフィルタの追加
バックエンドが発行日によって書籍のフィルタリングをサポートしているので、Webページ(またはView)の実装に日付ピッカーを追加できます.
@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 値の変更を聞くオブジェクト(値変更リスナーを介して).値を変更すると、サービスクラスを使用して、ユーザーが選択した日付に発行された書籍を取得します.マッチした本は、それからGrid .

遅いクエリのテスト
我々は、フィルタを実装しているしかし、例えば、テーブルの中に2 , 000の行があれば、非常に遅いです.それをお試しください!私は書いたarticle Javaアプリケーション用の現実的なデモデータを生成する方法について説明します.行のこの数で、私のマシン(MacBook Pro 2、3 GHzのクワッドコアインテルコアi 5)上のWebページ上のデータを表示するには、アプリケーションを数秒かかりました.これは完全にユーザーエクスペリエンスを台無しにします.

説明クエリによるクエリの解析
クエリのログを有効にすると、Hibernateによって生成されたクエリがサーバーのログに表示されます.コピーして、クエリーマークを実際の値に置き換え、SQLデータベースクライアントで実行します.実際には、私はいくつかの時間を節約できます.以下に簡単なクエリのバージョンを示します.
SELECT id, author, image_data, pages, publish_date, title
FROM book
WHERE publish_date = '2021-09-02';
マリアブはEXPLAIN クエリを実行するエンジンの推定方法についての有益な情報を与えるステートメント.それを使用するにはEXPLAIN クエリの前に:
EXPLAIN SELECT id, author, image_data, pages, publish_date, title
FROM book
WHERE publish_date = '2021-09-02';
結果を以下に示します.
The documentation あなたがそれについて知っている必要があるすべてを持っています、しかし、重要なビットはタイプコラムで値です:すべて.この値は、エンジンがテーブル内のすべての行を取得または読み込む必要があると推定します.良いことではない.

インデックスの作成
幸いにも、データをフィルタリングするために使用している列にインデックスを作成することで簡単に修正できます.publish_date . 以下に
CREATE INDEX book\_publish\_date_index ON book(publish_date);
データベースインデックスは、エンジンによって作成されたデータ構造であり、通常B - tree(平衡のためのB)であり、テーブル内のある行を見つけるプロセス、すなわちインデックスが構築された列の値を与えられた行を検索します.プロセスはB -トリーの性質のおかげで速くなります.データを順序順にO(n)からO(log(n))とO(log(1))まで減らします.

改善の試験
インデックスを構築すると、再度説明文を実行することができます.
ref値は、クエリを実行するときにエンジンがインデックスを使用することを意味します.インデックスをより複雑なクエリに追加するときにチェックすることが重要です.常に使用するEXPLAIN あなたがインデックスを導入するとき、あなたがパフォーマンスを得ていることを二重にチェックする声明.
場合は、ブラウザでWebアプリケーションを試してみて、日付ピッカー(必要はありませんサーバーを再起動する)で別の日付を選択すると、巨大な違いが表示されます!例えば、私たちがインデックスを作成する前に、数秒と対照的に、データは私のマシンの上の1秒未満で検索されます!