转载:http://www.javashuo.com/article/p-bkattthh-ew.htmlhtml
转载:https://blog.csdn.net/venchia_lu/article/details/50456142sql
转载:https://blog.csdn.net/guofu8241260/article/details/36378291数据库
转载:https://www.jianshu.com/p/15051fbd5a35?utm_campaign多线程
转载:http://www.cnblogs.com/hustcat/archive/2009/03/01/1400757.html并发
转载:http://www.javashuo.com/article/p-guuroinp-ma.html(相同的项目场景)函数
转载:http://www.cnblogs.com/gfxxbk/p/5469200.html(错误码)spa
转载:http://www.javashuo.com/article/p-fecetapm-hy.html(sqlite线程模型).net
转载:http://www.javashuo.com/article/p-fegylplx-cn.html(API详细介绍)线程
最近项目中涉及到sqlite并发读写的问题,先搞清楚sqlite3自身的机制。sqlite
1.sqlite3支持多线程同时读操做,但不支持多线程同时写操做。
2.同一时刻只能有一个线程去进行写操做,而且在一个线程进行写操做的时候,其余线程是不能进行读操做的。
当一个线程正在写操做时,其余线程的读写都会返回操做失败的错误,显示数据库文件被锁住。
3.sqlite3的锁类型
sqlite3总共有三种事务类型:BEGIN [DEFERRED /IMMEDIATE / EXCLUSIVE] TRANSCATION,五种锁,按锁的级别依次是:UNLOCKED /SHARED /RESERVERD /PENDING /EXCLUSIVE。
当执行select即读操做时,须要获取到SHARED锁(共享锁);
当执行insert/update/delete操做(即内存写操做时),须要进一步获取到RESERVERD锁(保留锁),当进行commit操做(即磁盘写操做时),须要进一步获取到EXCLUSIVE锁(排它锁)。
对于RESERVERD锁,sqlite3保证同一时间只有一个链接能够获取到保留锁,也就是同一时间只有一个链接能够写数据库(内存),可是其它链接仍然能够获取SHARED锁,也就是其它链接仍然能够进行读操做(这里能够认为写操做只是对磁盘数据的一分内存拷贝进行 修改,并不影响读操做)。
对于EXCLUSIVE锁,是比保留锁更为严格的一种锁,在须要把修改写入磁盘即commit时须要在保留锁/未决锁的基础上进一步获取到排他锁,顾名思义,排他锁排斥任何其它类型的锁,即便是SHARED锁也不行,因此,在一个链接进行commit时,其它链接是不能作任何操做的(包括读)。
PENDING锁(即未决锁),则是比较特殊的一种锁,它能够容许已获取到SHARED锁的事务继续进行,但不容许其它链接再获取SHARED锁,当已存在的SHARED锁都被释放后(事务执行完成),持有未决锁的事务就能够得到commit的机会了。sqlite3使用这种锁来防止writer starvation(写饿死)。
sqlite3只支持库级锁,库级锁意味着什么?——意味着同时只能容许一个写操做,也就是说,即事务T1在A表插入一条数据,事务T2在B表中插入一条数据,这两个操做不能同时进行,即便你的机器有100个CPU,也没法同时进行,而只能顺序进行。表级都不能并行,更别说元组级了——这就是库级锁。可是,SQLite尽可能延迟申请X锁,直到数据块真正写盘时才申请X锁,这是很是巧妙而有效的。
4.解决方案
对于多线程写数据库的状况,Sqlite3不能实现同时写,可是能够实现串行写数据,也就是一个线程在写的时候,其余线程等待,第一个线程写完的时候,另外一个线程得到数据库文件锁开始写。Sqlite3提供了接口sqlite3_busy_handler(),来实现多线程串行写数据。BusyHandler实际上是一个回调函数。也就是当A线程正在写操做时,其余线程写失败时进行的重试操做,其余线程不断地调用BusyHandler来进行一些处理,直到本身得到写权限以后。