SQLite是库级锁,支持并发读,但是不支持并发写。所以如果多个线程同时进行写操作,就有可能造成database locked问题。如果是纯原生应用,这篇文章介绍了怎么利用FMDatabaseQueue避免锁库:
使用FMDatabaseQueue避免database locked问题
但是如果是hybrid应用,就相对比较复杂,我们的APP就踩坑了。刚开始搭框架的时候,使用cordova搭建了hybrid框架,并使用SQLitePlugin,来支持js访问数据库。然后原生的部分,就用FMDB来访问数据库
一开始的时候,大部分的数据库访问都是在js里调用的,想必这个SQLitePlugin已经处理了并发写的场景,所以一直都没有出现过锁库的情况。原生的部分,一开始没有经验,遇到了锁库的问题,后来也用FMDatabaseQueue解决了。当时大部分的操作都在js里,如果涉及到原生访问数据库的场景,一般都用模态窗阻止了用户的其他操作,所以2个sqlite的入口没有发生冲突,安稳了很长时间
但是最近需求变得更复杂,出现了js和原生代码同时操作数据库的场景,结果2个队列产生了竞争,database locked的问题又开始概率出现了
所以教训就是:如果是hybrid的应用,最好在一开始的时候就要写好数据库的公共组件,SQLitePlugin肯定是用不了了,应该是自己写一个cordova plugin,调用数据库访问的公共组件,这样无论是原生的代码,还是js代码,对数据库的操作都会被FMDatabaseQueue统一放在队列里管理,才不会出现多线程并发写的问题
对于我们这种已经踩坑的来说,只有几个办法:
1、改造,弃用SQLitePlugin
2、分析代码的调用顺序,用setTimeout等办法,尽量把调用错开
3、增加异常处理,如果由于database locked而写入失败,就等待再尝试写一次
第一种方法是最彻底的,可以从根本上解决问题,但是对于一个老项目,改造起来难度比较大,第二,三种办法实现比较简单,但是都不是彻底解决问题的办法