SQLiteは、アプリケーションに埋め込む人気のあるリレーショナルデータベースです。データベース内のデータ量が増えるにつれて、SQLiteパフォーマンスチューニングを適用する必要があります。この記事では、インデックスとその落とし穴、クエリプランナーの使用、ログ先行書き込み(WAL)ジャーナルモード、およびキャッシュサイズの増加について説明します。また、自動テストを使用して、微調整の影響を測定することの重要性についても詳しく説明します。
はじめに
SQLiteは人気のあるリレーショナルデータベース(DB)システムです 。 MySQLなどのより大きなクライアントサーバーベースの兄弟とは異なり、SQLiteはライブラリとしてアプリケーションに埋め込むことができます 。 SQLiteには非常によく似た機能セットがあり、パフォーマンスチューニングに関するいくつかのヒントとコツを知っていれば、何百万もの行を処理することもできます。次のセクションで示すように、インデックスを作成するだけでなく、SQLiteのパフォーマンスチューニングについて知っておくべきことがたくさんあります。
インデックスを作成しますが、注意が必要です
インデックスの基本的な考え方は、読み取りを高速化することです。 特定のデータの 、つまり、SELECT
WHERE
を含むステートメント 句。 インデックスは並べ替えも加速します データ (ORDER BY
)、またはJOIN
テーブルを作成します。残念ながら、インデックスは追加のディスクスペースを消費し、データ操作を遅くするため、両刃の剣です(INSERT
、UPDATE
、DELETE
。
一般的なアドバイスは、できるだけ少ないインデックスを作成することですが、必要なだけ多くのインデックスを作成することです 。また、インデックスは大きいに対してのみ意味があります 数千または数百万行のデータベース。
SQLiteが内部でインデックスを使用する方法は文書化されていますが、理解するのは簡単ではありません。この記事でさらに詳しく説明するように、クエリの前にEXPLAIN QUERY PLAN
を付けてクエリを分析することをお勧めします。 。すべての出力行を見てください。そのうちの3つの基本的なバリエーションがあります。
SEARCH table ...
行は良い兆候です– SQLiteはあなたのインデックスの1つを使用します!SCAN table ... USING INDEX
悪い兆候ですSCAN table ...
さらに悪いです!
SCAN table [using index]
は避けてください EXPLAIN QUERY PLAN
の出力のエントリ 大規模なデータベースではパフォーマンスの問題が発生するため、可能な限り。 EXPLAIN QUERY PLAN
を使用します SCAN table
がなくなるまで、インデックスを繰り返し追加または変更します エントリが表示されます。
IS NOT
を含むクエリを最適化する
IS NOT ...
を確認しています 高い SQLiteはスキャンする必要があるためです テーブルのすべての行、影響を受ける列にインデックスがある場合でも 。インデックスは、特定の値、つまり<を含む比較を探す場合にのみ役立ちます。 (小さい)、> (より大きい)、または = (等しい)が、!=(等しくない)には適用されません。
ちょっとしたコツは、WHERE column != value
を置き換えることができるということです。 WHERE column > value OR column < value
。これにより、列のインデックスが使用され、値がvalue
と等しくないすべての行に効果的に影響します。 。同様に、WHERE stringColumn != ''
WHERE stringColumn > ''
に置き換えることができます 、文字列は並べ替え可能であるため。ただし、このトリックを適用するときは、SQLiteがNULL
をどのように処理するかを知っていることを確認してください 比較。たとえば、SQLiteはNULL > ''
を評価します FALSE
として 。
このような比較トリックを使用する場合、クエリにWHERE
が含まれている場合の別の注意点があります。 およびORDER BY
、それぞれに異なる列があります。これにより、クエリが再び非効率になります。可能であれば、同じを使用してください WHERE
の列 およびORDER BY
、またはカバーインデックスを作成します これには、WHERE
の両方が含まれます およびORDER BY
列。
先行書き込みログを使用して、書き込み速度を向上させます
先行書き込み(WAL) ジャーナルモードは、書き込み/更新のパフォーマンスを大幅に向上させます 、デフォルトのロールバックと比較 ジャーナルモード。ただし、ここに記載されているように、いくつかの注意事項があります 。たとえば、WALモードは特定のオペレーティングシステムでは使用できません。また、ハードウェア障害が発生した場合のデータ整合性の保証が低下します 。最初の数ページを読んで、何をしているかを理解してください。
コマンドPRAGMA synchronous = NORMAL
が見つかりました 3〜4倍のスピードアップを提供します。 journal_mode
の設定 WAL
へ その後、パフォーマンスが再び大幅に向上します(オペレーティングシステムによって異なりますが、約10倍以上)。
すでに述べた警告とは別に、次の点にも注意する必要があります。
- WALジャーナルモードを使用すると、ファイルシステムのデータベースファイルの横に2つの追加ファイルがあります。これらのファイルは、データベースと同じ名前ですが、接尾辞が「-shm」と「-wal」です。通常は気にする必要はありませんが、アプリケーションの実行中にデータベースを別のマシンに送信する場合は、これら2つのファイルを含めることを忘れないでください。 SQLiteは、開いているすべてのデータベース接続を通常閉じるたびに、これら2つのファイルをメインファイルに圧縮します。
- クエリによってWALログファイルの内容がメインデータベースファイルにマージされるたびに、挿入または更新のパフォーマンスが低下することがあります。これはチェックポイントと呼ばれます 、こちらをご覧ください。
-
PRAGMA
が見つかりました sjournal_mode
を変更する およびsynchronous
データベースに永続的に保存されていないようです。したがって、私は常に 初めてテーブルを作成するときに実行するのではなく、新しいデータベース接続を開くたびに再実行します。
パフォーマンスの微調整を追加するときは、必ず影響を測定してください。 自動(単体)テストは、このための優れたアプローチです。 ドキュメント化に役立ちます あなたのチームのためのあなたの発見、そして彼らは時間の経過とともに逸脱した行動を明らかにするでしょう 、例:新しいSQLiteバージョンに更新するとき。測定できるものの例:
- WALを使用するとどのような効果がありますか ロールバックでのジャーナルモード モード?他の(おそらく)パフォーマンスを向上させる
PRAGMA
の効果は何ですか s? - インデックスを追加/変更/削除したら、
SELECT
をどれだけ速く実行しますか ステートメントはなりますか?INSERT/DELETE/UPDATE
の処理速度はどれくらい遅くなりますか ステートメントはなりますか? - インデックスはどのくらいの追加ディスクスペースを消費しますか?
これらのテストのいずれについても、さまざまなデータベースサイズで繰り返すことを検討してください。例えば。空のデータベースで実行し、すでに数千(または数百万)のエントリが含まれているデータベースでも実行します。また、特に開発環境と本番環境が大幅に異なる場合は、さまざまなデバイスとオペレーティングシステムでテストを実行する必要があります。
SQLiteは一時的な情報をキャッシュに保存します (RAM内)、例: SELECT
の結果を作成している間 クエリ、またはまだコミットされていないデータを操作する場合。 デフォルトでは、このサイズはわずか2MBです 。最新のデスクトップマシンは、はるかに多くのことを惜しまない可能性があります。 PRAGMA cache_size = -kibibytes
を実行します この値を増やすには (マイナスに注意してください 値の前にサインインしてください!)。詳細については、こちらをご覧ください。繰り返しますが、測定 この設定がパフォーマンスに与える影響!
REPLACEINTOを使用して行を作成または更新します
これは、ちょっとしたトリックなので、パフォーマンスの微調整ではないかもしれません。 更新する必要があるとします テーブルt
の行 、または作成 まだ存在しない場合は行。 2つのクエリを使用するのではなく(SELECT
続いてINSERT
またはUPDATE
)、REPLACE INTO
を使用します (公式ドキュメント)。
これを機能させるには、ダミーの列を追加します(例:replacer
)テーブルt
、UNIQUE
があります 制約します。列の宣言は、たとえば... replacer INTEGER UNIQUE ...
これはCREATE TABLE
の一部です 声明。次に、
REPLACE INTO t (col1, col2, ..., replacer) VALUES (?,?,...,1)
Code language: SQL (Structured Query Language) (sql)
このクエリを初めて実行すると、INSERT
が実行されます。 。 2回目に実行すると、UNIQUE
replacer
の制約 列がトリガーされ、競合解決の動作により古い行が削除され、新しい行が自動的に作成されます。関連するUPSERTコマンドが役立つ場合もあります。
結論
データベースの行数が増えると、パフォーマンスの微調整が必要になります。インデックスは最も一般的な解決策です。これらは、時間の複雑さの改善とスペースの複雑さの減少を交換し、読み取り速度を改善する一方で、データ変更のパフォーマンスに悪影響を及ぼします。 A私が示したように、不平等を比較するときは、特に注意する必要があります。 SELECT
で SQLiteはそのような種類の比較にインデックスを使用できないためです。通常、クエリプランナーを使用することをお勧めします これは、各SQLクエリの内部で何が起こるかを説明しています。何かを微調整するときはいつでも、影響を測定してください!