简单来说,事务就是要保证一组数据库操作,要么全部成功,要么全部失败。在MySQL中,事务支持是在引擎层实现的,但并不是所有的引擎都支持事务,如MyISAM引擎就不支持事务,这也是MyISAM被InnoDB取代的重要原因之一。
提到事务,肯定会想到ACID(Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔离性、持久性)
当数据库上有多个事务同时执行的时候,就可能出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题,为了解决这些问题,就有了“隔离级别”的概念。隔离得越严实,效率就会越低,SQL标准的事务隔离级别包括:读未提交(read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(serializable)。
读未提交:一个事务还没有提交时,它做的变更就能被别的事务看到;
读提交:一个事务提交之后,它做的变更才会被其他事务看到;
可重复读:一个事务在执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的,在此级别下,未提交变更对其他事务也是不可见的;
串行化:对于同一行记录,写会加写锁,读会加读锁,当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
模拟一个场景,来对比一下四种隔离级别对SQL执行结果的影响:
建一张表:create table T(c int) engine=InnoDB;
插入一条数据:insert into T(c) values (1);
参照下图中的时间顺序来执行两个事务,四种隔离级别下V1、V2、V3的值分别是多少?
隔离级别 | V1 | V2 | V3 |
读未提交 | 2 | 2 | 2 |
读提交 | 1 | 2 | 2 |
可重复读 | 1 | 1 | 2 |
串行化 | 1 | 1 | 2 |
在实现上,数据库里面会创建一个视图,访问的时候以视图的逻辑结果为准。在“可重复读”级别下,这个视图是在事务启动时创建的,整个事务存在期间都用这个视图;在“读提交”级别下,这个视图是在每个SQL语句开始执行的时候创建的;在“读未提交”级别下,直接返回记录上的最新值,没有视图的概念,在“串行化”级别下直接用加锁的方式来避免并行访问。
MySQL的默认隔离级别为:可重复读;Oracle的默认隔离级别是:读提交。
可通过执行:show variables like 'transaction_isolation'; 来查看当前的隔离级别。
隔离级别的实现是通过回滚日志来实现的,回滚日志类似下图:
同一条记录在系统中可以存在多个版本,这就是数据库的多版本并发控制(MVCC)。系统会判断当没有事务再需要用到这些回滚日志的时候,也就是系统里没有比这个回滚日志更早的read-view的时候,回滚日志就会被删除。因此,建议尽量不要使用长事务。
显式启动事务的方式:begin或者start transaction,配套的提交语句是commit,回滚语句是rollback。
一般建议将自动提交事务开启,即执行set autocommit=1,当需要使用事务的时候,通过上述显式语句来启动事务。
查找持续时间超过60秒的事务:
select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60;