14

FMDB を使用して iPhone に大量のデータを挿入する最良の方法を誰か説明できますか? beginTransaction コマンドを使用しているようです。正直なところ、これまたは setShouldCacheStatements が何をするのかわかりません。これまでに同僚が行ったコードをたどったところ、次のようになりました。

BOOL oldshouldcachestatements = _db.shouldCacheStatements;
[_db setShouldCacheStatements:YES];
[_db beginTransaction];
NSString *insertQuery = [[NSString alloc] initWithFormat:@"INSERT INTO %@ values(null, ?, ?, ?, ?, ?, ?, ?);", tableName];
[tableName release];
BOOL success;

bPtr += 2;
int *iPtr = (int *)bPtr;
int numRecords = *iPtr++;

for (NSInteger record = 0; record < numRecords; record++) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    // Some business logic to read the binary stream  

    NSNumber *freq = aFreq > 0 ? [NSNumber numberWithDouble:100 * aFreq / 32768]: [NSNumber numberWithDouble:-1.0];

    // these fields were calculated in the business logic section
    success = [_db executeUpdate:insertQuery,
               cID,                                                                                                   
               [NSNumber numberWithInt:position],                                                                       
               [NSString stringWithFormat:@"%@%@", [self stringForTypeA:typeA], [self stringForTypeB:typeB]],     // methods are switch statements that look up the decimal number and return a string
               [NSString stringWithFormat:@"r%i", rID],                                                               
               [self stringForOriginal:original],                                                                    
               [self stringForModified:modified],                                                                    
               freq];

    [pool drain];
}
[outerPool drain];

[_db commit];
[_db setShouldCacheStatements:oldshouldcachestatements];

これが最速ですか?書き込みはsqliteの制限ですか?http://www.sqlite.org/faq.html#q19を見ましたが、この実装が fmdb で最適かどうか、または他にできることがあるかどうかわかりませんでした。他の同僚の何人かは、一括挿入とその最適化について言及していましたが、これが私の最初の sqlite 遭遇であるため、それが何を意味するのか正直わかりません。研究に行くことができる考えや方向性はありますか? ありがとう!

4

2 に答える 2

39

まず第一に、完全に間違った使い方をしていなければ、ほとんどの場合、sqlite3 のパフォーマンスについて心配する必要はありません。

INSERT次のことにより、ステートメントのパフォーマンスが向上します。

取引

すでに述べたように、トランザクションは最も重要な機能です。特に大量のクエリがある場合、トランザクションはs を最大 10 倍高速化しますINSERT

プラグマ構成

Sqlite3 は、最悪の場合にデータベースの破損を回避するいくつかのメカニズムを提供します。一部のシナリオでは、これは必要ありません。他の国では、それは絶対に不可欠です。次の sqlite3 コマンドは、INSERTステートメントを高速化する可能性があります。アプリの通常のクラッシュではデータベースが破損しませんが、OS がクラッシュすると破損する可能性があります。

PRAGMA synchronous=OFF -- may cause corruption if the OS fails
PRAGMA journal_mode=MEMORY -- Insert statements will be written to the disk at the end of the transaction
PRAGMA cache_size = 4000 -- If your SELECTs are really big, you may need to increase the cache
PRAGMA temp_store=MEMORY -- Attention: increases RAM use

インデックスを無効にする

SQL インデックスは、INSERTステートメントの速度を低下させます。テーブルにいくつかのインデックスがあるかどうかを確認します。

.indices <table_name>

はいの場合は、トランザクションの後に削除INDEXします。CREATE

ワンセレクト

新しいデータを生成しているため、BULK 挿入を使用する方法がわかりません。INSERTただし、データを収集して 1 つのステートメントのみを実行することもできます。これにより、パフォーマンスが劇的に向上する可能性がありますが、失敗する可能性も高くなります (たとえば、構文)。1 つのハックが別のハックに遭遇します。sqlite3 はこれを直接サポートしていないため、UNION コマンドを使用してすべての挿入ステートメントを適切に収集する必要があります。

INSERT INTO 'tablename'
      SELECT 'data1' AS 'column1', 'data2' AS 'column2'
UNION SELECT 'data3', 'data4'
UNION SELECT 'data5', 'data6'
UNION SELECT 'data7', 'data8'

ステートメントのキャッシング

この機能には未解決の問題があるため、ステートメント キャッシュの使用を避けることをお勧めします (私の知る限り、パフォーマンスに劇的な影響はありません)。

メモリ管理

最後に言及したい点は、ObjC についてです。基本的な操作に比べて、メモリ管理には非常に多くの時間が必要です。たぶん、ループの外でこれらの変数を準備することによって、いくつstringWithFormat:かを避けることができます。numberWithDouble:

概要

全体として、単にtransactionsを使用するだけであれば、sqlite3 の速度に問題はないと思います。

于 2012-08-07T19:28:11.340 に答える
9

多くの行を非常に迅速に挿入する方法に関する具体的なコード例を見つけるのは非常に困難であることがわかりました。FMDBと上記の回答の助けを借りて多くの実験を行った後、私が現在使用しているものは次のとおりです。

[_objectController.dbQueue inDatabase:^(FMDatabase *db)
{
    [db open];
    [db setShouldCacheStatements:YES];

    NSArray *documentsJSONStream = responseObject[@"details"][@"stream"];

    static NSString *insertSQLStatment = @"INSERT INTO documents (`isShared`,`thumbnailURLString`,`userID`,`version`,`timestamp`,`tags`,`title`,`UDID`,`username`,`documentURLString`,`shareURLString`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
    [db beginTransaction];
    for (NSDictionary *dJ in documentsJSONStream)
    {
        [db executeUpdate:insertSQLStatment withArgumentsInArray:@[dJ[@"isShared"],dJ[@"thumbnailURLString"],dJ[@"userID"],dJ[@"version"],dJ[@"timestamp"],dJ[@"tags"],dJ[@"title"],dJ[@"UDID"],dJ[@"username"],dJ[@"documentURLString"],dJ[@"shareURLString"]]];
    }
    [db commit];
    [db close];
 }];

inTransaction を使用すると、奇妙な FMDB is not open エラーが発生しました。これを改善する方法について何か提案があれば、私に知らせてください。

于 2015-02-02T10:16:05.453 に答える