SQLiteのトランザクション管理はパフォーマンスに影響するでしょうか?
Androidアプリ開発において、DBのトランザクション管理が、パフォーマンスの観点で、どの程度影響するのか気になりました。トランザクション管理を適切に行うと、パフォーマンスが向上すると思うのですが、それは使用者が体感できるほどでしょうか?今回動作確認した結果から言えば、体感できない、という結論が出ました。
※今回、トランザクション管理をパフォーマンスという観点から注目しましたが、本来、トランザクション管理は、DBの整合性を維持するために使う機能だと認識しています。
※2012/08/12 re_shikajiroさんのご指摘を受けて計測し直しました。どうもありがとうございます!
測定項目
次の前提に従って簡単なパフォーマンス測定をして見ました。
次の項目を変更することによって、パフォーマンスにどの程度差が出るのか測定しました。
測定結果
測定してみた結果が下表になります。今回使用した端末は、初代Xperiaです。今では見劣りするスペックですが、それでも、すべてのパターンで200ミリ秒以下の処理結果です。各パターンの違いは、私には体感できるほどではありません。
SQL種類 | トランザクション | 時間(ミリ秒) |
---|---|---|
Insert 10回 | あり | 23ミリ秒 |
Insert 10回 | なし | 197ミリ秒 |
Select 10回 | あり | 20ミリ秒 |
Select 10回 | なし | 70ミリ秒 |
トランザクションのありとなしでは、処理時間の違いは体感できませんでしたが、Insertで約8.6倍、Selectで約3.5倍処理時間が違っています。この処理を何度も繰り返し行うようなアプリでは、バッテリーの消耗に影響がありそうですね。
ちなみに、SQL文100回のときは、下表のようになります。この場合は、トランザクションありとなしで、Insert時に1.5秒、Select時に0.5秒程度違いがありました。これなら体感できそうです。
SQL種類 | トランザクション | 時間(ミリ秒) |
---|---|---|
Insert 100回 | あり | 52ミリ秒 |
Insert 100回 | なし | 1,690ミリ秒 |
Select 100回 | あり | 333ミリ秒 |
Select 100回 | なし | 848ミリ秒 |
測定用処理
測定は、別スレッドで次のように実装しました。抜粋します。
@Override protected Void doInBackground(Void... arg0) { LogUtil.called(); insertOnlyMsec = 0; insertAndConnectionMsec = 0; queryOnlyMsec = 0; queryAndConnectionMsec = 0; dbHelper.clear(); for (int i = 0; i < numLoop; i++) { insertOnlyLoop(); insertAndConnectionLoop(); queryOnlyLoop(); queryAndConnectionLoop(); publishProgress(i); } dbHelper.clear(); return null; } private void insertOnlyLoop() { long startTimestamp = System.currentTimeMillis(); SQLiteDatabase db = dbHelper.getWritableDatabase(); try { db.beginTransaction(); for (int i = 0; i < numSql; i++) { insert(db, i); } db.setTransactionSuccessful(); } finally { db.endTransaction(); db.close(); } long endTimestamp = System.currentTimeMillis(); insertOnlyMsec += endTimestamp - startTimestamp; } private void insertAndConnectionLoop() { long startTimestamp = System.currentTimeMillis(); for (int i = 0; i < numSql; i++) { SQLiteDatabase db = dbHelper.getWritableDatabase(); try { insert(db, i); } finally { db.close(); } } long endTimestamp = System.currentTimeMillis(); insertAndConnectionMsec += endTimestamp - startTimestamp; } private void insert(SQLiteDatabase db, int i) { ContentValues values = new ContentValues(); values.put("text", "text" + i); values.put("timestamp", System.currentTimeMillis()); db.insertOrThrow("performance_entity", null, values); } private void queryOnlyLoop() { long startTimestamp = System.currentTimeMillis(); SQLiteDatabase db = dbHelper.getReadableDatabase(); try { db.beginTransaction(); for (int i = 0; i < numSql; i++) { query(db, i); } db.setTransactionSuccessful(); } finally { db.endTransaction(); db.close(); } long endTimestamp = System.currentTimeMillis(); queryOnlyMsec += endTimestamp - startTimestamp; } private void queryAndConnectionLoop() { long startTimestamp = System.currentTimeMillis(); for (int i = 0; i < numSql; i++) { SQLiteDatabase db = dbHelper.getReadableDatabase(); try { query(db, i); } finally { db.close(); } } long endTimestamp = System.currentTimeMillis(); queryAndConnectionMsec += endTimestamp - startTimestamp; } private PerformanceEntity query(SQLiteDatabase db, int i) { PerformanceEntity entity = null; Cursor cursor = null; try { cursor = db.query("performance_entity", new String[] { "id", "text", "timestamp" }, "text=?", new String[] { "text" + i }, null, null, "id", String.valueOf(numSql)); cursor.moveToNext(); entity = new PerformanceEntity(); entity.id = cursor.getInt(0); entity.text = cursor.getString(1); entity.timestamp = cursor.getLong(2); } finally { if (cursor != null) { cursor.close(); } } return entity; }