34.MySQL中的事务

Posted 码海无际

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了34.MySQL中的事务相关的知识,希望对你有一定的参考价值。

1.事务的概念

所谓的事务就是针对数据库的一组操作,它可以由一条或多条SQL语句组成,同一个事务的操作具备同步的特点,即事务中的语句要么都执行,要么都不执行。

举例:

转账可以分为两部分来完成:转入和转出。只有这两个部分都完成才认为转账成功。

A账户给B账户转账100元:

  1. A账户减少100元,

  2. B账户增加100元。

在数据库中,这个过程是使用两条SQL语句来完成的。

# A 账户减少100元
UPDATE account SET money = money - 100 WHERE name = 'A';
# B 账户增加100元
UPDATE account SET money = money + 100 WHERE name = 'B';

如果其中任意一条语句出现异常没有执行,则会导致两个账户的金额不同步,造成错误。

为了防止这种情况发生,就需要使用mysql中的事务(Transaction)。

2.事务的基本操作

在默认情况下,用户执行的每一条SQL语句都会被当成单独的事务自动提交。

如果要将一组SQL语句作为一个事务,则需要先执行以下语句显式地开启一个事务。

START TRANSACTION;

此时,每一条SQL语句不再自动提交,用户需要手动提交,操作才会生效。

COMMIT;

如果不想提交当前事务,可以取消事务(即回滚)。

ROLLBACK;

准备数据

CREATE DATABASE mahaiwuji;
USE mahaiwuji;

CREATE TABLE student(
  id INT(4) PRIMARY KEY,
  sname VARCHAR(36),
money INT(4)
) ENGINE = INNODB DEFAULT CHARSET = utf8;

INSERT INTO student VALUES(1,'A',1000);
INSERT INTO student VALUES(2,'B',1000);

提交实例演示

# 开启事务
START TRANSACTION;
# A减少100元
UPDATE student SET money = money - 100 WHERE sname = 'A';
# B增加100元
UPDATE student SET money = money + 100 WHERE sname = 'B';
# 提交事务
COMMIT;

回滚实例演示

# 开启事务
START TRANSACTION;
# B扣除100元
UPDATE student SET money = money - 100 WHERE sname = 'B';
# 回滚事务
ROLLBACK;

事务的执行要么成功,要么就返回到事务开始前的状态,这就保证了同一事务操作的同步性和数据的完整性。

MySQL中的事务必须满足A、C、I、D这4个基本特性:

  1. Atomicity:原子性,一个事务必须被视为一个不可分割的最小工作单元,只有事务中所有的数据库操作都执行成功,才算整个事务执行成功。事务中如果有任何一个SQL语句执行失败,已经执行成功的SQL语句也必须撤销,数据库的状态退回到执行事务前的状态。

  2. Consistency:一致性,是指在事务处理时,无论执行成功还是失败,都要保证数据库系统处于一致的状态,保证数据库系统从不返回到一个未处理的事务中。MySQL中的一致性主要由日志机制实现的,通过日志记录数据库的所有变化,为事务恢复提供了跟踪记录。

  3. Isolation:隔离性,是指当一个事务在执行时,不会受到其他事务的影响。保证了未完成事务的所有操作与数据库系统的隔离,直到事务完成为止,才能看到事务的执行结果。隔离性相关的技术有并发控制、可串行化、锁等。当多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。

  4. Durability:持久性,是指事务一旦提交,其对数据库的修改就是永久性的。事务的持久性不能做到百分百的持久,只能从事务本身的角度来保证永久性,而一些外部原因导致数据库发生故障,如硬盘损坏,那么所有提交的数据可能都会丢失。

注意:

  1. MySQL中的事务不允许嵌套,若在执行START TRANSACTION语句前上一个事务还未提交,会隐式地执行提交操作。

  2. 事务处理主要是针对数据表中数据的处理,不包括创建或删除数据库数据表修改表结构等操作,而且执行这类操作时会隐式地提交事务。例如:在使用START TRANSACTION开启事务后执行ALTER TABLE修改表结构,会隐式提交事务。

  3. 当执行COMMIT或ROLLBACK后,当前事务就会自动结束。ROLLBACK只能针对未提交的事务回滚,已提交的事务无法回滚。

  4. MySQL 5.7默认的存储引擎为InnoDB,该存储引擎支持事务,而另一个常见的存储引擎MyISAM不支持事务。对于MyISAM存储引擎的数据表,无论事务是否提交,对数据的操作都会立即生效,不能回滚。

  5. 在MySQL中,还可以使用START TRANSACTION的别名BEGIN或BEGIN WORK来显式地开启一个事务。但由于BEGIN与MySQL编程中的BEGIN...END冲突,因此不推荐使用BEGIN。

事务的自动提交:

MySQL默认是自动提交模式。

如果没有显式开启事务(START TRANSACTION),每一条SQL语句都会自动提交(COMMIT)。

查看事务是否自动提交:

SELECT @@autocommit;

关闭自动提交:

SET AUTOCOMMIT = 0;

关闭后:用户需要手动执行提交(COMMIT)操作。若直接终止MySQL会话,MySQL会自动进行回滚。

开启自动提交:

SET AUTOCOMMIT = 1;

3.事务的保存点

回滚事务时,若希望只撤销一部分,可以用保存点来实现:

SAVEPOINT 保存点名;

在设置保存点后,可以将事务回滚到指定保存点

ROLLBACK TO SAVEPOINT 保存点名;

