読者です 読者をやめる 読者になる 読者になる

kurukuru-papaのブログ

主に、ソフトウェア開発に関連したメモを書き溜めたいと思います。

SQLiteのトランザクション管理はパフォーマンスに影響するでしょうか?

Androidアプリ開発において、DBのトランザクション管理が、パフォーマンスの観点で、どの程度影響するのか気になりました。トランザクション管理を適切に行うと、パフォーマンスが向上すると思うのですが、それは使用者が体感できるほどでしょうか?今回動作確認した結果から言えば、体感できない、という結論が出ました。
※今回、トランザクション管理をパフォーマンスという観点から注目しましたが、本来、トランザクション管理は、DBの整合性を維持するために使う機能だと認識しています。

※2012/08/12 re_shikajiroさんのご指摘を受けて計測し直しました。どうもありがとうございます!

測定項目

次の前提に従って簡単なパフォーマンス測定をして見ました。

  1. 対話的に操作を進める簡単なAndroidアプリを想定します。
  2. そのため、ユーザ操作の一回のイベントでアクセスするSQL文は10件としました。

次の項目を変更することによって、パフォーマンスにどの程度差が出るのか測定しました。

  1. トランザクションありとなし。トランザクションありの場合は、SQL文10件の直前で、DB接続とトランザクション開始を行い、SQL文10件実行後に、トランザクション終了とDB切断を行いました。トランザクションなしの場合は、SQL実行ごとにDB接続と切断を行いました。
  2. SelectとUpdateの違い

測定結果

測定してみた結果が下表になります。今回使用した端末は、初代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;
	}

動作環境

  1. Xperia SO-01B
  2. Android 2.3.3