關(guān)于FMDB數(shù)據(jù)庫(kù)崩潰和多線程研究

最近做項(xiàng)目使用本地化的時(shí)候出現(xiàn)一個(gè)崩潰,一開(kāi)始是偶發(fā),后來(lái)幾乎高達(dá)99%的崩潰率。好吧,抽個(gè)空研究研究吧。

rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0);

崩潰在FMDB的這行代碼上,相信很多人都遇到過(guò)。我也百度了一番,但是大多都沒(méi)有解決辦法。

1. 去除多線程

這個(gè)問(wèn)題其實(shí)很簡(jiǎn)單。多線程引起的。說(shuō)來(lái)也好笑,后面會(huì)講為什么出現(xiàn)這個(gè)bug。

出現(xiàn)崩潰而且毫無(wú)提示,好在是有觸發(fā)點(diǎn)。研究了一番代碼,發(fā)現(xiàn)是在數(shù)據(jù)庫(kù)操作的外面套了一個(gè)異步子線程。。。

但FMDB不能在多個(gè)線程中共同一個(gè)FMDatabase對(duì)象,因?yàn)檫@個(gè)類(lèi)本身不是線程安全的,如果這樣使用會(huì)造成數(shù)據(jù)混亂等問(wèn)題。然后巧的是之前的程序員使用的加鎖是數(shù)據(jù)庫(kù)本身。。。那么,我使用多線程,開(kāi)辟出來(lái)的線程會(huì)持有一個(gè)數(shù)據(jù)庫(kù),主線程本身也持有該數(shù)據(jù)庫(kù),這就造成了資源競(jìng)爭(zhēng)了。然后數(shù)據(jù)庫(kù)本身可能已經(jīng)不唯一了。然后你用數(shù)據(jù)庫(kù)作為依據(jù)去加鎖,可能已經(jīng)失去了本身的意義。

所以做法很簡(jiǎn)單,去掉外部的多線程就解決問(wèn)題了。


這里提一下,數(shù)據(jù)庫(kù)不是不支持多線程操作,最新版的FMDB是支持多線程操作的。多線程操作同一個(gè)數(shù)據(jù)看的時(shí)候,每個(gè)線程調(diào)用數(shù)據(jù)庫(kù)的時(shí)候都及時(shí)開(kāi)關(guān),其他線程要操作數(shù)據(jù)庫(kù)必須等數(shù)據(jù)庫(kù)關(guān)閉后才能打開(kāi)操作。當(dāng)然,這樣會(huì)造成混亂。不怎么推薦。

FMDatabaseQueue:類(lèi)似于線程隊(duì)列。

使用的時(shí)候可以先判斷該隊(duì)列是否存在,不存在再創(chuàng)建:(沒(méi)有MarkDown排版好難受)

FMDatabaseQueue *dbQueue = [_dbQueueDic objectForKey:dbPath];
if (dbQueue == nil) {
??? dbQueue = [FMDatabaseQueue databaseQueueWithPath:dbPath];
}

//調(diào)用方式 多次調(diào)用亦串行

[queue inDatabase:^(FMDatabase *db) {

??? [db open];

??? //可以執(zhí)行多條語(yǔ)句,串行執(zhí)行

??? resultFlag = [db executeUpdate:sql];//普通執(zhí)行

??? resultFlag = [db executeUpdate:sql];//普通執(zhí)行

??? resultFlag = [db executeUpdate:sql withParameterDictionary:args];//添加參數(shù)執(zhí)行

??? [db close];

}]


3. 關(guān)于事務(wù)

有時(shí)候我們的數(shù)據(jù)庫(kù)業(yè)務(wù),可能會(huì)考慮到數(shù)據(jù)安全唯一性,如果某段sql執(zhí)行了其中一部分或者一半失效了,那么需要全部回滾,就可以用到事務(wù)操作。代碼很簡(jiǎn)單,封裝好的一個(gè)方法:

[self.queue inTransaction:^(FMDatabase *db, BOOL *rollback) {

??? resultFlag = [db executeUpdate:sql1];

??? resultFlag = [db executeUpdate:sql2];

??? resultFlag = [db executeUpdate:sql3];

? }];


3. 為什么添加多線程

這個(gè)還是線程問(wèn)題,慢慢意識(shí)到線程真的很重要。問(wèn)題:tableview上拉加載以后不展示無(wú)數(shù)據(jù)footer(阻止用戶(hù)繼續(xù)上拉加載),聯(lián)網(wǎng)無(wú)問(wèn)題,本地化有問(wèn)題。那么很快定位到是因?yàn)榫W(wǎng)絡(luò)請(qǐng)求是異步的,那么我們可以在子線程請(qǐng)求完成以后通過(guò)回調(diào),拿到數(shù)據(jù)刷新UI。? 但是本地化的時(shí)候就變成了全在主線程進(jìn)行,然后就在本地化操作的最外面加了個(gè)異步的子線程。這樣做到了異步回調(diào)的形式。先執(zhí)行footerEndRefreshing后執(zhí)行endRefreshingWithNoMore解決了該問(wèn)題。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。