当前位置:   article > 正文

【MySQL系统学习专栏】- 事务处理_表 事务处理

表 事务处理


前言

本篇我们讲解有关事务处理的相关知识点,也意味着我们从此篇开始,就进入了MySQL的高级篇章,其中事务的并发控制是一个内容庞大的话题,有大量的理论文献对其进行详细的论述,本篇只简要地讨论MySQL如何控制并发读写

事务与锁机制是密不可分的,事务的隔离性通过事务的隔离级别来定义,并用锁机制来保证写操作的隔离性,锁机制将在下篇博客中整理
 

一、什么是事务

字面意思理解:事务,就是指一件具体、完整的事情

在数据库中,事务就是指一组SQL语句,这一组SQL语句是相互依赖的,作为一个不可分割的整体存在。

  1. 那么既然是一个整体,这组SQL语句的执行情况就只有全部成功,和全部都不成功两种情况,而不会存在部分成功或失败的情况。

  2. 进而可以说明,如果这组SQL语句的其中一条语句执行失败,那么将会发生回滚操作,将之前执行成功的语句以及它们所进行的操作、影响的数据都撤销,返回事务开始之前的状态。即单元中的所有sql语句都执行成功的话,那么该事务也就被顺利执行。

  3. 关于事务处理需要知道的几个术语

    事务(transaction):指一组SQL语句;

    回退(rollback):指撤销指定SQL语句的过程;

    提交(commit):指将未存储的SQL语句结果写入数据库表;

    保留点(savepoint):指事务处理中设置的临时占位符(place-holder),你可以对它发布回退(与回退整个事务处理不同)。

  4. 必须通过严格的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 中的记录来恢复数据。

  5. 并非所有存储引擎都支持事务处理,而MySQL支持几种基本的数据库引擎,MyISAM和InnoDB是两种最常用的引擎,但前者不支持明确的事务处理。

给出一个常用来解释事务的例子方便理解(银行应用):

假设一个银行的数据库有两张表:支票(checking)表和储蓄(saving)表。现在要从用户ChenQi的支票账户转移200美元到他的储蓄账户,那么需要至少三个步骤

  • 检查支票账户的余额是否高于200美元
  • 从支票账户余额中减去200美元
  • 在储蓄账户中增加200美元

上述三个步骤的操作必须打包在一个事务中,任何一个步骤失败,则必须回滚所有步骤。如果不采用事务处理,在第三步时发生错误,钱并没有增加进储蓄账户,而之前的操作又不能撤销,那ChenQi就白白损失了200美元,或者在第二步和第三步中间的时候,另外一个进程要删除支票账户所有余额,那么结果可能就是银行在不知道这个逻辑的情况下白白给了ChenQi 200美元。
 

二、事务的分类

按照事务的启动与执行方式,可以将事务分为3类:

  • 显示事务 :也称之为用户定义或用户指定的事务,即可以显式地定义启动和结束的事务。分布式事务属于显示事务
  • 自动提交事务:默认事务管理模式。如果一个语句成功地完成,则提交该语句;如果遇到错误,则回滚该语句。
  • 隐性事务:当连接以此模式进行操作时,sql将在提交或回滚当前事务后自动启动新事务。无须描述事务的开始,只需提交或回滚每个事务。它生成连续的事务链。

在默认情况下,一条sql语句就是一个事务,其实说的就是事务的自动提交模式,因为它是数据库引擎管理事务时默认的模式。
 

三、控制事务处理

(一)开启事务

MySQL使用下面的语句来标识一条事务的开始

输入:

START TRANSACTION
  • 1

(二)使用ROLLBACK

MySQL的ROLLBACK命令用来回退(撤销)MySQL语句

输入:

SELECT * FROM checking; 
START TRANSACTION;
DELETE FROM checking; 
SELECT * FROM checking; 
ROLLBACK;
SELECT * FROM checking;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

分析:

  • 首先显示checking表所有内容,表中内容不为空,
  • 然后开启一条事务处理,接下来的语句属于事务中的内容
  • 接着删除checking表中的所有记录
  • 紧跟着一条查询语句,证明checking表中内容为空
  • 此时用一条ROLLBACK语句回退START TRANSACTION之后的所有语句
  • 最后再次执行查询语句,输出显示该表并不为空

事务处理用来回退INSERT、UPDATE、DELETE语句,但不能回退SELECT语句,实际上回退SELECT语句也没有什么意义。也不能回退CREATE和DROP语句,即使执行了,这两条操作并不会被撤销

