1

Firefox の SQLiteManager プラグインでテストしたクエリに問題があります。

この特定のクエリは、Firefox プラグインでは約 60 ミリ秒で実行されますが、最新の Sqlite JDBC ドライバーを使用して Java で同じクエリと同じデータベースを使用すると、実行になんと 3.7 秒かかります。

Firefox プラグインは、正常に動作する他のクエリよりも少し高速であることがよくあります (上部で 50 ミリ秒速く、JDBC の方が速い場合もあります)。これはおそらく、接続を作成して結果をリストに追加するオーバーヘッドですが、この特定のクエリのパフォーマンスの違いはばかげています。

クエリは次のとおりです。

SELECT p1.Id, p1.FirstName || ' ' || p1.LastName AS PlayerName, sch1.LaneNum, l1.Name AS LeagueName, l1.Season, SUM(s1.Score) AS Series, e1.Date FROM Scores s1
JOIN SchedulePlayers sp1 ON  s1.SchedulePlayerId = sp1.Id
JOIN Schedules sch1 ON sp1.ScheduleId = sch1.Id
JOIN Players p1 ON sp1.PlayerId = p1.Id
JOIN TeamEncounters te1 ON sch1.TeamEncounterId = te1.Id
JOIN Encounters e1 ON te1.EncounterId = e1.Id
JOIN Leagues l1 ON e1.LeagueId = l1.Id

WHERE s1.GameNum < 4 AND l1.Name LIKE 'Juniors%' AND l1.Season = 2013 AND (sch1.LaneNum = 1 OR  sch1.LaneNum = 2) AND s1.IsBowlout = 0
GROUP BY p1.Id, l1.Id, e1.Id
ORDER BY Series DESC LIMIT 0,20

明らかに、遅い部分は「LIKE 'Juniors%'」ですが、プラグインではなく Java で遅い理由を説明していません。

EXPLAIN QUERY PLANを実行すると、firefox プラグインが Leagues テーブルに次のインデックスを使用していることがわかります: Columns: "Season, Name, RealName" (RealName はこのクエリではまだ使用されていません)。

Java でEXPLAIN QUERY PLANを実行すると、リーグ テーブルに使用されるインデックスは INTEGER PRIMARY KEY インデックスであり、ここに問題があると思います。

Java で上記のクエリを実行し、同じ接続を使用して同じクエリをさらに 2 回実行しますが、2 回目はl1.Name LIKE 'Juniors%の部分をp1.Sex = 1およびp1.Sex = 2に置き換えます。 . これらの最後の 2 つのクエリはどちらの場合も高速であり、問​​題がl1.Name LIKE 'Juniors%'に起因することをさらに証明しています。

すべてのテーブルに主キーがあり、それを必要とするすべての列に外部キーがあります。多くの繰り返しフィールドがあり、さらに高速にするためにインデックスを追加することにしたため、古いデータベースをゼロから再設計しているため、他にも多くのインデックスがありますが、この特定のケースでは、特に1つで動作するため、行き詰まっていますケースですが、別のものではありません。テーブルのインデックスを積極的に作成しすぎたために、マネージャーが正しいインデックスを選択するのが難しくなった可能性はありますか?

テーブル、列、クエリなどの詳細については、お気軽にお問い合わせください。

編集

Firefox プラグインは SQLite 3.7.17 を使用し、JDBC ドライバーは SQLite 3.8.0 を使用します。3.7.20 JDBC ドライバーを使用してみましたが (3.7.17 ドライバーへのダウンロード リンクが見つかりませんでした)、同じパフォーマンスの問題が発生し、他のクエリのパフォーマンスが低下したため、3.8 に切り替えました。 .0.

ベンチマーク時にミスを犯したため、パフォーマンス時間を編集しました。以前の時間は、クエリを複数回実行するためのものでした。したがって、Firefox ではクエリを 1 回実行するのに約 60 ミリ秒かかりますが、Java では 3600 ミリ秒かかるため、60 倍以上かかり、私のアプリケーションでは受け入れられません。

以下は、Java クエリ実行からの詳細な EXPLAIN QUERY PLAN です。列は、SelectId、Order、From、Detail の順に並んでいます。

0 0 0 SEARCH TABLE Scores AS s1 USING INDEX idxScoresGameNumScore (GameNum<?)
0 1 1 SEARCH TABLE SchedulePlayers AS sp1 USING INTEGER PRIMARY KEY (rowid=?)
0 2 3 SEARCH TABLE Players AS p1 USING INTEGER PRIMARY KEY (rowid=?)
0 3 2 SEARCH TABLE Schedules AS sch1 USING INTEGER PRIMARY KEY (rowid=?)
0 0 0 EXECUTE LIST SUBQUERY 1
0 4 4 SEARCH TABLE TeamEncounters AS te1 USING INTEGER PRIMARY KEY (rowid=?)
0 5 5 SEARCH TABLE Encounters AS e1 USING INTEGER PRIMARY KEY (rowid=?)
0 6 6 SEARCH TABLE Leagues AS l1 USING INTEGER PRIMARY KEY (rowid=?)
0 0 0 USE TEMP B-TREE FOR GROUP BY
0 0 0 USE TEMP B-TREE FOR ORDER BY

ご覧のとおり、リーグは整数の主キーを使用するため、「名前」を含むインデックスは完全に無視されます。

Firefox プラグインの EXPLAIN QUERY PLAN は次のとおりです。

