赞
踩
本篇我们讲解有关事务处理的相关知识点,也意味着我们从此篇开始,就进入了MySQL的高级篇章,其中事务的并发控制是一个内容庞大的话题,有大量的理论文献对其进行详细的论述,本篇只简要地讨论MySQL如何控制并发读写
事务与锁机制是密不可分的,事务的隔离性通过事务的隔离级别来定义,并用锁机制来保证写操作的隔离性,锁机制将在下篇博客中整理
字面意思理解:事务,就是指一件具体、完整的事情
在数据库中,事务就是指一组SQL语句,这一组SQL语句是相互依赖的,作为一个不可分割的整体存在。
那么既然是一个整体,这组SQL语句的执行情况就只有全部成功,和全部都不成功两种情况,而不会存在部分成功或失败的情况。
进而可以说明,如果这组SQL语句的其中一条语句执行失败,那么将会发生回滚操作,将之前执行成功的语句以及它们所进行的操作、影响的数据都撤销,返回事务开始之前的状态。即单元中的所有sql语句都执行成功的话,那么该事务也就被顺利执行。
关于事务处理需要知道的几个术语
事务(transaction):指一组SQL语句;
回退(rollback):指撤销指定SQL语句的过程;
提交(commit):指将未存储的SQL语句结果写入数据库表;
保留点(savepoint):指事务处理中设置的临时占位符(place-holder),你可以对它发布回退(与回退整个事务处理不同)。
必须通过严格的ACID测试才能被称为事务,否则空谈事务的概念是不够的,所以事务最重要的四个特性通常被称为 ACID
特性
A - Atomicity 原子性
: 一个事务是一个不可分割的最小单位,事务中的所有操作要么全部成功,要么全部失败,没有中间状态。原子性主要是通过事务日志中的回滚日志(undo log)
来实现的,当事务对数据库进行修改时,InnoDB 会根据操作生成相反操作的 undo log,比如说对 insert 操作,会生成 delete 记录,如果事务执行失败或者调用了 rollback,就会根据 undo log 的内容恢复到执行之前的状态。
C - Consistency 一致性
: 事务执行之前和执行之后数据都是合法的一致性状态,即使发生了异常,也不会因为异常引而破坏数据库的完整性约束,比如唯一性约束等。
I - Isolation 隔离性
: 每个事务是彼此独立的,不会受到其他事务的执行影响,事务在提交之前对其他事务不可见。隔离性通过事务的隔离级别来定义,并用锁机制来保证写操作的隔离性,用 MVCC 来保证读操作的隔离性,将在下篇锁机制详细介绍。
D - Durability 持久性
: 事务提交之后对数据的修改是持久性的,即使数据库宕机也不会丢失,通过事务日志中的重做日志(redo log)
来保证。事务修改之前,会先把变更信息预写到 redo log 中,如果数据库宕机,恢复后会读取 redo log 中的记录来恢复数据。
并非所有存储引擎都支持事务处理,而MySQL支持几种基本的数据库引擎,MyISAM和InnoDB是两种最常用的引擎,但前者不支持明确的事务处理。
给出一个常用来解释事务的例子方便理解(银行应用):
假设一个银行的数据库有两张表:支票(checking)表和储蓄(saving)表。现在要从用户ChenQi的支票账户转移200美元到他的储蓄账户,那么需要至少三个步骤
上述三个步骤的操作必须打包在一个事务中,任何一个步骤失败,则必须回滚所有步骤。如果不采用事务处理,在第三步时发生错误,钱并没有增加进储蓄账户,而之前的操作又不能撤销,那ChenQi就白白损失了200美元,或者在第二步和第三步中间的时候,另外一个进程要删除支票账户所有余额,那么结果可能就是银行在不知道这个逻辑的情况下白白给了ChenQi 200美元。
按照事务的启动与执行方式,可以将事务分为3类:
在默认情况下,一条sql语句就是一个事务,其实说的就是事务的自动提交模式,因为它是数据库引擎管理事务时默认的模式。
MySQL使用下面的语句来标识一条事务的开始
输入:
START TRANSACTION
MySQL的ROLLBACK命令用来回退(撤销)MySQL语句
输入:
SELECT * FROM checking;
START TRANSACTION;
DELETE FROM checking;
SELECT * FROM checking;
ROLLBACK;
SELECT * FROM checking;
分析:
事务处理用来回退INSERT、UPDATE、DELETE语句,但不能回退SELECT语句,实际上回退SELECT语句也没有什么意义。也不能回退CREATE和DROP语句,即使执行了,这两条操作并不会被撤销
一般的MySQL语句都是直接针对数据库表执行和编写的。实际上这就是隐式提交(隐式事务),即提交(写或保存)操作是自动进行的。
但是在事务处理的局域快中,提交并不会隐式的进行,需要进行明确的提交,要使用COMMIT语句
输入:
START TRANSACTION;
DELETE FROM checking WHERE number = 200;
INSERT INTO saving values(200);
COMMIT;
分析:
实际上,当COMMIT和ROLLBACK语句执行后,当前事务就会自动关闭。
简单的ROLLBACK和COMMIT语句可以撤销或写入整个事务处理。但是,只是简单的事务处理可以如此操作,复杂的事务处理可能需要部分提交或回退。
为了支持部分回退的操作,必须能在事务处理块中合适的位置防止一个占位符,如此操作,若需要回退,可以回退到某个已记录的占位符的位置,这些占位符就称为保留点
例如,如果上面的插入200美元操作发生错误,我们只需要返回到删除checking表中记录后,插入操作之前即可,不需要整个回退。
输入:
SAVEPOINT delete1;
ROLLBACK TO delete1;
每个保留点都要取表示它的唯一名字,以便在回退时,MySQL知道要回退到何处。
保留点在事务处理完成后(执行一条ROLLBACK或COMMIT)后会自动释放,自MySQL5以来,也可以用RELEASE SAVEPOINT
来手动释放
之前提到,MySQL的事务默认提交方式是自动提交。换句话说:任何时候你执行一条MySQL语句,对于数据库或表所做的更改,成功则立即生效,失败就不会更改,还会给出错误信息
为指定MySQL不自动提交,需要使用以下语句
输入:
SET autocommit = 0;
分析:
autocommit标志决定是否自动提交更改操作,不管有没有COMMIT语句,设置其为0则指示MySQL不自动提交更改操作(直到autocommit被设置为真位置)
无论何时,只要有多个查询需要在同一时刻修改数据,都会产生并发控制的问题,MySQL主要是在两个层面的并发控制:服务器层和存储引擎层。
因为某一刻不可能总只有一个事务在运行,可能出现A在操作checking表中的数据,B也同样在操作checking表,那么就会出现并发问题,对于同时运行的多个事务,当这些事务访问数据库中相同的数据时,如果没有采用必要的隔离机制,就会发生以下各种并发问题。
所以,为了避免以上出现的各种并发问题,我们就必然要采取一些手段。mysql数据库系统提供了四种事务的隔离级别,用来隔离并发运行各个事务,使得它们相互不受影响,这就是数据库事务的隔离性。
一个事务与其他事务隔离的程度称为隔离级别。数据库规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性就越差。
READ UNCOMMITTED(未提交读)
在READ UNCOMMITTED级别,事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读(Dirty Read)。这个级别会导致很多问题,从性能上来说,READ UNCOMMITTED不会比其他的级别好太多,但却缺乏其他级别的很多好处,除非真的有非常必要的理由,在实际应用中一般很少使用。
READ COMMITTED(提交读)
大多数数据库系统的默认隔离级别都是READ COMMITTED (但MySQL 不是)。READ COMMITTED满足前面提到的隔离性的简单定义:一个事务开始时,只能“看见”已经提交的事务所做的修改。换句话说,一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。这个级别有时候也叫做不可重复读(nonrepeatable read),因为两次执行同样的查询,可能会得到不一样的结果。
REPEATABLE READ(可重复读)
REPEATABLE READ解决了脏读的问题。该级别保证了在同一个事务中多次读取同样记录的结果是一致的。在这个事务持续期间,禁止其他事务对这个字段进行更新(可以避免脏读和不可重复读,但幻读仍然存在)。InnoDB和XtraDB存储引擎通过多版本并发控制(MVCC,MultiversionConcurrency Control)解决了幻读的问题。本章稍后会做进一步的讨论。可重复读是MySQL的默认事务隔离级别。
SERIALIZABLE(可串行化)
SERIALIZABLE是最高的隔离级别。它通过强制事务串行执行,避免了前面说的幻读的问题。简单来说,SERIALIZABLE会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题。实际应用中也很少用到这个隔离级别,只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑采用该级别。
四种隔离级别中上述三种异常情况的容忍度如下(✔ 代表允许,❌ 代表禁止):
脏读 | 不可重复读 | 幻读 | |
---|---|---|---|
读未提交 Read Uncommitted | ✔ | ✔ | ✔ |
读已提交 Read Committed | ❌ | ✔ | ✔ |
可重复读 Repeatable Read | ❌ | ❌ | ✔ |
串行化 Serializable | ❌ | ❌ | ❌ |
输入:
SHOW VARIABLES LIKE 'transaction_isolation';
输出:
+-----------------------+-----------------+
| Variable_name | Value |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+-----------------+
输入:
# 将当前会话的隔离级别设为读未提交
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
# 将全局的隔离级别设为读未提交
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。