大多数产品在本地Sqlite存储的时候,基本都是用了串行模式。即整个应用全局只有一个sql操作句柄,用单例管理着这个SqlHandler。另外还有一个模式叫线程模式。这种模式下很容易出现Sqlite_Busy错误。

  1. 当有写操作时,其他读操作会被驳回。
  2. 当有写操作时,其他写操作会被驳回。
  3. 当开启事务时,在事务提交之前,其他写操作会被驳回。
  4. 当开启事务时,在事务提交之前,其他事务请求会被驳回。
  5. 当有读操作时,其他写操作会被驳回。
  6. 读操作之间能够并发执行。

第三方库WCDB支持多线程度于读与读,读与写并发执行,写与写串行执行。WCDB 在多线程方面明显优于 FMDB 和 ModelSqliteKit,通过 WCDB 的改造,使得SQLite的性能发挥到极致。

我们可以设计所有写的任务都放在一个队列中。

1
_gyWriteQueue = dispatch_queue_create([[NSString stringWithFormat:@"gy_write_queue.%@", self]

当所有关于新增,删除,修改等sql 业务时候统一用_gyWriteQueue。
1
2
3
dispatch_sync(_gyWriteQueue,^{
//进行写操作
);

我们可以根据队列的名字,确定是属于写队列,还是读队列。
1
2
const char * queueLabel = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL); //获取当前队列的名字。
NSString *queueName = [NSString stringWithUTF8String:queueLabel];

如果是读队列,则从读的队列池中取中取出与之对应的队列,拿出队列中所持有的handle句柄。这样就能保证每个队列中唯一正在执行的子线程使用该handle句柄。
首先准备队列池,队列最大并发数1,并且持有一个handle. 读取操作时候,从队列池取当前操作数最小的队列进行添加任务。

以下是创建读队列的队列池:

1
2
3
4
5
6
7
8
9
10
11
12
13
- (void)createSqlHandleToPool:(NSString *)queueName
{
GYSqlHandle *handleModel = [[GYSqlHandle alloc] init]; //初始化即创建了一个sql句柄
if(handleModel.sqlHandle){
GYSqlOperationQueue *sqlQueue = [[GYSqlOperationQueue alloc] init];
sqlQueue.name = queueName;
sqlQueue.sqlQueueName = queueName;
sqlQueue.maxConcurrentOperationCount = 1;
sqlQueue.handle = handleModel;

[self.dbQueueList addObject:sqlQueue];
}
}

由于sqlite的句柄返回的是结构体指针,所以用GYSqlHandle对象进行包裹一下方便处理。

GYSqlOperationQueue代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@interface GYSqlOperationQueue: NSOperationQueue
@property(nonatomic,strong) GYSqlHandle *handle; //每一个queue持有一个handle模型
@property(nonatomic,strong) NSString *sqlQueueName; //记录队列名
- (NSComparisonResult)compareByOperationsCount:(GYSqlOperationQueue *) another;
-(void)addSqlOperationWithBlock:(void (^)(void))block;
@end

@implementation GYSqlOperationQueue

- (NSComparisonResult)compareByOperationsCount:(GYSqlOperationQueue *) another
{
if (self.operations.count > another.operations.count) {
return NSOrderedAscending;
}
else if(self.operations.count < another.operations.count)
{
return NSOrderedDescending;
}
return NSOrderedSame;
}

-(void)addSqlOperationWithBlock:(void (^)(void))block {
if(self){
NSOperation *operation = [NSBlockOperation blockOperationWithBlock:block];
id lastOperation = self.operations.lastObject;
if(lastOperation){
[operation addDependency:lastOperation];
}
[self addOperation:operation];
}
}
@end

这里仅为个人的一些试验性的探索,因为全局业务代码无法固定使用某统一队列,线程之间可能出现嵌套等,无法保证一个Handle同一时间只有一个线程在使用。这也是我的疑惑点,希望大家多多交流。

评论