转载:https://www.cnblogs.com/lijingcheng/p/4454884.html
转载:https://blog.csdn.net/venchia_lu/article/details/50456142
转载: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
转载:https://www.cnblogs.com/xienb/p/3455562.html(相同的项目场景)
转载:http://www.cnblogs.com/gfxxbk/p/5469200.html(错误码)
转载:https://www.cnblogs.com/feng9exe/p/10682567.html(sqlite线程模型)
转载:https://www.cnblogs.com/cchust/p/5121559.html(API详细介绍)
最近项目中涉及到sqlite并发读写的问题,先搞清楚sqlite3自身的机制。
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来进行一些处理,直到自己获得写权限之后。
|