一、事务概念
事务就是一个原子性的事务查询,也就是说事务内的语句要么都执行成功,要么都执行失败。
开启事务样例, START TRANSACTION开启一个事务,要么使用COMMIT提交事务使数据持久保留,要么ROLLBACK撤销事务撤销所有修改。
START TRANSACTION;
UPDATE table1 set xx = 'xx' where x = 1;
UPDATE table2 set xx = 'xx' where x = 2;
COMMIT;
二、事务特性
单纯的事务概念是不够的,试想一下,如果上面语句在第三条语句执行的时候,数据库直接崩了,那不还是无法保证数据同时被更新吗!也即是说一个运行良好的事务处理系统必须经过严格的A(atomicity 原子性)C(consistency 一致性)I(isolation 隔离性)D( durability 持久性)测试的。
特性 | 说明 |
---|---|
原子性 | 一个事务是一个不可分割的最小单元,要么全成功,要么全失败 |
一致性 | 数据库总是从一个一致性转换到另一个一致性 |
隔离性 | 对其他事务是不可见的 |
持久性 | 事务一旦提交,数据持久保存到数据库中 |
三、事物的隔离级别
隔离级别 | 说明 |
---|---|
读未提交(READ UNCOMMITED) | 可以读取到未提交的数据(脏读),很少用到这个级别。 |
读已提交(READ COMMITED) | 一个事务从开始到提交之前,对其他事务都是不可见的(不可重复读),也就是两次执行同样的查询,可能得到不一样的结果。大多数数据库的默认级别是这个,但是MySQL不是。 |
可重复读(REPEATABLE READ) | 解决了脏读的问题,该级别保证了在同一事务中,多次读取同样的记录结果是一致的,但是该级别无法解决幻读问题。幻读就是指当某个事务在读取某个范围内的记录时,另外的一个事务新增或者删除了某个记录,会产生幻行。InnoDB通过多版本并发控制(MVCC)解决了幻读问题。该级别是数据的默认级别。 |
可串行化(SERIALIZABLE) | 强制事务串行执行,避免前面的幻读问题。简单来说,SERIALIZABLE会在读取每行数据上都加锁,这就可能导致大量超时或者锁争用,效率很低。很少用到这个级别 |
隔离级别 | 脏读 | 不可重复读 | 幻读 | 加锁读 |
---|---|---|---|---|
读未提交 | YES | YES | YES | NO |
读已提交 | NO | YES | YES | NO |
可重复读 | NO | NO | YES | NO |
可串行化 | NO | NO | NO | YES |
四、MySQL中事务
MySQL提供了两种事务型存储引擎:InnoDB和NDB Cluster;当然还有一些其他第三方的存储引擎也支持事务。我们常用的存储引擎是InnoDB。
MySQL默认采用自动提交的(AUTOCOMMIT)模式,如果不显示的开启一个事务,则每个查询都被 当做一个事务执行提交操作。
可以通过设置来改变自动提交:
#1或ON表示启用,0或OFF标识禁用
SET AUTOCOMMIT = 1;
修改事务的隔离级别命令,InnoDB支持所有级别的隔离级别:
SET SESSION TRANSACTION IOSLATION LEVEL READ COMMITED;
五、说一下MVCC(多版本并发控制)
概念: 行级锁的一个变种,但是在很多情况下避免了加锁操作,因此开销更低。费阻塞的读操作,写操作也只是锁定必要的行。
实现原理:通过保存数据的在某个时间点的快照来实现。不管执行多长时间,每个数据看到的事务都是一致的。根据事务开始时间不同,每个事务对同一张表,同一时刻看到的数据可能是不一样的。
InnoDB的MVCC: 通过在每行记录后面保存两个隐藏列来实现。一个列保存的创建时间,一个保存行的过期时间。
当然存储的不是实际的时间值,而是系统的版本号。每开始一个新的事物,系统的版本号会自动递增。事物开始时候的版本号回作为事务的版本号,用来和查询到的每行记录比较。
举个例子:可重复读级别下的MVCC具体如何操作的。
SELECT
InnoDB会根据以下两个条件检查每行记录:
1、InnoDB只查找版本早于当前事务版本的数据行(行版本号小于等于事务版本号),这样就可以保证事务读取的行要么是之前已经存在的,要么是事务自身插入的或者修改过的。
2、行的删除版本要么未定义,要么大于当前事务版本号。这样可以保证事务读取的行在事务之前未被删除。
还记得前面幻读问题吗?这样是不是就解决了?
INSERT
InnoDB为新插入的每一行保存当前系统版本号作为行版本号。
DELETE
InnoDB为删除的每一行保存当前系统版本号作为删除标识。
UPDATE
InnoDB为插入一条新纪录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为删除标识
MVCC添加两个额外的隐藏列,这样的设计性能会很好,但是会产生额外存储的开销。另外MVCC只在可重复读和读已提交两个级别下有效。