转自:http://blog.csdn.net/rongyongfeikai2/article/details/41311193
记得以前设计评审时,想用sqlite数据库实现某个功能,被教导说应该用Postgresql数据库,因为Postgresql数据库是行锁,而sqlite的锁粒度太粗了。当时还没有什么感觉。
后来在另一个产品的群里面,经常看到其中的开发和测试说sqlite数据库死锁了。这才留了一下心。
最近又要使用它,于是拜读了下《sqlite权威指南》,里面赫赫一句话:sqlite处理并发读没有什么问题,但是如果你的应用需要并发写的话,那么sqlite就不适合你了。
它包括四种锁,共享锁(Shared lock)、预留锁(Reserved lock)和未决锁(Pending lock)、排他锁(Exclusive lock)
其中读操作,用的是Shared lock,所以并发的多个读数据库。如果有一个读操作存在,那么都不会允许写。
而写就比较麻烦,
1.它首先会申请一个预留锁(Reserved lock),在启用Reserved lock之后,已存在的读可以继续读,也可以有新的读请求。
2.然后,它会把需要更新的数据写到缓冲区中。
3.需要写到缓冲区的更新写完以后,就需要将更新刷到硬盘db了。这时,它会申请Pending lock,就不能再有新的Shared lock申请了,也就是阻止了新的读操作。但是已经存在的读操作还是可以继续读的。然后它就等待,直到没有读操作存在(即所有的读都已经结束)这个时候,它就会申请排他锁,此时不允许有其他锁的存在,然后进行commit,将缓冲区的数据写入db中。
书上给举了个例子:
B进行写操作,申请了预留锁;然后A进行读操作,申请了共享锁(有预留锁时,是允许读操作申请的);然后A又同时想进行写操作(未释放共享锁的情况),此时申请预留锁(因为已经有预留锁存在了)失败;B写完缓存,想commit时,申请了未决锁,但是无法从未决锁提升到排他锁(因为有共享锁存在)。此时,发生死锁,A和B都想等待对方释放锁。
对应一下自己的场景:
1.页面有多个读
2.后台会定时写
按照书上说的,写锁的时长大概是几毫秒。我写程序也尽量注意了。也许在极端情况下,在写时,恰好有读锁未释放,不过几毫秒内,概率不算很大。
另外,就算是这种极端情况未写成功,在下一个5分钟写时,也会把上一个5分钟未commit的给补救上去。从前台看来,就是数据会有一定时延。
另外一个隐含的需求:页面可能也需要进行更新数据的操作,这个写是有用户的某个动作触发的,那么在多用户的情况下,读写同时、写写同时的概率就会很大。对此,希望是采取规避的方式,在后台提供与此更新操作的脚本,而非在前台页面提供。
由此可见,sqlite作为一个嵌入式数据库,不太适合用在高并发的场景下;另外,以上都是理论,希望有时间阅读源码,能够彻底弄清楚一切。
讨论了下,为了规避风险,还是不用sqlite了。什么时候,sqlite才能把数据库级别的锁改为行锁?不过如果真的sqlite支持行锁,那么就违背它轻量、简单的初衷了。所以,这个终究是一个梦。