若不再需要一个保存点,使用如下语句删除

RELEASE SAVEPOINT 保存点名;

实例演示:

# 查询A的金额
SELECT sname, money FROM student WHERE sname = 'A';
# 开启事务
START TRANSACTION;
# A扣除100元
UPDATE student SET money = money - 100 WHERE sname = 'A';
# 查询A的金额
SELECT sname, money FROM student WHERE sname = 'A';
# 创建保存点s1
SAVEPOINT s1;
# A再扣除50元
UPDATE student SET money = money - 50 WHERE sname = 'A';
# 查询A的金额
SELECT sname, money FROM student WHERE sname = 'A';
# 回滚到保存点s1
ROLLBACK TO SAVEPOINT s1;
# 查询A的金额
SELECT sname, money FROM student WHERE sname = 'A';
# 回滚事务
ROLLBACK;
# 查询A的金额
SELECT sname, money FROM student WHERE sname = 'A';

一个事务中可以创建多个保存点,在提交事务后,事务中的保存点就会被删除

回滚到某个保存点后,在该保存点之后创建过的保存点也会消失

4.事务隔离级别

事务隔离级的意义:

数据库是一个多用户的共享资源,MySQL允许多线程并发访问,用户可以通过不同的线程执行不同的事务。为了保证这些事务之间不受影响,对事务设置隔离级是十分必要的。

查看隔离级别:

# 查看全局隔离级
SELECT @@global.transaction_isolation;
# 查看当前会话中的隔离级
SELECT @@session.transaction_isolation;
# 查看下一个事务的隔离级
SELECT @@transaction_isolation;

全局的隔离级:影响所有连接MySQL用户。

当前会话隔离级:只影响当前正在登录MySQL服务器的用户。(不会影响其他用户)

下一个事务的隔离级:仅对当前用户的下一个事务操作有影响。

默认情况下3种方式的结果都是REPEATABLE READ

MySQL中事务的隔离级别:

  1. REPEATABLE READ:可重复读

  2. READ UNCOMMITTED:读取未提交

  3. READ COMMITTED:读取提交

  4. SERIALIZABLE:可串行化

设置事务的隔离级别:

SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL 参数值
  • SESSION:当前会话

  • GLOBAL:全局

  • 直接省略:下一个事务的隔离级

  • TRANSACTION:事务

  • ISOLATION:隔离

  • LEVEL:级别

修改为READ UNCOMMITTED

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

修改为默认REPEATABLE READ

SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

事务的默认访问模式为 READ WRITE(读/写模式),事务可以执行:(查询)或 (更改、插入、删除等)操作。

若开发需要,可以将事务的访问模式设置为READ ONLY(只读模式),禁止对表进行更改。

# 设置只读事务
SET [SESSION | GLOBAL] TRANSACTION READ ONLY
# 恢复成读写事务
SET [SESSION | GLOBAL] TRANSACTION READ WRITE

5.MySQL的4种隔离级别

1.READ UNCOMMITTED(读取未提交):

事务中最低的级别,可以读取到其他事务中未提交的数据。

也称为脏读(Dirty Read):一个事务读取了另外一个事务未提交的数据。

情景模拟

A给B转账100元购买商品。

A开启事务后转账,但不提交事务,通知B来查询,

如果B的隔离级别较低,就会读取到A的事务中未提交的数据

发现A确实给自己转了100元,就给A发货。

等B发货成功后,A将事务回滚,B就会受到损失。

2.READ COMMITTED(读取提交):

大多数DBMS(如SQL Server、Oracle)的默认隔离级,但不包括MySQL。只能读取其他事务已经提交的数据,避免了脏读问题。

但是会出现不可重复读(NON-REPEATABLE READ)问题。

不可重复读:一个事务中多次查询的结果不一致,原因是查询的过程中数据发生了改变。

情景模拟

在网站后台统计所有用户的总金额。

第1次查询A有900元,

第2次查询A有800元。

问题:在同一个事务中,同样的两次查询结果不同。

原因:第2次查询前A取出了100元。

3.REPEATABLE READ(可重复读):

MySQL的默认事务隔离级,它解决了脏读和不可重复读的问题,确保了同一事务的多个实例在并发读取数据时,会看到同样的结果。

该级别理论上会出现幻读(PHANTOM READ)问题。

幻读又被称为虚读,是指在一个事务内两次查询中数据条数不一致

如:其他事务做了插入记录的操作,导致记录数有所增加。

不过,MySQL的InnoDB存储引擎已经解决了幻读问题。

情景模拟

在网站后台统计所有用户的总金额时,

当前只有两个用户,总金额为2000元,

此时新增一个用户,并且存入1000元。

再次统计会发现总金额变为3000元,造成了幻读的情况。

4.SERIALIZABLE(可串行化):

隔离级的最高级别,它在每个读的数据行上加锁,使之不会发生冲突,

解决了脏读、不可重复读和幻读的问题。

由于加锁可能导致超时(Timeout)和锁竞争(Lock Contention)现象,

性能是4种隔离级中最低的。

除非为了数据的稳定性,需要强制减少并发的情况时,才会选择此种隔离级。

以上是关于34.MySQL中的事务的主要内容,如果未能解决你的问题,请参考以下文章

Android中的片段事务问题

片段事务分离和附加后ListView不工作?

BottomNavigationView 滞后于片段事务

理解片段事务期间片段的生命周期方法调用

提交带有全屏片段的片段事务

使用 OnItemClickListener 列出视图片段到片段事务