开篇闲话:

     准备开始写一些技术文章,听了无数大神论述写博客的道理,却仍旧未写一篇博客。四大才子,逼格高者如岳父,拍个电影,传出无数高逼格、高冷的段子,引出一段段风潮。呵呵,扯远了,现在想写博客,最主要还是工作了一段时间以后,越来越理解为何大家都推荐写博客。引用刘未鹏在《暗时间》中一句话——书写是为了更好的思考。

     感兴趣的可以看看这篇文章----《为什么你从现在开始就应该写博客?》

http://mindhacks.cn/2009/02/15/why-you-should-start-blogging-now/ 

     闲话扯过了,进入正题。在过去一年的项目开发中,涉及使用sqlite,累积了一些经验,在这里做一个分享,也算是做一个笔记。


正题:

关于sqlite介绍、基本使用等网上的资料很多,大家可以查阅参考。

参考:

http://www.sqlite.org/(sqlite官网)

http://www.cnblogs.com/stephen-liu74/category/348367.html(一位高手整理的学习笔记)

我主要分享一下以下几方面:sqlite 的并发控制、sqlite 加密、sqlite 性能优化。这些都是我在开发中遇到的问题,且解决问题的过程中,在网上找到讲的比较清楚全面的资料相对少一些。

今天先分享一下开发sqlite中几乎必然会遇到的问题——sqlite并发访问控制。

     你是否在开发使用sqlite的过程中 sqlite 抛错 database is locked 而困扰?如果是,请看下文分解。

在多线程环境下,访问sqlite是线程安全的吗?——不是,访问时需要做并发控制。

      sqlite 支持 single-thread/multi-thread/serialized 三种不同的线程安全模式。可以在编译sqlite组件时进行配置,或者可以通过 sqlite3_threadsafe()/sqlite3_config() 在程序运行时进行查看并配置线程安全模式。sqlite的官网讲得很详细,参考(http://www.sqlite.org/threadsafe.html)。

     通过上面的配置 sqlite 就是线程安全了吗?经过实际写 demo 测试,进行 multi-thread 或 serialized 配置以后,多线程并发读的场景下,没有问题。但是多线程并发写时依旧会抛错 database is locked。开始怀疑是因为多个线程使用了同一个数据库连接,检查代码以后发现,demo 测试的代码中每个线程都是独立的数据库连接。也就是说 sqlite 实际支持的是多读单写。        

     经过上面的测试以后,我们在项目中对sqlite使用了线程互斥锁进行并发控制。其实根据具体开发环境支持,更建议使用读写锁,这样并发读时效率更好。


     后续在开发中又遇到了新的问题,在进行了 multi-thread 或 serialized 配置的情况下,并发写同一个数据库文件中的两张表,也会抛错 database is locked。查了资料,sqlite内部使用的是大粒度的文件锁,所有会有以上问题。当时我们因为项目需要快速上线,就做了简单的处理,直接多张表的并发控制使用同一个锁来解决,这个方案牺牲了效率,但是改动小,影响小,不涉及升级等问题。其实还可以考虑分库,把需要并发访问控制的表放到单独的数据库文件中。解决问题的方法通常有很多,根据实际的项目情况,用不同的方案。只是以上这两个方法都不算是特别好的方案。


     使用了多线程互斥锁,多进程访问怎么办?

     使用进程锁?效率!性能!进程锁牺牲太大,不能接受。查阅资料,发现sqlite提供两个 busy handle 函数sqlite3_busy_timeout()/sqlite3_busy_handle() 在并发访问失败时,会调用注册的 busy handle 函数,在注册的自定义的 busy handle 函数中可以进行处理(如重试n次等)。(参考:http://www.sqlite.org/c3ref/busy_handler.html)


     或者直接一点,对sqlite接口做一层封装,对写数据执行失败时,识别SQLITE_BUSY错误码,进行一定条件的持续重试。(如sleep 5ms重试一次,最多重试n次)


     这样可以解决问题,不过还是显得麻烦,是否还有简单的办法?还真有。直接使用

sqlite3_exec(db, "PRAGMA busy_timeout=times", 0, 0, err);  times 是设置的超时时间。这样就把重试放给 sqlite 内部去做,操作时间可设置得稍长一点(我们设置了30s)。

通过上面的方法,基本解决了多线程、多进程访问 sqlite 的问题,也不会影响 sqlite 的效率。


后续我会继续写sqlite加密和sqlite性能优化两个续篇,欢迎关注。


因个人水平有限,如有笔误、讲解不清楚、探究的不清楚的地方。还请各位海涵,并指出问题,帮助我干掉它。转载请注明出处。