今天比较闲看到大家在群里讨论关于数据库操作的问题,其中谈到了“事务”这个词,坦白讲虽然作为计算机专业的学生,在上学的时候确实知道存储过程、触发器、事务等等这些名词的概念,但是由于毕业后从事的不是服务器方面的工作,对数据库方面的知识一直停留在理论阶段,在iOS中有两种比较常用的数据库,一种是sqlite,另外一种是coreData,这二者的优缺点在这里不再赘述,如有感兴趣的同学可以查阅相关资料
令人兴奋的是,当谈到“事务”时,我想到了sqlite本身是支持事务操作的,FMDB作为sqlite的上层封装也对事务进行了支持,那么说了这么多给不了解“事务”这个词的同学解释一下吧:通常一次 sqlite3_exec 就是一次事务,假如你要对数据库中的Stutent表插入新数据,那么该事务的具体过程是:开始新事物->插入数据->提交事务,那么当我们要往该表内插入500条数据,如果按常规操作处理就要执行500次“开始新事物->插入数据->提交事务”的过程。
好吧,今天的重点来了,举个例子:假如北京的一家A工厂接了上海一家B公司的500件产品的订单,思考一下:A工厂是生产完一件立即就送到B公司还是将500件产品全部生产完成后再送往B公司?答案肯定是后者,因为前者浪费了大量的时间、人力物力花费在往返于北京和上海之间。同样这个道理也能用在我们的数据库操作上,下面是我自己对使用事务和不使用事务的两种测试:
测试代码:
下面代码中总共调用了三次插入数据的方法,由于第一次调用过程中包含数据库和表的创建等一系列过程,因此第二三次调用插入方法计算出的时间才是准确的。(数据库采用FMDB)
-(BOOL)openDB
{
NSFileManager *filaManager = [NSFileManager defaultManager];
BOOL isFile = NO;
//判断有没有数据库文件
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [paths objectAtIndex:0];
//dbPath: 数据库路径,在Document中。
NSString* uidPath = documentDirectory;
BOOL isDir = FALSE;
BOOL isDirExist = [filaManager fileExistsAtPath:uidPath isDirectory:&isDir];
if(!(isDirExist && isDir))
{
BOOL bCreateDir = [filaManager createDirectoryAtPath:uidPath withIntermediateDirectories:YES attributes:nil error:nil];
if(!bCreateDir)
{
MCLogDebug(@"Create MsgPic Directory Failed.");
}
}
NSString* DBFilePath = [uidPath stringByAppendingPathComponent:@"DBInfo.db"];
if([filaManager fileExistsAtPath:DBFilePath])
isFile = YES;
self.iFmDb = [FMDatabase databaseWithPath:DBFilePath];
self.databaseQueue = [FMDatabaseQueue databaseQueueWithPath:DBFilePath];
NSLog(@"openDB");
if (![self.iFmDb open])
{
return NO;
}
return YES;
}
-(void)testDBSpeed
{
NSDate *date1 = [NSDate date];
[self insertData:500 useTransaction:NO];
NSDate *date2 = [NSDate date];
NSTimeInterval a = [date2 timeIntervalSince1970] - [date1 timeIntervalSince1970];
NSLog(@"不使用事务插入500条数据用时%.3f秒",a);
[self insertData:1000 useTransaction:YES];
NSDate *date3 = [NSDate date];
NSTimeInterval b = [date3 timeIntervalSince1970] - [date2 timeIntervalSince1970];
NSLog(@"使用事务插入500条数据用时%.3f秒",b);
}
- (void)insertData:(int)fromIndex useTransaction:(BOOL)useTransaction
{
[self openDB];
if (useTransaction) {
[self.iFmDb beginTransaction];
BOOL isRollBack = NO;
@try {
for (int i = fromIndex; i<500+fromIndex; i++) {
NSString *nId = [NSString stringWithFormat:@"%d",i];
NSString *phone= [[NSString alloc] initWithFormat:@"phone_%d",i];
NSString *strName = [[NSString alloc] initWithFormat:@"name_%d",i];
NSString *roomID= [[NSString alloc] initWithFormat:@"roomid_%d",i];
NSString *sql = @"INSERT INTO GroupPersonInfo(uid,phone,name,groupRoomId) VALUES (?,?,?,?)";
BOOL a = [self.iFmDb executeUpdate:sql,nId,phone,strName,roomID];
if (!a) {
NSLog(@"插入失败1");
}
}
}
@catch (NSException *exception) {
isRollBack = YES;
[self.iFmDb rollback];
}
@finally {
if (!isRollBack) {
[self.iFmDb commit];
}
}
}else{
for (int i = fromIndex; i<500+fromIndex; i++) {
NSString *nId = [NSString stringWithFormat:@"%d",i];
NSString *phone= [[NSString alloc] initWithFormat:@"phone_%d",i];
NSString *strName = [[NSString alloc] initWithFormat:@"name_%d",i];
NSString *roomID= [[NSString alloc] initWithFormat:@"roomid_%d",i];
NSString *sql = @"INSERT INTO GroupPersonInfo(uid,phone,name,groupRoomId) VALUES (?,?,?,?)";
BOOL a = [self.iFmDb executeUpdate:sql,nId,phone,strName,roomID];
if (!a) {
NSLog(@"插入失败2");
}
}
}
[self closeDB];
}
- (BOOL) closeDB
{
NSLog(@"closeDB");
if (self.iFmDb)
{
return [self.iFmDb close];
}
return NO;
}
从上面这些真实的数据来看,你就应该知道当你批量更新数据库的时候应该利用事务处理!
总结:使用事务处理就是将所有任务执行完成以后将结果一次性提交到数据库,如果此过程出现异常则会执行回滚操作,这样节省了大量的重复提交环节所浪费的时间
我自己测试的效率: