1. 什么是数据库事务
1.1 数据库事务是指作为单个逻辑工作单元执行的一系列操作(SQL语句)。这些操作要么全部执行,要么全部不执行。
1.2 通过ACID实现数据库事务模型
1.2.1 原子性(Atomicity):事务是数据库的逻辑工作单位,它对数据库的修改要么全部执行,要么全部不执行。
1.2.2 一致性(Consistemcy):事务执行前后,数据库的状态都满足所有的完整性约束。
1.2.3 隔离性(Isolation):并发执行的事务是隔离的,保证多个事务互不影响。如果有两个事务,运行在相同的时间内,执行相同的功能,事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。这种属性有时称为串行化,为了防止事务操作间的混淆,必须串行化或序列化请求,使得在同一时间仅有一个请求用于同一数据。通过设置数据库的隔离级别,可以达到不同的隔离效果。
1.2.4 持久性(Durability):一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。
1.3 事务运行的三种模式
1.3.1 自动提交事务:默认事务管理模式。如果一个语句成功地完成,则提交该语句;如果遇到错误,则回滚该语句。
1.3.2 显式事务:以BEGIN TRANSACTION显式开始,以COMMIT或ROLLBACK显式结束。
1.3.3 隐性事务:当连接以此模式进行操作时,sql将在提交或回滚当前事务后自动启动新事务。无须描述事务的开始,只需提交或回滚每个事务。它生成连续的事务链。
2. 数据库事务与事务日志
2.1 以mysql的innodb的事务为例,通过事务日志实现ACID特性。事务日志遵循WAL(Write-Ahead Logging 预写日志方式)协议。
2.1.1 事务日志包括(参考:http://blog.sina.com.cn/s/blog_4e46604d0101gyzq.html)
重做日志redo:在Innodb存储引擎中,事务日志是通过redo和innodb的存储引擎日志缓冲(Innodb log buffer)来实现的,当开始一个事务的时候,会记录该事务的lsn(log sequence number)号; 当事务执行时,会往InnoDB存储引擎的日志的日志缓存里面插入事务日志;当事务提交时,必须将存储引擎的日志缓冲写入磁盘(通过innodb_flush_log_at_trx_commit来控制),也就是写数据前,需要先写日志。这种方式称为“预写日志方式”,innodb通过此方式来保证事务的完整性。也就意味着磁盘上存储的数据页和内存缓冲池上面的页是不同步的,是先写入redo log,然后写入data file,因此是一种异步的方式。通过 show engine innodb status\G 来观察之间的差距。记录文件是ib_logfile0 ib_logfile1
回滚日志undo:用于事务的回滚,undo的记录正好与redo的相反,insert变成delete,update变成相反的update,redo放在redo file里面。而undo放在一个内部的一个特殊segment上面,存储与共享表空间内(ibdata1或者ibdata2中)。
Innodb_log_buffer_size 重做日志缓存大小
Innodb_log_file_size redo log文件大小 文件越大 数据恢复的时间越长
Innodb_log_file_group redo log文件数量 默认是2个 ib_logfile0 ib_logfile1
2.1.2 事务日志恢复事务:一般情况下,mysql在崩溃之后,重启服务,innodb通过回滚日志undo将所有已完成并写入磁盘的未完成事务进行rollback,然后redo中的事务全部重新执行一遍即可恢复数据,但是随着redo的量增加,每次从redo的第一条开始恢复就会浪费长的时间,所以引入了checkpoint机制(如果在某个时间点,脏页的数据被刷新到了磁盘,系统就把这个刷新的时间点记录到redo log的结尾位置,在进行恢复数据的时候,checkpoint时间点之前的数据就不需要进行恢复了,可以缩短时间)
2.1.3 Dirty page:脏页
一般业务运行过程中,当业务需要对某张的某行数据进行修改的时候,innodb会先将该数据从磁盘读取到缓存中去,然后在缓存中对这条数据进行修改,这样缓存中的数据就和磁盘的数据不一致了,这个时候缓存中的数据就称为dirty page,只有当脏页统一刷新到磁盘中才会是clean page
3. 数据库事务是如何实现并发的
4. 数据库事务隔离级别与锁的关系
mysql innodb事务死锁:
事务A需要先查询数据1,然后修改数据2;
事务B需要先查询数据2,然后修改数据1;
此时2个事务并发执行,由于mysql innodb的默认隔离级别是可重复读,读锁、写锁只能在事务结束才会释放。
事务A中需要等待事务B释放数据2的共享锁,才能执行对数据2添加排他锁;事务B需要等待事务A释放数据1的共享锁,才能执行对数据1添加排他锁。这样会就出现事务死锁
这种死锁在oracle中就不会出现,因为oracle的默认隔离级别是读已提交,共享锁在执行完查询之后就会释放,不会导致排他锁无法添加的问题。