(三)使用COMMIT

一般的MySQL语句都是直接针对数据库表执行和编写的。实际上这就是隐式提交(隐式事务),即提交(写或保存)操作是自动进行的。

但是在事务处理的局域快中,提交并不会隐式的进行,需要进行明确的提交,要使用COMMIT语句

输入:

START TRANSACTION;
DELETE FROM checking WHERE number = 200; 
INSERT INTO saving values(200); 
COMMIT;
  • 1
  • 2
  • 3
  • 4

分析:

  • 这里我们要从checking表中删除200美元的记录,给saving表中插入一条200美元的记录
  • 因为涉及到两张表checking和saving,所以使用事务处理来保证整体的操作,不会只进行删除操作
  • 最后的COMMIT语句仅会在整个事务不出错的情况下才会对数据做出更改
  • 如果第一条DELETE语句起作用,但第二条失败,则第一条不会提交,会被自动撤销(回滚)

实际上,当COMMIT和ROLLBACK语句执行后,当前事务就会自动关闭。

(四)使用保留点

简单的ROLLBACK和COMMIT语句可以撤销或写入整个事务处理。但是,只是简单的事务处理可以如此操作,复杂的事务处理可能需要部分提交或回退。
为了支持部分回退的操作,必须能在事务处理块中合适的位置防止一个占位符,如此操作,若需要回退,可以回退到某个已记录的占位符的位置,这些占位符就称为保留点

例如,如果上面的插入200美元操作发生错误,我们只需要返回到删除checking表中记录后,插入操作之前即可,不需要整个回退。

输入:

SAVEPOINT delete1;

ROLLBACK TO delete1;
  • 1
  • 2
  • 3

每个保留点都要取表示它的唯一名字,以便在回退时,MySQL知道要回退到何处。

保留点在事务处理完成后(执行一条ROLLBACK或COMMIT)后会自动释放,自MySQL5以来,也可以用RELEASE SAVEPOINT来手动释放

(五)更改默认的提交方式

之前提到,MySQL的事务默认提交方式是自动提交。换句话说:任何时候你执行一条MySQL语句,对于数据库或表所做的更改,成功则立即生效,失败就不会更改,还会给出错误信息

为指定MySQL不自动提交,需要使用以下语句

输入:

SET autocommit = 0;
  • 1

分析:

autocommit标志决定是否自动提交更改操作,不管有没有COMMIT语句,设置其为0则指示MySQL不自动提交更改操作(直到autocommit被设置为真位置)
 

四、事务并发

无论何时,只要有多个查询需要在同一时刻修改数据,都会产生并发控制的问题,MySQL主要是在两个层面的并发控制:服务器层和存储引擎层。

因为某一刻不可能总只有一个事务在运行,可能出现A在操作checking表中的数据,B也同样在操作checking表,那么就会出现并发问题,对于同时运行的多个事务,当这些事务访问数据库中相同的数据时,如果没有采用必要的隔离机制,就会发生以下各种并发问题。

  • 脏读:对于两个事务T1,T2,T1读取了已经被T2更新但还没有被提交的字段之后,若T2回滚,T1读取的内容就是临时且无效的
  • 不可重复读 :对于两个事务T1,T2,T1读取了一个字段,然后T2更新了该字段之后,T1在读取同一个字段,值就不同了
  • 幻读:对于两个事务T1,T2,T1在A表中读取了一个字段,然后T2又在A表中插入了一些新的数据时,T1再读取该表时,就会发现神不知鬼不觉的多出几行了…,当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行(PhantomRow)

所以,为了避免以上出现的各种并发问题,我们就必然要采取一些手段。mysql数据库系统提供了四种事务的隔离级别,用来隔离并发运行各个事务,使得它们相互不受影响,这就是数据库事务的隔离性。
 

五、事务的隔离级别

一个事务与其他事务隔离的程度称为隔离级别。数据库规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性就越差。

(一)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

(二)事务隔离级别相关命令

1.查看隔离级别

输入:

SHOW VARIABLES LIKE 'transaction_isolation';
  • 1

输出:

+-----------------------+-----------------+
| Variable_name         | Value           |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+-----------------+
  • 1
  • 2
  • 3
  • 4
  • 5

2.设置隔离级别

输入:

# 将当前会话的隔离级别设为读未提交
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; 
# 将全局的隔离级别设为读未提交
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; 
  • 1
  • 2
  • 3
  • 4
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/239589?site
推荐阅读
相关标签
  

闽ICP备14008679号