事务:
事务定义了一株sql命令的边界,这组命令或者作为一个整体被全部执行,或者都不执行,称为原子性原则。 事务的本质就是都执行,或者都不执行,而且是同时性的。
最具有说服力的是银行转账过程。(详情请百度orgoogle)。
事务的范围:
事务的三个控制命令:begin 、 commit 、 rollback 。begin开始事务,如果没有commit,则几乎所有的操作都可以取消。commit提交事务后,执行所有的操作。rollback还原begin后的所有的操作。
当开始事务后,但是没提交:
此时,可以看到,表foo里没有任何数据。
那么rollback之后呢?看看:
可以看到,rollback之后,数据没有发生任何变化,相对于事务开始前。
再看commit的功能:
值得注意的是,在sqlite中,,每句sql语言自成事务,可自动提交。其可在遇到错误的命令的时候自动回滚。即隐式事务。而且sqlite支持savepoint和release命令。
创建和启动savepoint的命令的方法;
savepoint justincase ;
rollback [ transaction ] to justincase ;
再看:
这里就回滚到了预期的插入行了。
冲突解决:
违反约束可鞥导致事务结束。
sqlite的五种可能的冲入解决方案:replace,ignore,fail,abort,rollback。容错范围和敏感度依次增加,越来越严格。
replace:违反约束时,只是利用新的记录代替违反约束的记录,删除原来违反约束的记录,sql继续执行,不报错。不会触发触发器。
ignore:继续执行,违反约束的行的记录不变,其前后都被修改。不报错。
fail :直接在错误的记录处终止,不恢复违反约束之前的修改的记录,不回滚。命令终止。
abort :约束违法时,恢复所有的命令做出的改变。是默认的解决办法,sql标准定义的行为。此时,这个冲突解决策略是最昂贵的,需要额外的工作。
rollback: 违反约束时,sqlite执行回滚,终止当前的命令和整个事务。当前命令和之前的命令的改变都将会回滚。最严格的冲突解决办法。
冲突解决办法在sql命令中指定,也可以在表和索引的执行中执行。也就是说,冲突解决办法可以在insert、update、create table、create index中指定。
冲突解决办法在insert或者update中的语法形式:
insert or resolution into table (column_list ) values (value_list ) ;
update or resolution into table set (value_list ) where predicate ; 注意参考其他数据库的merge和upset。
在表的定义中为单个字段指定冲突解决办法:
无法提交,是因为事务已经自动提前终止了。注意rollback的特点。
数据库锁:
sqlite中,锁与事务关系密切。它采用粗粒度的锁。当一个连接
当一个连接写数据库,其他的连接被锁住,直到写事务完成。
采用锁逐步提升技术。sqlite的五种锁状态:未加锁(unlocked)、共享锁(shared)、预留锁(reserved)、未决锁(pending)、排他锁(exclusive)。一个连接一种状态,一种锁。
最初未加锁状态,连接没访问数据库;当连接数据库时,或者事务已经begin时,还是未加锁状态。当从数据库中读数据时,进入共享状态。多个连接可以保持多个共享锁,即多个连接可从同一个数据库中读数据。如果共享锁至少有一个没释放,那么就不能写数据。
一个连接写数据的时候,先申请预留锁,一个数据库只能由一个预留锁,此锁可与共享锁共存。预留锁不阻止其他连接对数据库的读和其他连接获得对数据库新的共享锁。这时候,预留锁状态时,开始进行写操作,实在缓冲区进行的 , 并没写入到磁盘 ; 而当连接提交事务时,预留锁上升为排他锁。 在提升预留锁为排他锁的过程中,要先将预留锁提升为未决锁,获得未决锁后,其他连接不能再获得新的共享锁,但已有的共享锁还是可以连接并且读取数据库的,同时在共享锁读取数据库的过程中,未决锁一直在等待共享锁完成取并且释放共享锁。 当所有的共享锁被释放完毕后,拥有未决锁的连接就可将其所提升为排他锁,此时对数据库的修改开始,所有以前缓存的修改将会被写到数据库文件中。
死锁:*******************
死锁的情况比较少见,可以通过正确的事务类型来解决。先来了解下死锁产生的一种情况:
执行顺序 |
连接A | 连接B | ||
1 | begin; | |||
2 | begin; | |||
3 | insert into foo values (xxx ); |
|||
4 | select * from foo; | 5 | commit; | |
6 | error:db id locked | |||
7 | insert into foo values (yyy); |
8 | error:db is locked; |
两连接都结束于死锁。B连接先写数据库,产生了未决锁,阻止其他连接的共享锁。这时A开始写数据库,但是无法获得提升共享锁为预留锁。其他的连接这时候都被拒绝了,既不能读,更不能写。此时,A、B各自的锁都不放弃控制,将其他的操作阻止在数据库外了,其他的进程不能操作数据库了。
两个连接最终都想写数据库,但他们最终没放弃自己原来的锁,共享锁导致错误。如果两个连接以begin immediate / exclusive 开始,就无错。