Mysql事务原理介绍

时间:2024-07-19 08:33:26

事务

一个事务会涉及到大量的cpu计算和IO操作,这些操作被打包成一个执行单元,要么同时都完成,要么同时都不完成.

事务是一组原子性的sql命令或者说是一个独立的工作单元,如果数据库引擎能够成功的对数据库应用该组的全部sql语句,那么就执行该组命令

如果其中有任何一条语句因为崩溃或者其它原因无法执行,那么该组中所有的sql语句都不会执行

如果没有显示启动事务,数据库会根据autocommit的值.默认每条sql操作都会自动提交.

原子性

一个事务中的所有操作,要么都完成,要么都不执行.对于一个事务来说,不可能只执行其中的一部分.

一致性

数据库总是从一个一致性的状态转换到另外一个一致性状态.

隔离性

一个事务所做的修改在最终提交以前,对其它事务是不可见的.多个事务之间的操作相互不影响. 每降低一个事务隔离级别都能提高数据库的并发

1.读未提交          其它事务未提交就可以读
       2.读已提交          其它事务只有提交了才能读
       3.可重复读          只管自己启动事务时候的状态,不受其它事务的影响(mysql默认)
       4.事务串行          按照顺序提交事务保证了数据的安全性,但无法实现并发

持久性

一旦一个事务已经提交了,就算服务器崩溃,仍然需要在下次启动的时候自动恢复.

结合事务日志完成:

事务日志写入磁盘的时候是顺序IO,写数据文件的时候是随机IO

一旦事务提交了,必须立即执行一个IO操作,确保此事务立即写入磁盘.

事务的状态

活动

部分提交

失败

中止

提交

事务一旦成功提交,便无法再撤销.

事务并发访问控制方式:

时间

多版本和快照隔离(mvcc)

MVCC是行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此开销更低,虽然实现机制有所不同,但大都实现了非阻塞的读操作,写操作也只锁定必要的行.

MVCC的实现是通过保存数据在某个时间点的快照来实现的,也就是说,不管需要执行多长时间,只要事务开始时间相同,每个事务看到的数据都是一致的,事务开始的时间不同时,每个事务对同一张表,同一时刻看到的数据可能是不一样的(因为不同的时间点可能数据就已经产生了不同的快照版本,而每个事务在默认的RR隔离级别下只能看到事务开始时的数据快照)

innodd的mvcc是通过在每行记录后面保存两个隐藏列来实现的.这两个列,一个保存了行的创建时间.一个保存了行的过期时间(删除时间).列里面存储的并不是实际的时间值.而是系统版本号.每开启一个新的事务,系统版本号都会自动递增.

一个事务在开启的时刻的系统版本号作为当前事务的版本号,用来和查询到的每行记录的版本号做对比.mvcc具体操作如下:

select:

A:过滤创建版本

innodb只查找版本早于当前事务版本的数据行(也就是,行的系统版本号小于或等于事务的系统版本号),这样可以确保事务读取的行,要么是在事务开始之前已经存在,要么是事务自身插入或修改的数据.

B: 过滤删除版本

行的删除版本要么未定义,要么大于当前事务版本号,这可以确保事务读取到的行,在事务开始之前未被删除(即,这样做的目的是为了事务不会读取到被真正删除的行,删除版本号小于当前事务版本号的表示操作删除记录的事务已经提交--数据已经被删除,删除版本号大于当前事务版本号的表示这个事务是在当前事务之后开始的--当前事务开始时这些记录是还存在的,根据事务的隔离性,一致性要求,之后开始的事务操作的记录并提交,对当前事务不可见,所以还需要当前事务能够查询这些记录--只能够查询,不能够修改和删除)

只有满足以上两个条件的才可以返回作为查询结果

insert:

innodb为新插入的每一行保存当前系统版本号作为行版本号

delete:

innodb为删除的每一行保存当前系统版本号作为行删除标识

