3

100 レコードのバッチを挿入しています。それぞれのレコードには、任意の長さの HTML 文字列を含む辞書が含まれています。iPhone では、このトランザクション中に runloop が数秒間ブロックされます。別のスレッドを使用する唯一の手段ですか? 私はすでにHTTPサーバーからデータを取得するためにいくつかを使用しています.sqliteのドキュメントでは、スレッドセーフであると想定されているにもかかわらず、データベースとのスレッド化を明示的に思いとどまらせています...私が非常に間違っていることはありますか?操作全体を完了するのにかかる時間を大幅に短縮しますか?

    NSString* statement;
    statement = @"BEGIN EXCLUSIVE TRANSACTION";
    sqlite3_stmt *beginStatement;
    if (sqlite3_prepare_v2(database, [statement UTF8String], -1, &beginStatement, NULL) != SQLITE_OK) {
        printf("db error: %s\n", sqlite3_errmsg(database)); 
        return;
    }
    if (sqlite3_step(beginStatement) != SQLITE_DONE) {
        sqlite3_finalize(beginStatement);
        printf("db error: %s\n", sqlite3_errmsg(database)); 
        return;
    }

    NSTimeInterval timestampB = [[NSDate date] timeIntervalSince1970];
    statement = @"INSERT OR REPLACE INTO item (hash, tag, owner, timestamp, dictionary) VALUES (?, ?, ?, ?, ?)";
    sqlite3_stmt *compiledStatement;
    if(sqlite3_prepare_v2(database, [statement UTF8String], -1, &compiledStatement, NULL) == SQLITE_OK)
    {
        for(int i = 0; i < [items count]; i++){
            NSMutableDictionary* item = [items objectAtIndex:i];
            NSString* tag       = [item objectForKey:@"id"];
            NSInteger hash      = [[NSString stringWithFormat:@"%@%@", tag, ownerID] hash];
            NSInteger timestamp = [[item objectForKey:@"updated"] intValue];
            NSData *dictionary  = [NSKeyedArchiver archivedDataWithRootObject:item];

            sqlite3_bind_int(   compiledStatement, 1, hash);
            sqlite3_bind_text(  compiledStatement, 2, [tag UTF8String], -1, SQLITE_TRANSIENT);
            sqlite3_bind_text(  compiledStatement, 3, [ownerID UTF8String], -1, SQLITE_TRANSIENT);
            sqlite3_bind_int(   compiledStatement, 4, timestamp);
            sqlite3_bind_blob(  compiledStatement, 5, [dictionary bytes], [dictionary length], SQLITE_TRANSIENT);

            while(YES){
                NSInteger result = sqlite3_step(compiledStatement);
                if(result == SQLITE_DONE){
                    break;
                }
                else if(result != SQLITE_BUSY){
                    printf("db error: %s\n", sqlite3_errmsg(database)); 
                    break;
                }
            }
            sqlite3_reset(compiledStatement);
        }
        timestampB = [[NSDate date] timeIntervalSince1970] - timestampB;
        NSLog(@"Insert Time Taken: %f",timestampB);

        // COMMIT
        statement = @"COMMIT TRANSACTION";
        sqlite3_stmt *commitStatement;
        if (sqlite3_prepare_v2(database, [statement UTF8String], -1, &commitStatement, NULL) != SQLITE_OK) {
            printf("db error: %s\n", sqlite3_errmsg(database)); 
        }
        if (sqlite3_step(commitStatement) != SQLITE_DONE) {
            printf("db error: %s\n", sqlite3_errmsg(database)); 
        }

        sqlite3_finalize(beginStatement);
        sqlite3_finalize(compiledStatement);
        sqlite3_finalize(commitStatement);
4

4 に答える 4

4

知っておく必要があるのは、SQLite のドキュメントでは、複数のスレッドからデータベースにアクセスしたり、データベースに書き込んだりしないように警告していることです。単一のスレッドからデータベースにアクセスする限り、問題ありません。そのスレッドがプログラムのメイン スレッドであるか、他のスレッドであるかは関係ありません。

iPhone 上の SQLite のコンパイル済みバージョンでは、スレッド モードが「マルチスレッド」に設定されていることに注意してください。ドキュメントによると、「データベース接続と準備済みステートメント オブジェクトのミューテックスを無効にします。アプリケーションは、データベース接続へのアクセスをシリアル化する責任があります。 2 つのスレッドが同時に同じデータベース接続を使用しようとしない限り、マルチスレッド環境で SQLite を安全に使用できるように、プリペアド ステートメントと他のミューテックスが有効になっています。」そのため、このトランザクションを別のスレッドに置くことにした場合は、データベースで他に何をしようとしているのかに注意してください。

そうは言っても、まず Yonel のアドバイスに従い、「BEGIN」と「COMMIT」に切り替えます。それでも問題が解決しない場合は、トランザクションを別のスレッドに移動します。私が聞いたところによると、「ブロブ」の操作はかなり遅くなる可能性があります。

于 2010-03-15T11:21:01.523 に答える
1

コードと同じように試しましたが、"BEGIN"and"COMMIT"の代わりに"BEGIN EXCLUSIVE TRANSACTION"andを使用しました"COMMIT TRANSACTION"か?

私は単にBEGINとCOMMITを使用しており、トランザクションごとにコミットするよりもはるかに高速なので、これらのキーワードで機能していると思います。

http://www.sqlite.org/lang_transaction.html

于 2010-03-15T10:15:53.943 に答える
0

iPhoneを初めて使用する開発者が、軽量のハードウェアに多くの処理を要求するだけの場合、コードが遅いと信じているケースがたくさんあります。数百(数千?)の「任意に長いHTML文字列」を処理することは、iPhoneがタイムリーに実行するためのタスクを重くする可能性があります。

iPhoneはそれほど強力なハードウェアではないことを忘れないでください。それはあなたが他のタスクのためにアクセスすることができない計算能力を持つ専用のハードウェアですべての気の利いたグラフィックスを引き出します。コードを最適化したとしても、本格的なラップトップやデスクトップでの経験に基づいて直感的に予想するよりもはるかに遅くなる可能性があります。

ボトルネックがどこにあるかを推測する代わりに、Instrumentsでコードをプロファイリングして(またはタイムスタンプ付きのNSLogを使用して)、コードがほとんどの時間を費やしている場所を正確に確認することをお勧めします。

于 2010-03-15T12:32:53.287 に答える
0

ブロッキングの問題を回避するためのより良い方法は、非同期コールバックを使用することです。Enorm EGO sqlite ラッパーを使用してみてください https://github.com/jdp-global/egodatabase

EGODatabaseRequest - db への非同期要求 /inserts の私の Readme セクションを見てください。

2) requestDidSucceed /requestDidFail コールバック メソッドを追加します。

 -(void)requestDidSucceed:(EGODatabaseRequest*)request withResult:(EGODatabaseResult*)result
    idx++
    if ([items count]<idx) [self insertRow];

}

-(void)requestDidFail:(EGODatabaseRequest*)request withError:(NSError*)error{

    NSLog(@"WARNING requestDidFail");
}




-(void)insertRow{
    NSMutableDictionary* item = [items objectAtIndex:idx];
    NSInteger hash      = [[NSString stringWithFormat:@"%@%@", tag, ownerID] hash];
    NSInteger timestamp = [[item objectForKey:@"updated"] intValue];
    NSData *dictionary  = [NSKeyedArchiver archivedDataWithRootObject:item];
    NSString *qry = [NSString stringWithFormat:@"INSERT OR REPLACE INTO item (hash, tag, owner, timestamp, dictionary) VALUES (%@, %@, %@, %@, %@);",NUMBER(hash),[tag UTF8String],[ownerID UTF8String],NUMBER(timestamp),dictionary];

    // be sure to use NSNumbers not NSIntegers
    EGODatabaseRequest* request = [[EGODatabaseRequest alloc] initWithQuery:qry parameters:nil];
    request.delegate = self;
    request.database = appDelegate.database;
    request.requestKind = EGODatabaseUpdateRequest; // use update not select
    [request fire];
    [request release];
}
于 2011-10-10T23:24:05.667 に答える