0 0 6 SEARCH TABLE Leagues AS l1 USING COVERING INDEX idxLeaguesRealName (Season=?) (~19 rows)
0 1 5 SEARCH TABLE Encounters AS e1 USING INDEX idxEncounters (LeagueId=?) (~16 rows)
0 2 4 SEARCH TABLE TeamEncounters AS te1 USING AUTOMATIC COVERING INDEX (EncounterId=?) (~6 rows)
0 3 2 SEARCH TABLE Schedules AS sch1 USING INDEX sqlite_autoindex_Schedules_1 (TeamEncounterId=?) (~1 rows)
0 4 1 SEARCH TABLE SchedulePlayers AS sp1 USING COVERING INDEX idxSchedulePlayers (ScheduleId=?) (~6 rows)
0 5 3 SEARCH TABLE Players AS p1 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)
0 6 0 SEARCH TABLE Scores AS s1 USING INDEX sqlite_autoindex_Scores_1 (SchedulePlayerId=? AND GameNum<?) (~1 rows)
0 0 0 USE TEMP B-TREE FOR GROUP BY
0 0 0 USE TEMP B-TREE FOR ORDER BY

ご覧のとおり、テーブルの順序も同じではなく、実際には、すべてのテーブルが Java の PRIMARY KEY インデックスを使用しているのに対し、Java では「より良い」インデックスと思われるものを使用していますが、これは奇妙だと思います。

* JOIN Leagues l1 の後でINDEXED BY idxLeaguesRealName * を使用してみましたが、パフォーマンスは同じままです (おそらく、SEARCH TABLE Leagues が最初のテーブルではなく Java で一番下にあるため)。

idxLeaguesRealName は、@CLによって投稿されたこのリンクの 5.3 によると、Season、Name、RealName のインデックスです。シーズンは 230 の異なるリーグに対して約 4 つの異なる値しかとらないため、低品質のインデックスです。ただし、クエリを実行する前にANALYZEコマンドを実行したため、そのリンクによると、低品質のインデックスを使用する問題を修正する必要があります

私が試したもう 1 つのことは、主キー フィールド (ID、季節、名前など) も使用する新しいインデックスを作成することですが、クエリ プランナーはそれを使用しません。ユーザーが作成したインデックスのフィールドの 1 つとして主キーを配置することが良い考えかどうかもわかりません。クエリを実行する2つの方法のパフォーマンスの違いを理解していないため、ここで迷っているため、考えられることはすべて試しています。

ほとんど同じである他のクエリに関する追加情報

前述したように、l1.Name LIKE 'Juniors%'がp1.Sex = 1またはp1.Sex = 2に置き換えられていることを除いて、ほぼ同じ他のクエリを実行します。これらのクエリは、Firefox ではそれぞれ約 62 ミリ秒で実行され、Java では 52 ミリ秒で実行されます。これは、クエリ プランナーがこの同様のクエリでうまく機能することを意味します。

JDBC では、EXPLAIN QUERY PLAN は次の出力を提供します。

0 0 4 SCAN TABLE TeamEncounters AS te1 USING COVERING INDEX idxTeamEncounters
0 1 5 SEARCH TABLE Encounters AS e1 USING INTEGER PRIMARY KEY (rowid=?)
0 2 2 SEARCH TABLE Schedules AS sch1 USING INDEX sqlite_autoindex_Schedules_1 (TeamEncounterId=?)
0 0 0 EXECUTE LIST SUBQUERY 1
0 3 6 SEARCH TABLE Leagues AS l1 USING INTEGER PRIMARY KEY (rowid=?)
0 4 1 SEARCH TABLE SchedulePlayers AS sp1 USING COVERING INDEX idxSchedulePlayers (ScheduleId=?)
0 5 3 SEARCH TABLE Players AS p1 USING INTEGER PRIMARY KEY (rowid=?)
0 6 0 SEARCH TABLE Scores AS s1 USING INDEX sqlite_autoindex_Scores_1 (SchedulePlayerId=? AND GameNum<?)
0 0 0 USE TEMP B-TREE FOR GROUP BY
0 0 0 USE TEMP B-TREE FOR ORDER BY

これは、元のクエリの計画とは大きく異なります。これは、他の場合のように PRIMARY KEY インデックスのみを使用するよりも意味があると思われるインデックスを使用するためです。

確認したところ、アプリケーションに実行速度の遅い他のクエリがあります。すべての遅いクエリは 'l1.Name LIKE 'Juniors%' を持つもので、それ以外はすべて非常に高速に実行されます。

LIKEを使用するクエリの実行が遅いことを読みました。これにより、フィールド「IsJuniorLeague」を追加して代わりにそれと比較するなど、いくつかのテーブルの設計方法を切り替えることになり、おそらく問題が解決するでしょうが、 Firefoxプラグインのように、これらのクエリを十分に高速にすることが可能です。通常、アプリケーションでクエリを試す前にまずFirefoxでクエリをテストするため、舞台裏で何が起こっているのかを本当に理解したいと思っています。

4

1 に答える 1

3

違いは、SQLite のバージョンが異なることが原因である可能性があります。( で確認してくださいSELECT sqlite_version();。)

オプティマイザーのチェックリストを読んでください。
この特定のクエリでは、次のように記述して、インデックスの使用を強制できます。

... JOIN Leagues l1 INDEXED BY MyThreeColumnIndex ON ...
于 2013-11-09T08:41:50.703 に答える