update:

innodb为插入一行新记录,保存当前系统版本号作为行版本号

修改表中原来行把当前系统版本号更新到原来的行作为行删除版本号

保存这两个额外的系统版本号,使大多数读操作都可以不用加锁,这样设计使得读数据操作很简单,性能很好,并且也能保证只会读取到符合标准的行,不足之处是每行记录都需要额外的存储空间,需要做更多的行检查工作,以及一些额外的维护工作。

MVCC只在repeatable-read和read-committed两个隔离级别下才工作,其他两个隔离级别都和MVCC不兼容,因为read uncommitted总是读取最新的数据行,而不是符合当前事务版本的数据行,而serializeble则会对所有读取的行都加锁。

另外要注意:MVCC在RR和RC隔离级别下的区别,在RR隔离级别下,一个事务只能读取到事务开始的那个时刻的数据快照,即,别的事务修改并提交的数据在自身没有提交之前一般读取不到(加for update语句的select除外,因为这个语句要对数据加X锁必须读取最新的数据快照),在RC隔离级别下,事务总是读取数据行的最新快照,即会产生不可重复读的问题。

死锁

两个或者多个事务在同一资源上相互占用,并请求锁定对方占用的资源的状态

事务提交

自动提交

mysql默认采用自动提交(AUTOCOMMIT)模式.如果没有显示的开始一个事务,那么每条sql语句都会被当作一个事务执行提交的操作

当AUTOCOMMIT=0的时候所有的sql语句都是在一个事务中,直到显示的执行COMMIT和ROLLBACK回滚该事务结束.同时又开始了另外一个新的事务.

显式提交

开启事务

START TRANSACTION

结束事务

(1)  COMMIT:         提交

(2)  ROLLBACK:    回滚

注意:一旦一个事务成功提交,将无法回滚.一个事务要想回滚,只能在没有提交成功之前执行回滚

只有事务型存储引擎中的DML语句方能支持此类操作

建议:显式请求和提交事务,而不要使用“自动提交”功能 set autocommit={1|0}

事务支持保存点 savepoint:

SAVEPOINT identifier

ROLLBACK [WORK] TO [SAVEPOINT]  identifier

RELEASE SAVEPOINT identifier

事务日志

事务要保证ACID的完整性必须依靠事务日志做跟踪,每一个操作在真正写入数据数据库之前,先写入到日志文件中
     如要删除一行数据会先在日志文件中将此行标记为删除,但是数据库中的数据文件并没有发生变化.

只有在(包含多个sql语句)整个事务提交后,再把整个事务中的sql语句批量同步到磁盘上的数据库文件

在事务引擎上的每一次写操作都需要执行两遍:
        1.先写入日志文件中
                写入日志文件中的仅仅是操作过程,而不是操作数据本身,所以速度比写数据库文件速度要快很多.
        2.然后再写入数据库文件中

写入数据库文件的操作是重做事务日志中已提交的事务操作的记录.

日志组
         一般不止设置一个日志文件,一个文件写满之后使用另外一个日志文件提高服务器效率.

日志文件的日志同步到磁盘后空间会自动释放,单个日志文件不宜设置过大     如果日志文件过大mysql进程在把日志同步到数据文件的时候可能会崩溃

事务日志的用途

事务日志可以帮助提高事务的效率,使用事务日志,存储引擎在修改表的数据的时候只需要修改其内存拷贝,再把该行为记录到持久在磁盘的事务日志中.而不用每次都将修改的数据本身持久到磁盘.事务日志采用的是追加方式,因此写日志的操作是磁盘上一小块区域的顺序IO,而不像随机IO需要磁盘在多个地方移动.所以采用事务日志的方式相对来说要快的多,事务日志持久后,内存中的修改在后台慢慢的刷回磁盘.期间如果系统发生崩溃,存储引擎在重启的时候依靠事务日志自动恢复这部分被修改数据