SQLiteのマルチスレッド同時アクセスの応用

5943 ワード

SQLiteのマルチスレッド同時アクセスの応用
最近SQLiteの問題に遭遇して長い間私を引っ張って、最後に一言をまとめました:SQLiteはマルチスレッドをサポートしていません
検討してみると、以下の2つの案が実行可能であることが分かった.
1.まず、複数のスレッドが同じデータベースを同時に操作し、insert、delete、select操作が同時に存在する場合、データは安全ではなく、Androidに内蔵されているSQLiteでも許可されていないため、衝突異常を引き起こす.多くのスレッドが許可されていない場合は、マルチスレッド同期を実現する必要があります.
マルチスレッド同期ロックのアクセスSQLite使用:常にアプリケーション全体にdatabase接続を維持します.これにより、マルチスレッドがsqliteに同時にアクセスしても、databaseオブジェクトがjavaロックを使用するとアクセスのシーケンス化が維持されます.SQLhelperを使用してデータベースを管理するのが一般的ですが、1つのHelperインスタンスでdatabase接続が生成されるので、アプリケーション全体でSQLhelperインスタンスを1つ生成するだけでいいです.
public class SQLHelper extends SQLiteOpenHelper {

	private static final int VERSION_CODE = 1;
	private static SQLHelper helper;

	/**
	 *     +                
	 * 
	 * @param context
	 * @return
	 */
	public synchronized static SQLHelper getInstance(Context context) {
		if (helper == null) {
			helper = new SQLHelper(context);
		}
		return helper;
	}

	private SQLHelper(Context context) {
		super(context, "person.db", null, VERSION_CODE);
	}

	private SQLHelper(Context context, String name, CursorFactory factory,
			int version) {
		super(context, name, factory, VERSION_CODE);
	}

	@Override
	public void onCreate(SQLiteDatabase db) {
		Log.i("sqlite", "      ");
		//       
		String sql = "create table t_student(_id integer primary key autoincrement,id integer,name varchr(20),age int,address varchr(40))";
		db.execSQL(sql);
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		String sql = "drop table t_student";
		db.execSQL(sql);
	}

	@Override
	public synchronized void close() {
		super.close();
	}
}

プログラムの入口では、アプリケーションにSQLhelperのインスタンスが生成するが、ここでのonTerminate()は必ずしも終了するたびに実行できるとは限らないので、トップページ(メインactivity)のonDestory()メソッドでdbHelperを呼び出すことを推奨する.close();データベース接続を閉じる
public class MyApplication extends Application {
    private Context context;
    private DBHelper dbHelper;

    public void onCreate() {
        super.onCreate();
        context = getApplicationContext();
        dbHelper = DBHelper.getInstance(context);
    }

    public void onTerminate() {
        super.onTerminate();
        dbHelper.close();
    }
}

まとめ:マルチスレッド同期ロック方式でSQLiteにアクセスし、同じ時刻にSQLiteが1つの接続しかないことを保証し、複数のスレッドが並んで順番にデータベースにアクセスし、衝突問題を完璧に解決した.
この问题は确かに私を困らせて半日、自分で模索するのは确かに比较的に苦痛で、最后に国外の1枚の招待状の中でこのような1段の描写を见て、1种のおおらかな感じがします.
Inserts, updates, deletes and reads are generally OK from multiple threads, but Brad'sanswer is not correct.  You have to be careful with how you create your connections and use them.  There are situations where your update calls will fail, even if your database doesn't get corrupted. The basic answer. The SqliteOpenHelper object holds on to one database connection.  It appears to offer you a read and write connection, but it really doesn't.  Call the read-only, and you'll get the write database connection regardless. So, one helper instance, one db connection.  Even if you use it from multiple threads, one connection at a time.  The SqliteDatabase object uses java locks to keep access serialized.  So, if 100 threads have one db instance, calls to the actual on-disk database are serialized. So, one helper, one db connection, which is serialized in java code.  One thread, 1000 threads, if you use one helper instance shared between them, all of your db access code is serial.  And life is good (ish). If you try to write to the database from actual distinct connections at the same time, one will fail.  It will not wait till the first is done and then write.  It will simply not write your change.  Worse, if you don’t call the right version of insert/update on the SQLiteDatabase, you won’t get an exception.  You’ll just get a message in your LogCat, and that will be it. So, multiple threads?  Use one helper.  Period.  If you KNOW only one thread will be writing, you MAY be able to use multiple connections, and your reads will be faster, but buyer beware.  I haven't tested that much. Here's a blog post with far more detail and an example app.
  • Android Sqlite Locking (Updated link 6/18/2012)
  • Android-Database-Locking-Collisions-Example by touchlab on GitHub

  • Gray and I are actually wrapping up an ORM tool, based off of his Ormlite, that works natively with Android database implementations, and follows the safe creation/calling structure I describe in the blog post.  That should be out very soon.  Take a look.
    In the meantime, there is a follow up blog post:
  • Single SQLite connection

  • Also checkout the fork by2point0 of the previously mentioned locking example:
  • Android-Database-Locking-Collisions-Example by 2point0 onGitHub

  •  
    第2の方式についても,同時動作SQLiteを同様に解決できる.
    2.「1つのdatabase connectは、クエリーと更新(順序にかかわらず異なるstatement)があり、実行が完了した後、閉じないと、s 3 db-journalという拡張子の(一時的な)ファイルが生成され、再びこのconnectを使用して更新を実行すると、「unable to open database file」という異常が発生します.したがって、connectが実行されるとcloseになるか、一時ファイルを自分で処理します.」結局、異なるデバイスのデータベースファイルの格納パスに違いがあるので、削除はファイルパスエラー、ファイルに問題がないなどの問題が発生する可能性があるので、前者を使用して、実行が完了したら、すぐにclose()を閉じることをお勧めします.
    @Override
    	public synchronized void close() {
    		super.close();
    	}

    注意:ここでは、上記の例とは正反対に、データを使用するたびに構造関数を使用してSQLhelperインスタンスを構築します.同じSQLhelperを使用するたびに、閉じるとデータベースを開くことはできません.
    Cursor cursor = null;
    try {
    	// helper = SQLHelper.getInstance(context);
    	//  :           ,                  sqlhelper  
    	helper = new SQLHelper(context);
    	SQLiteDatabase db = helper.getReadableDatabase();
    	cursor = db.query("t_student", null, null, null, null, null, null);
    } catch (Exception e) {
    	// TODO: handle exception
    } finally {
    	helper.close(); //       
    }

     
    まとめ:この2つの方法を比較し、テスト後、第1の方法を強く推奨し、性能の観点から、第1の方法は単一の例と同期ロックのモードを使用し、グローバルにはSQLhelperオブジェクトが1つしかないが、第2の方法はSQLhelperオブジェクトを複数回作成し、閉じる必要があり、性能は単一例よりはるかに低い方法である.