sqlite 事务锁

Sqlite采用的粗粒度锁。当一个链接要写数据库的时候,所有其他的链接都被锁住,直到写事件结束。Sqlite有一个加锁表,用来帮助不同的写数据库都能够在最后一刻加锁,以保证最大的并发性。

锁的状态:

  • 未加锁(unlocked):链接没有访问数据库的状态
  • 共享锁(shared):读取数据的时候必须先获取共享锁,多个链接可以同时保持拥有共享锁,只有所有的共享锁都被释放,才允许其他链接写入数据库。
  • 预留锁(reserved):当链接想写入数据库的时候,必须先获取预留锁。一个数据库同一时刻只能拥有一个预留锁。预留锁既不阻止当前拥有的共享锁读取数据库,也不阻止获取新的共享锁。获取预留锁之后就可以在缓冲区中进行数据读写,并不实际写到磁盘。
  • 未决锁(pending):当一个链接获取预留锁,并且想提交缓冲区的读写内容的时候,需要将锁升级为未决锁。获取未决锁之后其他链接就不能再获取新的共享锁,当是已经获取到共享锁的链接可以继续读数据库。直到所有共享锁都被释放。
  • 排它锁(exclusive):当一个链接获取未决锁并且所有共享锁已经被释放的时候,未决锁升级为排他锁。此时就可以自由的对数据库就行修改,所有缓冲区的修改都将被写入到数据库文件中。

所以写SQlite的流程:预留锁(缓冲区操作)=》未决锁(准备提交,阻止新共享锁)=》排它锁(提交到文件)

执行顺序链接A链接B
1begin;
2begin
3insert into foo values(‘x’);
4select * from foo;
5commit;
6SQL error:database is locked
7insert into foo values(‘x’);
8SQL error:database is locked

通过上述事件我们来分析一下为什么A,B都会死锁。
步骤3:insert 链接B获取预留锁
步骤4:select 链接A获取共享锁
步骤5:commit,链接B提升预留锁为未决锁,等待A的共享锁退出,然后升级为排他锁,开始写数据库。
步骤7:insert 链接A获取预留锁,但是由于此时B占有未决锁,需要等待B的未决锁退出,升级为排它锁,开始写数据库。
此时我们发现步骤5中B在等待A,步骤7中A在等待B,从而造成AB相互等待死锁。同时由于步骤5中锁升级为未决锁,会阻止所有的共享锁。所以以后的新连接在想读数据库都会失败。
(预留锁,未决锁,排它锁全程同一时刻只能存在一个)

事务的类型

  • deferred(默认值):延迟事务类型,直到使用的时候才获取锁。所以对该类型的事务而言,begin语句不做任何事情。它从未决状态开始。
  • immediate:该事务在执行begin的时候获取预留锁。如果预留锁已经被其他链接占有,则提示SQLITE_BUSY。从而保证没有其他链接可以启动begin immediate或者begin exclusive。如果用户获取预留锁之后,commit的时候提示SQLITE_BUSY,那么说明还有其他链接读事务没有完成,需要等到所有的读事件完成之后再次commit
  • exclusive:排他事务类型,begin的时候获取排他锁。一旦成功此时阻止其他所有链接的读写操作。

结论:
如果在性能允许的前提下,可以约定使用Begin immediate或者begin exclusive 来避免死锁。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值