工作二总结——objective-C中sqlite3数据库的处理(其三) sqlite3数据库事务的使用

来源:互联网 发布:嵌入式软件版本说明 编辑:程序博客网 时间:2024/05/17 19:58

以前知道的太少了,这样大量的数据更新居然没有用到数据库事务,怪不得时间开销这么高。

工作二后来优化了一下,加入了sqlite3的事务,效果十分明显,70秒的数据库生成时间缩短到了1.5秒。

事务(transaction)的原理是,对数据库进行更新的语句先储存起来,储存到一定程度将这些语句一并提交(commit),如果尚未提交,但中途出现了异常,则这些语句都可以撤回(rollback)。

使用事务有两个非常明显的好处:

1、将大量更新语句合并到一起,极大的缩短了访问数据库的次数,如此大量提高了效率和吞吐量。例如本人的“工作二”,将八万多次数据库更新合并到一次提交,原操作时间70秒降低到了1.5秒,效率提高的幅度简直可怕。

2、避免了一些非常恶心的情况,比如同样一个对象有许多不同的属性都要插入到数据库的不同表中,需要多句sql更新才能完成操作。如果操作中途出现了异常,则可能某个对象只有一半的数据输入了数据库,还有另一半是空白的,这还不如完全没输入。而事务的rollback就是干这个用的。

FMDB下使用事务方式十分简单:

建立数据库db

FMDatabase *db = [FMDatabase databaseWithPath:path];

事务开始

[db beginTransaction];BOOL isRollback = NO;
具体数据库操作可以包在@try中,如果捕获到异常就rollback

@try {// 数据库操作代码}@catch (NSException *exception) {isRollback = YES;[db rollback];}@finally {if (isRollback == NO) {[db commit];}}
finally中判断,如果没有rollback就能提交。

因此工作二目前的最终结果是这样,没准以后还要改,比如C风格的字符串分割可以改成objC的tokenizer。


#import <Foundation/Foundation.h>#import "FMDB.h"const char *databasePath;const char *txtPath;int entityCount, tagCount, mapCount;int main(int argc, const char * argv[]) {    if (argc != 3) {        NSLog(@"Wrong arg number! Please type in txt path at first, and then the db path!");        return 1;    }    txtPath = argv[1];    FILE *txtFile = fopen(txtPath, "r");    if (!txtFile) {        NSLog(@"File loading is failed!");        return 1;    }    databasePath = argv[2];        // 判断表是否已经存在    FILE *dbFile = fopen(databasePath, "r");    BOOL dbExisted = YES;    if (!dbFile) {        dbExisted = NO;    }    fclose(dbFile);        FMDatabase *db = [[FMDatabase alloc] initWithPath:[NSString stringWithFormat:@"%s", databasePath]];    if ([db open] == NO) {        NSLog(@"Failed to open database!");        return 1;    }        // 为了防止表格式有问题,因此先删去同名表,再创建相应表    if (dbExisted) [db executeUpdateWithFormat:@"drop table entity;"];    BOOL ret = [db executeUpdateWithFormat:@"create table entity(id integer primary key autoincrement, name TEXT not null unique, score integer, popularity integer);"];    if (ret != YES) {        NSLog(@"Creating table entity failed!");        exit(1);    }    if (dbExisted) [db executeUpdateWithFormat:@"drop table tag;"];    ret = [db executeUpdateWithFormat:@"create table tag(id integer primary key autoincrement, name TEXT not null unique, score integer, popularity integer);"];    if (ret != YES) {        NSLog(@"Creating table tag failed!");        exit(1);    }    if (dbExisted) [db executeUpdateWithFormat:@"drop table map;"];    ret = [db executeUpdateWithFormat:@"create table map(id integer primary key autoincrement, entity_id not null, tag_id integer);"];    if (ret != YES) {        NSLog(@"Creating table map failed!");        exit(1);    }        NSDate *start = [NSDate date];        [db beginTransaction];    BOOL isRollback = NO;        @try {        // 用C的方式操作文本文件更加方便        char stringFromTxt[500], last[500]; //开始读取txt中数据,stringFromTxt代表txt一行,last表示上次处理的一行        fgets(stringFromTxt, 500, txtFile);        memset(last, 0, sizeof(last));        NSMutableDictionary *allTags = [NSMutableDictionary dictionary];//内存判断tag是否重复比让数据库判断unique值更快        while (1) {            // 每趟循环处理文本的一行,循环结束条件为“到达最后一行”,经测试,下面两种情况覆盖了结束条件            if (strcmp(last, stringFromTxt) == 0 || stringFromTxt == NULL){                break;            }            // 插入entity和score的数据            char *entityName = strtok(stringFromTxt," \t");            char *entityScore = strtok(NULL, " \t");            NSString *SQL = [NSString stringWithFormat:@"insert into entity(id,name,score,popularity) values(%d,'%s',%s, %d);", entityCount, entityName, entityScore, 0];            ret = [db executeUpdate:SQL];            if (ret != YES) NSLog(@"插入entity失败,第%d次", entityCount);                        // 开始处理tag,txt文件中出现过一个entity对应多个相同tag的情况,在map表构造中应该极力避免这种情况,用mutableSet可以保证高效            NSMutableSet *tagSet = [NSMutableSet set];            char *tag = strtok(NULL, " \n\t");            while (tag != NULL) {                // entity对应了两个相同的tag,直接跳过,处理下一个tag                NSString *tagObject = [NSString stringWithFormat:@"%s", tag];//会多次使用,因此先存下来                if ([tagSet containsObject:tagObject] == NO) {                    [tagSet addObject:tagObject];                } else {                    tag = strtok(NULL, " \t\n");                    continue;                }                // 如果是新出现的tag,就放入tag总集,查询开销为O(log(tag数))                NSNumber *tagId = allTags[tagObject];                if (tagId == nil) {                    tagId = [NSNumber numberWithInt:tagCount];                    [allTags setObject:tagId forKey:tagObject];                                        // 随机得到tagScore,并插入tag表,只有新出现的tag才有必要加入tag表                    int tagScore = arc4random() % 101;                    SQL = [NSString stringWithFormat:@"insert into tag(id,name,score,popularity) values(%d, '%s', %d, %d);", tagCount ++, tag, tagScore, 0];                    ret = [db executeUpdate:SQL];                }                // 需要将所有tag映射关系添加至map表                int intTagId = [tagId intValue];                SQL = [NSString stringWithFormat:@"insert into map(id,entity_id,tag_id) values(%d, %d, %d);", mapCount++, entityCount, intTagId];                [db executeUpdate: SQL];                                tag = strtok(NULL, " \t\n");            }                        strcpy(last, stringFromTxt);            fgets(stringFromTxt, 500, txtFile);            entityCount ++;        }    }    @catch (NSException *exception) {        isRollback = YES;        [db rollback];    }    @finally {        if (isRollback == NO) {            [db commit];        }    }        NSDate *end = [NSDate date];    double timeInterval = [end timeIntervalSinceDate:start];        NSLog(@"Time Consume: %lf", timeInterval);        [db close];    return 0;}





0 0
原创粉丝点击