動画

SQL: カバーリングインデックスと必要なものだけをフェッチ

データベースから必要なものだけをフェッチする際に利用できる最適化

概要

部分オブジェクトとは、エンティティBeanがすべてのプロパティで完全に設定されているのではなく、そのプロパティの一部のみが設定/ロードされている状態を指します。

部分プロパティは、データベースJDBC、およびネットワークのパフォーマンスの観点から非常に重要であり、Ebeanのすべては、部分的に設定されたBeanをサポートするように構築されています。クエリ言語、エンハンスメント、永続化、イベントアダプター、およびEbeanのすべての内部処理は、部分的に設定されたBeanが標準であるという前提で動作します。

インデックスのみのクエリ

使用されるすべての列がインデックスに含まれているSQLクエリを実行すると、データベースはインデックスのみを使用してクエリ結果を返し、テーブルに関連付けられたデータブロックの読み取りを特に回避することができます。これは、「カバーリングインデックス」または「カバーリングクエリ」と呼ばれることがあります。

選択された列数が少ない = インデックスのみのクエリの機会
database.find(Customer.class)

// only fetch the customer id and name
// ... if there is an index covering id and name the database has the ability
// ... to avoid reading the customer table data blocks and instead resolve the
// ... query by only using the index (which can be much faster)
.select("name")

// predicates and order by covered by index
.where().name.istartsWith("rob")
.orderBy().name.asc()
.findList();

インデックスのみの結合

「インデックスのみのクエリ」と同様に、結合テーブルで必要/使用される列がインデックスによって完全に解決できる場合も同様です。たとえば、顧客テーブルを結合し、顧客名のみが必要な場合などです。同様に、製品テーブルを結合し、製品名のみが必要な場合などです。これらは、インデックスのみを使用し、実際にテーブルのデータブロックを読み取り/アクセスする必要がない(インデックスブロックを読み取るだけで結合が解決され、テーブルブロックの読み取りを回避できる)ことで、データベースによって結合が解決されることが多い例です。

結合されたテーブルで選択された列数が少ない = インデックスのみの結合の機会
database.find(Order.class)
...
// only fetch the customer id and name
// ... if there is an index on the customer table that is covering the customer id and name
// ... the database has the ability to avoid reading the customer table data blocks and instead
// ... resolve the join part of this query from only the customer index (which is faster)
.fetch("customer", "name")
...
.findList();

データベースバッファ

データベースがクエリを処理するとき、ソートや結合などを実行するためのバッファを作成します。これらのバッファは、データが多いため、関与する列が多いほど大きくなります。必要な列のみを選択できる場合、これらのバッファとソートや結合などに関与するデータ量が削減されます。除外できる列が大きなvarchar列である場合や、エンティティBeanが多数の列を持つテーブルにマップされている場合は、これがより重要になります。

選択された列が少ない = DBがソート/結合などを処理するために使用するバッファが小さくなる

JDBCバッファ

JDBCドライバーにも、resultSetデータをネットワーク経由で転送するためのバッファがあります。列が多いほど、JDBCバッファが大きくなり、JDBCドライバーがより多くのメモリを必要とします。

選択された列が少ない = JDBCドライバーが使用するバッファが小さくなる

ネットワーク

フェッチに含まれる列が多いほど、より多くのデータがネットワーク経由で転送されます。一部のJDBCドライバーは、値が繰り返される場合にネットワークペイロードを削減するために、列ごとの圧縮のようなものを持っていることに注意してください(繰り返される値はSQL resultSetデータで非常に一般的な現象であるため)。

選択された列が少ない = ネットワーク経由で転送されるデータが少なくなる

選択 / フェッチ

Ebeanでは、select()fetch()を使用して、クエリでフェッチするものを簡単に制御できます。select()は、ルートレベルでフェッチ/ロードする特定のプロパティを定義するために使用され、fetch()は、他のすべてのレベルでフェッチ/ロードするプロパティを定義するために使用されます(さらに、遅延/即時ロードおよびロードバッチサイズなどを制御できます)。

// control the lazy loading of customers ...
List<Order> list = Ebean.find(Order.class)
   // fetch just these properties for the Order
  .select("status,orderDate,shipDate")
  // fetch customer name only
  .fetch("customer","name")
  .fetch("customer.shippingAddress", "*")
  .fetch("details", "*")
  // fetch product name only
  .fetch("details.product", "name")
  .where().eq("status",Order.Status.NEW)
  .findList();

AutoTune

AutoTuneは、各クエリを各ユースケースに合わせて調整し、最小限のプロパティセットをフェッチします。

必要なものだけをフェッチするようにクエリを手動で最適化するのは手間がかかる場合があります。また、そうした場合でも、「idで注文を検索」のような単一のクエリが複数のユースケースで使用され、各ユースケースがオブジェクトグラフの異なる部分を使用するというケースがよくあります。

AutoTuneは、各クエリの呼び出しスタックに対するオブジェクトグラフの使用状況をプロファイリングし、特定の呼び出しスタックに使用されるものだけを選択/フェッチするクエリを提供できる機能です。つまり、アプリケーションが使用するものだけをフェッチするようにクエリを最適化する、優れた自動化された方法を提供します。