MySQL高级篇——事务的概述与简单应用

Posted 张起灵-小哥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL高级篇——事务的概述与简单应用相关的知识,希望对你有一定的参考价值。

文章目录:

1.事务概述

2.事务的ACID特性

3.如何使用事务?

3.1 显式事务

3.2 隐式事务

4.实操案例


1.事务概述

首先,我们可以查看一下各种存储引擎对事务的支持情况。 仅有INNODB是支持事务的。

事务: 一组逻辑操作单元,使数据从一种状态变换到另一种状态。 事务处理的原则: 保证所有事务都作为 一个工作单元 来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交( commit ) ,那么这些修改就 永久 地保存下来;要么数据库管理系统将 放弃 所作的所有 修改 ,整个事务回滚 ( rollback ) 到最初状态。

2.事务的ACID特性

原子性(  atomicity ): 原子性是指事务是一个不可分割的工作单位,要么全部提交,要么全部失败回滚。 一致性(  consistency ): (国内很多网站上对一致性的阐述有误,具体你可以参考 Wikipedia Consistency 的阐述) 根据定义,一致性是指事务执行前后,数据从一个 合法性状态 变换到另外一个 合法性状态 。这种状态是 语义上 的而不是语法上的,跟具体的业务有关。 那什么是合法的数据状态呢?满足 预定的约束 的状态就叫做合法的状态。通俗一点,这状态是由你自己来定义的(比如满足现实世界中的约束)。满足这个状态,数据就是一致的,不满足这个状态,数据就 是不一致的!如果事务中的某个操作失败了,系统就会自动撤销当前正在执行的事务,返回到事务操作之前的状态。 隔离型(  isolation): 事务的隔离性是指一个事务的执行 不能被其他事务干扰 ,即一个事务内部的操作及使用的数据对 并发 其他事务是隔离的,并发执行的各个事务之间不能互相干扰。 持久性(  durability ): 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是 永久性的 ,接下来的其他操作和数据库故障不应该对其有任何影响。 持久性是通过 事务日志 来保证的。日志包括了 重做日志 回滚日志 。当我们通过事务对数据进行修改的时候,首先会将数据库的变化信息记录到重做日志中,然后再对数据库中对应的行进行修改。这样做的好处是,即使数据库系统崩溃,数据库重启后也能找到没有更新到数据库系统中的重做日志,重新执行,从而使事务具有持久性。

3.如何使用事务?

使用事务有两种方式,分别为 显式事务 隐式事务

3.1 显式事务

