sqlite数据库并发问题
目录描述sqlite的锁死锁现象解决方法描述做过一些qt工程项目,单机版软件中经常性的会使用sqlite数据库,优缺点都很明显。优点:轻量级,容易跨平台移植,操作简单,尤其针对linux这种种类繁杂的系统,适配起来相对简单,而且一些系统已经内置了sqlite动态库。缺点:既然是轻量级,那功能和性能上就有点跟不上了,尤其是多线程操作时的并发问题。sqlite的锁用过sqlite的小伙伴都知道,sql
目录
描述
做过一些qt工程项目,单机版软件中经常性的会使用sqlite数据库,优缺点都很明显。
优点:轻量级,容易跨平台移植,操作简单,尤其针对linux这种种类繁杂的系统,适配起来相对简单,而且一些系统已经内置了sqlite动态库。
缺点:既然是轻量级,那功能和性能上就有点跟不上了,尤其是多线程操作时的并发问题。
sqlite的锁
用过sqlite的小伙伴都知道,sqlite使用的是库锁,当然也可以理解成“文件锁”。sqlite的锁并非单纯的互斥,多线程环境下使用不当就容易陷入死锁。
sqlite的锁有shared共享锁、reserve预留锁、exclusive独占锁/排他锁、pending未决锁 等几种。
- 当进行select时候获取的是shared,多个读操作同时可以获取shared锁。
- 当进行update、delete、drop的时候获取的是reserve,reserve与shared不互斥,两个reserve不能同时获取。
- 当进行commit事务提交的时候,获取的是exclusive,exclusive与其他锁不能同时获取。
- pending是防止写饿死存在的,可以理解为获取exclusive之前获取的一种锁。假如没有pending,当读操作频繁时,不间断的shared锁出现,exclusive的特性使得exclusive获取不到,就出现了写饿死现象。所以commit事务的时候先获取pending锁,当shared存在时,依然可以获取pending锁,两个pending不能同时获取,而且获取pending之后,新的shared则不能再获取。
死锁现象
以两个线程同时操作数据库举个例子:
pthreadA创建了数据库连接,执行select查询获取了shared锁。
pthreadB创建了数据库连接,执行了update更新并获取了reserve锁。
pthreadA也打算执行delete操作(需要获取reserve锁),reserve锁被pthreadB占用了,无法正常执行。
pthreadB更新完成,进行commit提交的时候需要获取exclusive锁,此时pthreadA占用着数据库的shared锁,无法正常执行。
pthreadA和pthreadB就陷入了死锁的爱恨纠葛。
在上述例子程序中,pthreadA占用着shared锁才导致了死锁的存在,其实如果pthreadA释放了shared锁就不会出现这个问题了。
我们来讨论一下sqlite锁释放的时机:
1、事务结束的时候
2、有next或者step等涉及游标操作的时候,当查询彻底执行完成(游标释放)的时候。
3、如果没有用到事务的话,当一条完整的sql语句执行结束的时候。
4、数据库连接关闭的时候。
解决方法
其实解决死锁的问题也简单,最彻底安全的方式就是进行读写分离,最好是所有写操作交给一个任务线程去搞,读操作就没什么必要了。
如果不考虑性能的话,当然也可以在所有操作数据库的地方加上系统的读写锁或者互斥锁。
当然了,如果不想重整架构,又或者在一些类似界面程序的少量数据的操作中,没有使用到事务或者少量使用事务的话,就注意在一个事务中不要同时出现又读又写的情况。在查询中,有用到游标类的操作,在查询遍历的过程中不要进行修改、删除等操作。抛开事务和游标等操作,正常的sql语句并发执行,其实是不会出现死锁现象的。
更多推荐
所有评论(0)