fmdb sqlite插入數據慢
之前使用FMDB加載一個全國的行政區(qū)劃的數據庫表到客戶端闯狱,好讓用戶在選擇收獲地址時在本地加載煞赢,提高用戶的使用體驗,在模擬器上跑時非常方便哄孤,但在測試時非常慢照筑,真機加載時格外慢,加載8萬多條的數據差不多要半個小時,這簡直不能忍受凝危,后來查找了很多資料波俄,發(fā)現只要添加了事務就好了, 一行代碼提升了好幾十倍蛾默,原本半小時才可以的現在一分鐘內就好了懦铺,10萬條數據測試結果如下(結論,開啟事務普通出入數據最快):
模擬器上的結果:(時間差別不大)
2017-07-25 16:02:06.233 TestFMDB[66270:4375010] *** 創(chuàng)建數據花費時間 0.409968 s
2017-07-25 16:02:59.004 TestFMDB[66270:4375010] *** 普通方式插入數據花費時間 52.770025 s
2017-07-25 16:02:59.004 TestFMDB[66270:4375010] *** address fmdb 加載數據庫結束
2017-07-25 16:02:06.233 TestFMDB[66270:4375010] *** 創(chuàng)建數據花費時間 0.409968 s
2017-07-25 16:02:59.004 TestFMDB[66270:4375010] *** 普通方式插入數據花費時間 52.770025 s
2017-07-25 16:02:59.004 TestFMDB[66270:4375010] *** address fmdb 加載數據庫結束
2017-07-25 16:08:38.285 TestFMDB[66420:4379160] *** 創(chuàng)建數據花費時間 0.433273 s
2017-07-25 16:08:51.874 TestFMDB[66420:4379157] *** 多線程開啟事務方式插入數據花費時間 13.587659 s
2017-07-25 16:09:03.745 TestFMDB[66420:4379158] *** 多線程開啟事務方式插入數據花費時間 25.459356 s
2017-07-25 16:09:23.915 TestFMDB[66420:4379222] *** 多線程開啟事務方式插入數據花費時間 45.629394 s
2017-07-25 16:09:40.919 TestFMDB[66420:4379211] *** 多線程開啟事務方式插入數據花費時間 62.632683 s
真機測試結果(沒有重現我項目中不開啟事務特別慢的情況)
// 普通插入數據
2017-07-25 16:23:21.120660+0800 TestFMDB[2152:677046] *** 創(chuàng)建數據花費時間 1.374888 s
2017-07-25 16:24:14.512719+0800 TestFMDB[2152:677046] *** 普通方式插入數據花費時間 53.391885 s
2017-07-25 16:24:14.512885+0800 TestFMDB[2152:677046] *** address fmdb 加載數據庫結束
// 普通開啟事務的結果
2017-07-25 16:21:22.480394+0800 TestFMDB[2147:676024] *** 創(chuàng)建數據花費時間 1.359878 s
2017-07-25 16:22:00.316775+0800 TestFMDB[2147:676024] *** 開啟事務方式插入數據花費時間 37.836121 s
2017-07-25 16:22:00.316933+0800 TestFMDB[2147:676024] *** address fmdb 加載數據庫結束
// 開啟4個線程的結果
2017-07-25 16:14:07.000092+0800 TestFMDB[2135:671904] *** 創(chuàng)建數據花費時間 1.484458 s
2017-07-25 16:14:16.818597+0800 TestFMDB[2135:671902] *** 線程1開啟事務方式插入數據花費時間 9.818329 s
2017-07-25 16:14:26.386697+0800 TestFMDB[2135:671903] *** 線程2開啟事務方式插入數據花費時間 19.386441 s
2017-07-25 16:14:36.702309+0800 TestFMDB[2135:671900] *** 線程3開啟事務方式插入數據花費時間 29.702041 s
2017-07-25 16:14:47.890248+0800 TestFMDB[2135:671901] *** 線程4開啟事務方式插入數據花費時間 40.889873 s
開啟2個線程的結果
2017-07-25 16:15:52.293433+0800 TestFMDB[2137:672875] *** 創(chuàng)建數據花費時間 2.423719 s
2017-07-25 16:16:13.158949+0800 TestFMDB[2137:672877] *** 線程1開啟事務方式插入數據花費時間 20.865313 s
2017-07-25 16:16:32.692071+0800 TestFMDB[2137:672876] *** 線程2開啟事務方式插入數據花費時間 40.398287 s
開啟2個線程不開啟事務的結果
2017-07-25 16:17:20.354732+0800 TestFMDB[2140:673793] *** 創(chuàng)建數據花費時間 1.563348 s
2017-07-25 16:17:47.615129+0800 TestFMDB[2140:673794] *** 線程1開啟事務方式插入數據花費時間 27.260129 s
2017-07-25 16:18:16.670929+0800 TestFMDB[2140:673796] *** 線程2開啟事務方式插入數據花費時間 56.316027 s
具體代碼如下:
#pragma mark - ###### 將數據存入數控庫
- (void)testInsertDB
{
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 創(chuàng)建測試數據
NSTimeInterval createDataBegin = [[NSDate date] timeIntervalSince1970];
NSMutableArray *arrayM = [NSMutableArray array];
for (int i=0; i<10*10000; i++) {
NSMutableDictionary *item = [NSMutableDictionary dictionary];
item[@"addr_id"] = [NSString stringWithFormat:@"addr_id_%d",i];
item[@"parent_addr_id"] = [NSString stringWithFormat:@"parent_addr_id_%d",i];
item[@"addr_value"] = [NSString stringWithFormat:@"addr_value_%d",i];
[arrayM addObject:item];
}
NSTimeInterval createDataEnd = [[NSDate date] timeIntervalSince1970];
CGFloat createDataCost = (createDataEnd - createDataBegin);
NSLog(@"*** 創(chuàng)建數據花費時間 %f s",createDataCost);
// 調用
// 普通插入
[weakSelf normalSaveData2LocalDataBaseWithNSArray:arrayM];
// 開啟事務插入
//[weakSelf transationSaveData2LocalDataBaseWithNSArray:arrayM];
// 開啟多個Queue和事務插入
//[weakSelf mutalQueueSaveData2LocalDataBaseWithNSArray:arrayM];
});
}
#pragma mark - ******** 普通一條一條插入
- (void)normalSaveData2LocalDataBaseWithNSArray:(NSArray *)addressList
{
NSTimeInterval insertDataBegin = [[NSDate date] timeIntervalSince1970];
// 獲得Documents目錄路徑
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
// 文件路徑
NSString *filePath = [documentsPath stringByAppendingPathComponent:@"testAddress.sqlite"];
// 實例化FMDataBase對象
FMDatabase *db = [FMDatabase databaseWithPath:filePath];
[db open];
NSString *dropSql = @"DROP TABLE t_test_address;";
[db executeUpdate:dropSql];
// 初始化數據表
NSString *createSql = @"CREATE TABLE IF NOT EXISTS 't_test_address' ('id' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL ,'addr_id' VARCHAR(255),'parent_addr_id' VARCHAR(255),'addr_value' VARCHAR(255)) ";
[db executeUpdate:createSql];
for (NSDictionary *item in addressList) {
// 初始化數據表
NSString *insertSql = @"INSERT INTO t_address (addr_id, parent_addr_id,addr_value) VALUES (?,?,?)";
BOOL insertRes = [db executeUpdate:insertSql withArgumentsInArray:@[item[@"addr_id"],item[@"parent_addr_id"],item[@"addr_value"]]];
}
NSTimeInterval insertDataEnd = [[NSDate date] timeIntervalSince1970];
CGFloat insertDataCost = (insertDataEnd - insertDataBegin);
NSLog(@"*** 普通方式插入數據花費時間 %f s",insertDataCost);
NSLog(@"*** address fmdb 加載數據庫結束");
}
#pragma mark - ******** 開啟事務插入
- (void)transationSaveData2LocalDataBaseWithNSArray:(NSArray *)addressList
{
NSTimeInterval insertDataBegin = [[NSDate date] timeIntervalSince1970];
// 獲得Documents目錄路徑
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
// 文件路徑
NSString *filePath = [documentsPath stringByAppendingPathComponent:@"testAddress.sqlite"];
// 實例化FMDataBase對象
FMDatabase *db = [FMDatabase databaseWithPath:filePath];
[db open];
NSString *dropSql = @"DROP TABLE t_test_address;";
[db executeUpdate:dropSql];
// 初始化數據表
NSString *createSql = @"CREATE TABLE IF NOT EXISTS 't_test_address' ('id' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL ,'addr_id' VARCHAR(255),'parent_addr_id' VARCHAR(255),'addr_value' VARCHAR(255)) ";
[db executeUpdate:createSql];
[db beginTransaction];
for (NSDictionary *item in addressList) {
// 初始化數據表
NSString *insertSql = @"INSERT INTO t_address (addr_id, parent_addr_id,addr_value) VALUES (?,?,?)";
BOOL insertRes = [db executeUpdate:insertSql withArgumentsInArray:@[item[@"addr_id"],item[@"parent_addr_id"],item[@"addr_value"]]];
}
[db commit];
NSTimeInterval insertDataEnd = [[NSDate date] timeIntervalSince1970];
CGFloat insertDataCost = (insertDataEnd - insertDataBegin);
NSLog(@"*** 開啟事務方式插入數據花費時間 %f s",insertDataCost);
NSLog(@"*** address fmdb 加載數據庫結束");
}
#pragma mark - ******** 多線程務事務插入
- (void)mutalQueueSaveData2LocalDataBaseWithNSArray:(NSArray *)addressList
{
NSTimeInterval insertDataBegin = [[NSDate date] timeIntervalSince1970];
// 獲得Documents目錄路徑
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
// 文件路徑
NSString *filePath = [documentsPath stringByAppendingPathComponent:@"testAddress.sqlite"];
// 實例化FMDataBase對象
FMDatabase *db = [FMDatabase databaseWithPath:filePath];
[db open];
NSString *dropSql = @"DROP TABLE t_test_address;";
[db executeUpdate:dropSql];
// 初始化數據表
NSString *createSql = @"CREATE TABLE IF NOT EXISTS 't_test_address' ('id' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL ,'addr_id' VARCHAR(255),'parent_addr_id' VARCHAR(255),'addr_value' VARCHAR(255)) ";
__block NSTimeInterval insertDataEnd = [[NSDate date] timeIntervalSince1970];
//創(chuàng)建連個線程(模擬多線程操作)
dispatch_queue_t q1 = dispatch_queue_create("queue1", NULL);
dispatch_queue_t q2 = dispatch_queue_create("queue2", NULL);
dispatch_queue_t q3 = dispatch_queue_create("queue3", NULL);
dispatch_queue_t q4 = dispatch_queue_create("queue4", NULL);
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:filePath];
NSInteger range = addressList.count / 2;
//線程一
dispatch_async(q1, ^{
[queue inDatabase:^(FMDatabase *db2) {
[db2 beginTransaction];
for (NSInteger i = range*0; i < range*1;i++) {
NSDictionary *item = addressList[i];
// 初始化數據表
NSString *insertSql = @"INSERT INTO t_address (addr_id, parent_addr_id,addr_value) VALUES (?,?,?)";
BOOL insert = [db2 executeUpdate:insertSql withArgumentsInArray:@[item[@"addr_id"],item[@"parent_addr_id"],item[@"addr_value"]]];
}
[db2 commit];
NSTimeInterval insertDataEnd1 = [[NSDate date] timeIntervalSince1970];
if (insertDataEnd1 > insertDataEnd) {
insertDataEnd = insertDataEnd1;
CGFloat insertDataCost = (insertDataEnd - insertDataBegin);
NSLog(@"*** 線程1開啟事務方式插入數據花費時間 %f s",insertDataCost);
}
}];
});
//線程二
dispatch_async(q2, ^{
[queue inDatabase:^(FMDatabase *db2) {
[db2 beginTransaction];
for (NSInteger i = range*1; i < addressList.count; i++) {
NSDictionary *item = addressList[i];
// 初始化數據表
NSString *insertSql = @"INSERT INTO t_address (addr_id, parent_addr_id,addr_value) VALUES (?,?,?)";
BOOL insert = [db2 executeUpdate:insertSql withArgumentsInArray:@[item[@"addr_id"],item[@"parent_addr_id"],item[@"addr_value"]]];
}
[db2 commit];
NSTimeInterval insertDataEnd2 = [[NSDate date] timeIntervalSince1970];
if (insertDataEnd2 > insertDataEnd) {
insertDataEnd = insertDataEnd2;
CGFloat insertDataCost = (insertDataEnd - insertDataBegin);
NSLog(@"*** 線程2開啟事務方式插入數據花費時間 %f s",insertDataCost);
}
}];
});
/*
//線程三
dispatch_async(q3, ^{
[queue inDatabase:^(FMDatabase *db2) {
[db2 beginTransaction];
for (NSInteger i = range*2; i < range*3; i++) {
NSDictionary *item = addressList[i];
// 初始化數據表
NSString *insertSql = @"INSERT INTO t_address (addr_id, parent_addr_id,addr_value) VALUES (?,?,?)";
BOOL insert = [db2 executeUpdate:insertSql withArgumentsInArray:@[item[@"addr_id"],item[@"parent_addr_id"],item[@"addr_value"]]];
}
[db2 commit];
NSTimeInterval insertDataEnd3 = [[NSDate date] timeIntervalSince1970];
if (insertDataEnd3 > insertDataEnd) {
insertDataEnd = insertDataEnd3;
CGFloat insertDataCost = (insertDataEnd - insertDataBegin);
NSLog(@"*** 線程3開啟事務方式插入數據花費時間 %f s",insertDataCost);
}
}];
});
//線程四
dispatch_async(q4, ^{
[queue inDatabase:^(FMDatabase *db2) {
[db2 beginTransaction];
for (NSInteger i = range*3; i < addressList.count; i++) {
NSDictionary *item = addressList[i];
// 初始化數據表
NSString *insertSql = @"INSERT INTO t_address (addr_id, parent_addr_id,addr_value) VALUES (?,?,?)";
BOOL insert = [db2 executeUpdate:insertSql withArgumentsInArray:@[item[@"addr_id"],item[@"parent_addr_id"],item[@"addr_value"]]];
}
[db2 commit];
NSTimeInterval insertDataEnd4 = [[NSDate date] timeIntervalSince1970];
if (insertDataEnd4 > insertDataEnd) {
insertDataEnd = insertDataEnd4;
CGFloat insertDataCost = (insertDataEnd - insertDataBegin);
NSLog(@"*** 線程4開啟事務方式插入數據花費時間 %f s",insertDataCost);
}
}];
});
*/
}