面试题答案
一键面试1. 锁机制
SQLite 本身提供了几种锁模式来处理并发访问,常用的是 PRAGMA locking_mode
。默认情况下,SQLite 使用 NORMAL
模式,在此模式下,SQLite 会根据操作类型在不同阶段获取不同的锁(共享锁或排他锁)。为了更好地处理并发,可以将其设置为 EXCLUSIVE
模式,这种模式下 SQLite 在开始写操作前就获取排他锁,减少锁争用。
2. 事务管理
事务是确保数据一致性的关键。在多线程环境下,每个线程应该在执行数据库操作时使用事务。通过将多个操作封装在一个事务中,要么所有操作都成功提交,要么在出现错误时回滚。
3. 优化并发性能
- 连接池:创建一个连接池,让多个线程从连接池中获取 SQLite 连接。这样可以减少频繁创建和销毁连接的开销。
- 批量操作:尽量将多个小的数据库操作合并为一个大的操作,减少锁的获取次数。
代码示例
以下是一个简单的 iOS 代码示例,使用 FMDB
框架(一个广泛使用的 SQLite 封装库)来处理 SQLite 并发访问。
首先,确保在项目中导入 FMDB
。
#import <UIKit/UIKit.h>
#import "FMDatabase.h"
#import "FMDatabaseQueue.h"
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) FMDatabaseQueue *databaseQueue;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *databasePath = [documentsPath stringByAppendingPathComponent:@"test.db"];
self.databaseQueue = [FMDatabaseQueue databaseQueueWithPath:databasePath];
// 初始化数据库表
[self.databaseQueue inDatabase:^(FMDatabase *db) {
NSString *createTableSQL = @"CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)";
[db executeUpdate:createTableSQL];
}];
return YES;
}
// 示例函数,在不同线程中插入数据
- (void)insertDataInBackgroundThread:(NSString *)name {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self.databaseQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
NSString *insertSQL = @"INSERT INTO users (name) VALUES (?)";
BOOL success = [db executeUpdate:insertSQL, name];
if (!success) {
NSLog(@"插入数据失败");
*rollback = YES;
}
}];
});
}
@end
在上述代码中:
FMDatabaseQueue
是FMDB
提供的用于处理多线程安全的数据库队列,它内部使用了 GCD 来管理队列和锁。inDatabase:
方法用于在数据库连接上执行单个操作。inTransaction:
方法用于在事务中执行多个操作,确保数据的一致性。
通过上述方式,可以有效地处理 SQLite 数据库在多线程 iOS 应用中的并发访问,保证数据一致性并优化性能。