步骤 1 START TRANSACTION 或者 BEGIN ,作用是显式开启一个事务。 START TRANSACTION 语句相较于 BEGIN 特别之处在于,后边能跟随几个 修饰符 READ ONLY :标识当前事务是一个 只读事务 ,也就是属于该事务的数据库操作只能读取数据,而不能修改数据。 READ WRITE :标识当前事务是一个 读写事务 ,也就是属于该事务的数据库操作既可以读取数据,也可以修改数据。 WITH CONSISTENT SNAPSHOT :启动一致性读。 步骤 2 一系列事务中的操作(主要是 DML ,不含 DDL 步骤 3 提交事务 或 中止事务(即回滚事务)
mysql> BEGIN; 
#或者 
mysql> START TRANSACTION;

# 提交事务。当提交事务后,对数据库的修改是永久性的。 
mysql> COMMIT; 

# 回滚事务。即撤销正在进行的所有没有提交的修改 
mysql> ROLLBACK; 

# 将事务回滚到某个保存点。 
mysql> ROLLBACK TO [SAVEPOINT]

3.2 隐式事务

MySQL 中有一个系统变量 autocommit :(表示事务在默认情况下是自动提交的)

当然,如果我们想关闭这种 自动提交 的功能,可以使用下边两种方法之一: 显式的的使用 START TRANSACTION 或者 BEGIN 语句开启一个事务。这样在本次事务提交或者回滚前会暂时关闭掉自动提交的功能。 把系统变量 autocommit 的值设置为 OFF ,就像这样:
SET autocommit = OFF; 
#或
SET autocommit = 0;
#set autocommit = false;

SHOW VARIABLES LIKE 'autocommit';#默认是ON

UPDATE account SET balance = balance - 10 WHERE id = 1; #此时这条DML操作是一个独立的事务

UPDATE account SET balance = balance + 10 WHERE id = 2; #此时这条DML操作是一个独立的事务
SET autocommit = FALSE; #针对于DML操作是有效的,对DDL操作是无效的。

UPDATE account SET balance = balance - 10 WHERE id = 1;

UPDATE account SET balance = balance + 10 WHERE id = 2; 

COMMIT; #或rollback;  此时以上两个UPDATE为一个事务整体
#我们在autocommit为true的情况下,使用start transaction 或begin开启事务,那么DML操作就不会自动提交数据

START TRANSACTION;

UPDATE account SET balance = balance - 10 WHERE id = 1;

UPDATE account SET balance = balance + 10 WHERE id = 2; 

COMMIT; #或rollback;

当然在mysql中,也有一些隐式提交数据的情况:

数据定义语言( Data definition language ,缩写为: DDL 隐式使用或修改 mysql 数据库中的表 事务控制或关于锁定的语句 ① 当我们在一个事务还没提交或者回滚时就又使用 START TRANSACTION 或者 BEGIN 语句开启了另一个事务时,会 隐式的提交 上一个事务。即: ② 当前的 autocommit 系统变量的值为 OFF ,我们手动把它调为 ON 时,也会 隐式的提交 前边语句所属的事务。 ③ 使用 LOCK TABLES UNLOCK TABLES 等关于锁定的语句也会 隐式的提交 前边语句所属的事务。

4.实操案例

USE atguigudb2;

CREATE TABLE user(
		NAME VARCHAR(15) PRIMARY KEY
);

SELECT * FROM user;

下面我们开启一个事务,向表中插入一条数据,然后提交。 

BEGIN;
INSERT INTO user VALUES('张三');
COMMIT;

SELECT * FROM user;

再开启一个事务,向表中添加两条数据,第二个INSERT会报错的,此时提交事务,只有第一个INSERT会成功的。 

BEGIN;
INSERT INTO user VALUES('李四');
INSERT INTO user VALUES('李四');
COMMIT;

SELECT * FROM user;

下面我们将表清空,采用ROLLBACK回滚进行练习。

TRUNCATE TABLE user;

SELECT * FROM user;

BEGIN;
INSERT INTO user VALUES('张三');
COMMIT;

SELECT * FROM user;

下面还像上面的操作一样,我们向表中插入两条一样的数据,同样第二个INSERT会报错。

执行一下ROLLBACK,看看数据库表中数据如何?

INSERT INTO user VALUES('李四'); #ROLLBACK回滚到此处
INSERT INTO user VALUES('李四');

ROLLBACK;
SELECT * FROM user;

下面再将表中数据清空,做新的操作。 将completion_type变量的值修改为1。

TRUNCATE TABLE user;
SELECT * FROM user;

SELECT @@completion_type;
SET @@completion_type = 1;
SELECT @@completion_type;

BEGIN;
INSERT INTO user VALUES('张三'); 
COMMIT; #ROLLBACK回滚到此处

SELECT * FROM user;

INSERT INTO user VALUES('李四');
INSERT INTO user VALUES('李四'); 

ROLLBACK;
SELECT * FROM user;

 

下面演示一下INNODB和MYISAM在事务背景下的案例。

CREATE TABLE test1(i INT) ENGINE = INNODB;
CREATE TABLE test2(i INT) ENGINE = MYISAM;

#针对于innodb表
BEGIN;
INSERT INTO test1 VALUES (1);

SELECT * FROM test1;

可以看到,存储引擎为INNODB的表,在ROLLBACK之后,会正常回滚到事务最开始的状态,也即回滚之后表中没有任何数据。 

ROLLBACK;
SELECT * FROM test1;

#针对于myisam表:不支持事务
BEGIN;
INSERT INTO test2 VALUES (1);

SELECT * FROM test2;

可以看到,存储引擎为MYISAM的表,在ROLLBACK之后,表中插入的那条数据仍然存在,也即MYISAM存储引擎不支持事务。

ROLLBACK;
SELECT * FROM test2;

下面针对 SAVEPOINT 举例。

#举例3:体会savepoint
CREATE TABLE user3(
		NAME VARCHAR(15),
		balance DECIMAL(10,2)
);

BEGIN;
INSERT INTO user3(NAME,balance) VALUES('张三',1000);
COMMIT;

SELECT * FROM user3;

BEGIN;
UPDATE user3 SET balance = balance - 100 WHERE NAME = '张三'; # 900
UPDATE user3 SET balance = balance - 100 WHERE NAME = '张三'; # 800

SAVEPOINT s1;#设置保存点

UPDATE user3 SET balance = balance + 1 WHERE NAME = '张三'; # 801

ROLLBACK TO s1; #回滚到保存点

SELECT * FROM user3;

此时ROLLBACK TO s1就正常的回滚到了保存点s1的位置,balance为800。 

下面是针对整个事务进行回滚,则回到了最初添加数据balance为1000的时候。 

ROLLBACK; #回滚操作

SELECT * FROM user3;

以上是关于MySQL高级篇——事务的概述与简单应用的主要内容,如果未能解决你的问题,请参考以下文章

MySQL高级篇——事务的隔离级别与简单应用

MySQL高级篇——事务的隔离级别与简单应用

MySQL高级篇——锁的概述与案例应用

MySQL高级篇——锁的概述与案例应用

MYSQL的重要知识概述(高级篇)

「MySQL高级篇」MySQL日志事务原理 -- undologredologbinlog两阶段提交