事务
事务就是一组SQL查询,作为一个独立的工作单元。应用时,数据库执行该组查询,如果任何一条语句失败或者无法执行,那么所有的语句都不会执行。也就是说,事务内的语句,要么全部执行成功,要么全部执行失败。
以银行的转账应用来举例:
1,检查余额是否能满足转账金额。
2,从余额中扣除转账金额。
3,给目标帐号余额增加转账金额
上面三个步骤必须在一个事务中,任何一个步骤失败,就必须回滚所有的步骤,不然...就可以刷钱了。这里就不举例事务的语句了,也不描述上述步骤的必要性,请自行逻辑脑补。
一个运行良好的事务处理系统,必须具备以下四个标准特征(ACID特性)。
原子性:一个事务必须被视为一个不可分割的最小的工作单元。
一致性:数据库总是从一个一致性的状态转换到另外一个一致性的状态。
隔离性:通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可见的。
持久性:一旦事务提交,则其所做的修改就会永久保存到数据库中。
就像锁粒度的升级会增加系统开销一样,这种事务处理过程中的额外的安全性,也会需要数据库系统做更多的额外工作。MySQL的存储引擎架构可以根据业务需要来选择存储引擎,一个非事务型的存储引擎,可以提供更高的性能,这都是可以自主选择的,是其优势。
隔离级别
隔离比较复杂。在SQL标准中定义了四种隔离级别,每一种级别都规定了一个事物中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的。较低级别的隔离通常可以执行更高的并发,系统的开销也更低。(每种存储引擎实现的隔离级别不尽相同,请根据自己的存储引擎查阅相关手册)
以下简单介绍一下四种隔离级别:
READ UNCOMMITTED (未提交读)
在READ UNCOMMITTED 级别,事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,被称为脏读(Dirty Read)。这个级别会导致很多问题,性能上虽然优势却也不太多,但却缺乏其他级别的很多好处,除非真的非常必要,在实际应用中一般很少使用。
READ COMMITTED (提交读)
大多数数据库系统的默认隔离级别都是READ COMMITTED (但MySQL不是)。它满足隔离性里的一个简单定义:一个事务从开始直到提交之前,所作的任何修改对其他事务都是不可见的。这个级别有时候也叫做不可重复读,因为两次执行同样的查询,可能会得到不一样的结果。
REPEATABLE READ (可重复读)
它解决了脏读的问题,该级别保证了在同一个事务中多次读取同样记录的结果是一致的。但是理论上,可重复读隔离级别还是无法解决另外一个幻读(Phantom Read)问题。幻读指的是,当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行(Phantom Row)。InnoDB和XtraDB存储引擎通过多版本并发控制(MVCC, Multiversion Concurrency Control)解决了幻读问题。
可重复读是MySQL的默认事务隔离级别。
SERIALIZABLE (可串行化)
这是最高隔离级别。它通过强制事务串行执行,避免了前面说的幻读的问题。简单来说,它会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题。很少用到该隔离,只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑采用该级别。
(我额外加了点内容上去)
死锁
死锁是指多个事务在同一资源上互相占用,并请求锁定对方占用的资源,从而导致恶性循环的现象。当多个事务试图以不同的顺序来锁定资源时,就可能会产生死锁。多个事务同时锁定同一个资源时,也会产生死锁。
数据库系统实现了各种死锁检测和死锁超时机制。越复杂的系统,比如InnoDB存储引擎,越能检测到死锁的循环依赖,并立即返回一个错误。InnoDB目前处理死锁的方法是,将持有最少行级排他锁的事务进行回滚。
死锁的产生有双重原因:有些是因为真正的数据冲突,这很难避免,但有些则完全是由于存储引擎的实现方式导致的。
MySQL中的事务
mysql提供了两种事务型的存储引擎:InnoDB和NDB Cluster。
首先mysql默认采用自动提交:如果不是显式的开始一个事务,则每个查询都被默认当作一个事务执行提交操作。
在当前连接中,可以通过设置AUTOCOMMIT 变量来启用或者禁用自动提交模式,1或者ON 表示启用,0或者OFF 表示禁用。例如:
mysql> SHOW VARIABLES LIKE 'AUTOCOMMIT';
mysql> SET AUTOCOMMIT = 1;
当AUTOCOMMIT=0 时,也就是自动提交关闭时,所有的查询都是在一个事务中,直到显式地执行COMMIT 提交或者ROLLBACK 回滚,该事务结束,同时又开始了另一个新事务。
修改AUTOCOMMIT 对非事务型的表,比如MyISAM 或者内存表,不会有任何影响。对于这样的表来说,没有COMMIT 或者ROLLBACK 的概念,也可以说是一直处于AUTOCOMMIT 启用的模式。
另外还有一些命令,在执行之前会强制执行COMMIT 提交当前的活动事务。典型的例子,在数据定义语言(DDL)中,如果是会导致大量数据改变的操作,比如ALTER TABLE,就是如此。当然除此之外还有其他类似LOCK TABLES 语句也会导致同样结果。
MySQL可以通过执行以下命令来设置隔离级别:
mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
新的隔离级别辉仔下一个事务开始的时候生效。可以在配置文件中设置整个数据库的隔离级别,也可以只改变当前会话的隔离级别。
MySQL能够识别所有的4个ANSI隔离级别,InnoDB引擎也支持所有的隔离级别。
不要再事务中混合使用存储引擎
如果事务需要回滚,非事务型的表上的变更无法撤销,这种情况很难修复,这种时候数据库甚至不会报错和提醒。
隐式和显式锁定
InnoDB采用的是两阶段锁定协议。在事务执行过程中,随时都可以执行锁定,锁只有在执行COMMIT 或者ROLLBACK 的时候才会释放。前面描述的都是隐式锁,InnoDB会根据隔离级别在需要的时候自动加锁。