4

事务基础知识_大鱼的技术博客_51CTO博客

 2 years ago
source link: https://blog.51cto.com/learningfish/5681878
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

一、数据库事务概述

1.1 存储引擎支持情况

 SHOW ENGINES命令来查看当前MySQL支持的存储引擎都有哪些,以及这些存储引擎是否支持事务。
事务基础知识_事务

1.2 基本概念

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

1.3 事务的ACID特性

  • 原子性(atomicity):原子性是指事务是一个不可分割的工作单位,要么全部提交,要么全部失败回滚。
  • 一致性(consistency):一致性是指事务执行前后,数据从一个合法性状态变换到另外一个合法性状态。这种状态是语义上的而不是语法上的,跟具体的业务有关。
  • 隔离型(isolation):事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
  • 持久性(durability):持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。

1.4 事务的状态

我们现在知道事务是一个抽象的概念,它其实对应着一个或多个数据库操作,MySQL根据这些操作所执行的不同阶段把事务大致划分成几个状态:

  • 活动的(active):事务对应的数据库操作正在执行过程中时,我们就说该事务处在活动的状态。
  • 部分提交的(partially committed):当事务中的最后一个操作执行完成,但由于操作都在内存中执行,所造成的影响并没有刷新到磁盘时,我们就说该事务处在部分提交的状态。
  • 失败的(failed):当事务处在活动的或者部分提交的状态时,可能遇到了某些错误(数据库自身的错误、操作系统错误或者直接断电等)而无法继续执行,或者人为的停止当前事务的执行,我们就说该事务处在失败的状态。
  • 中止的(aborted):如果事务执行了一部分而变为失败的状态,那么就需要把已经修改的事务中的操作还原到事务执,行前的状态。换句话说,就是要撤销失败事务对当前数据库造成的影响。我们把这个撤销的过程称之为回滚。当回滚操作执行完毕时,也就是数据库恢复到了执行事务之前的状态,我们就说该事务处在了中止的状态。
  • 提交的(committed):当一个处在部分提交的状态的事务将修改过的数据都同步到磁盘上之后,我们就可以说该事务处在了提交的状态。

状态转换图如下所示:

事务基础知识_mysql_02

二、事务隔离级别

2.1 数据并发问题

  • 脏写( Dirty Write ): 对于两个事务 Session A、Session B,如果事务Session A修改了另一个未提交事务Session B修改过的数 据,那就意味着发生了脏写。

  • 脏读( Dirty Read ) :对于两个事务 Session A、Session B,Session A 读取了已经被 Session B更新但还没有被提交的字段。 之后若 Session B 回滚 ,Session A 读取 的内容就是临时且无效的。

  • 不可重复读( Non-Repeatable Read ) :对于两个事务Session A、Session B,Session A读取了一个字段,然后 Session B更新了该字段。 之后 Session A再次读取同一个字段,值就不同了。那就意味着发生了不可重复读

  • 幻读( Phantom ) 对于两个事务Session A、Session B, Session A 从一个表中读取了一个字段, 然后 Session B 在该表中插入了一些新的行。 之后, 如果 Session A 再次读取同一个表, 就会多出几行。那就意味着发生了幻读

2.2 SQL中的四种隔离级别

  • READ UNCOMMITTED:读未提交,在该隔离级别,所有事务都可以看到其他未提交事务的执行结 果。不能避免脏读、不可重复读、幻读。
  • READ COMMITTED :读已提交,它满足了隔离的简单定义:一个事务只能看见已经提交事务所做 的改变。这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。可以避免脏读,但不可 重复读、幻读问题仍然存在。
  • REPEATABLE READ :可重复读,事务A在读到一条数据之后,此时事务B对该数据进行了修改并提 交,那么事务A再读该数据,读到的还是原来的内容。可以避免脏读、不可重复读,但幻读问题仍 然存在。这是MySQL的默认隔离级别。
  • SERIALIZABLE :可串行化,确保事务可以从一个表中读取相同的行。在这个事务持续期间,禁止 其他事务对该表执行插入、更新和删除操作。所有的并发问题都可以避免,但性能十分低下。能避 免脏读、不可重复读和幻读。

如下表所示:

隔离级别 脏读 不可重复读 幻读 加锁读
READ UNCOMMITED ×
READ COMMITTED × ×
REPEATABLE READ × × ×
SERIALIZABLE × × × ×

不同的隔离级别有不同的现象,并有不同的锁和并发机制,隔离级别越高,数据库的并发性能就越差,4 种事务隔离级别与并发性能的关系如下:

事务基础知识_事务_03

2.3 查询隔离级别

mysql> show variables like 'transaction_isolation';
+-----------------------+-----------------+
| Variable_name         | Value           |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set (0.00 sec)

mysql> SELECT @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| REPEATABLE-READ         |
+-------------------------+
1 row in set (0.00 sec)

2.4 如何设置事务的隔离级别

通过下面的语句修改事务的隔离级别:

SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL 隔离级别;
#其中,隔离级别格式:
> READ UNCOMMITTED
> READ COMMITTED
> REPEATABLE READ
> SERIALIZABLE
SET [GLOBAL|SESSION] TRANSACTION_ISOLATION = '隔离级别'
#其中,隔离级别格式:
> READ-UNCOMMITTED
> READ-COMMITTED
> REPEATABLE-READ
> SERIALIZABLE

三、使用事务操作

专业术语:

  • 事务(transaction):指一组SQL语句;
  • 回退(rollback):指撤销指定的SQL语句的过程;
  • 提交(commit):指将未存储的SQL语句写入数据库表;
  • 保留点(savepoint):指事务处理中设置的临时占位符(place-holder),你可以对它发布回退(与回退整个事务处理不同)

3.1 开启事务

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

3.2 使用ROLLBACK

mysql> select * from student;
+-----------+------+-------+
| studentno | name | class |
+-----------+------+-------+
|         1 | tom  | 1班   |
|         2 | jack | 2班   |
+-----------+------+-------+
2 rows in set (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> delete from student;
Query OK, 2 rows affected (0.00 sec)

mysql> select * from student;
Empty set (0.00 sec)

mysql> rollback;
Query OK, 0 rows affected (0.01 sec)

mysql> select * from student;
+-----------+------+-------+
| studentno | name | class |
+-----------+------+-------+
|         1 | tom  | 1班   |
|         2 | jack | 2班   |
+-----------+------+-------+
2 rows in set (0.00 sec)
    
mysql>

3.3 使用COMMIT

SESSION A: 插入数据未提交

mysql> select * from student;
+-----------+------+-------+
| studentno | name | class |
+-----------+------+-------+
|         1 | tom  | 1班   |
|         2 | jack | 2班   |
+-----------+------+-------+
2 rows in set (0.00 sec)

mysql> 
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into student(studentno,name,class) values(3,'lily','1班');
Query OK, 1 row affected (0.00 sec)

mysql> select * from student;
+-----------+------+-------+
| studentno | name | class |
+-----------+------+-------+
|         1 | tom  | 1班   |
|         2 | jack | 2班   |
|         3 | lily | 1班   |
+-----------+------+-------+
3 rows in set (0.00 sec)

mysql> 

SESSION B: 查询数据

mysql> select * from student;
+-----------+------+-------+
| studentno | name | class |
+-----------+------+-------+
|         1 | tom  | 1班   |
|         2 | jack | 2班   |
+-----------+------+-------+
2 rows in set (0.00 sec)

mysql> 

SESSION A: 提交数据

mysql> commit;
Query OK, 0 rows affected (0.01 sec)

SESSION B: 查询数据

mysql> select * from student;
+-----------+------+-------+
| studentno | name | class |
+-----------+------+-------+
|         1 | tom  | 1班   |
|         2 | jack | 2班   |
|         3 | lily | 1班   |
+-----------+------+-------+
3 rows in set (0.00 sec)

mysql> 

3.4 使用SAVEPOINT

mysql> select * from student;
+-----------+------+-------+
| studentno | name | class |
+-----------+------+-------+
|         1 | tom  | 1班   |
|         2 | jack | 1班   |
|         3 | lily | 1班   |
+-----------+------+-------+
3 rows in set (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.01 sec)

mysql> update student set class = '2.班' where studentno = 3;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from student;
+-----------+------+-------+
| studentno | name | class |
+-----------+------+-------+
|         1 | tom  | 1班   |
|         2 | jack | 1班   |
|         3 | lily | 2班   |
+-----------+------+-------+
3 rows in set (0.00 sec)

mysql> savepoint p1;
Query OK, 0 rows affected (0.00 sec)

mysql> delete from student;
Query OK, 3 rows affected (0.00 sec)

mysql> select * from student;
Empty set (0.01 sec)

mysql> rollback to p1;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from student;
+-----------+------+-------+
| studentno | name | class |
+-----------+------+-------+
|         1 | tom  | 1班   |
|         2 | jack | 1班   |
|         3 | lily | 2班   |
+-----------+------+-------+
3 rows in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.01 sec)

mysql> select * from student;
+-----------+------+-------+
| studentno | name | class |
+-----------+------+-------+
|         1 | tom  | 1班   |
|         2 | jack | 1班   |
|         3 | lily | 2班   |
+-----------+------+-------+
3 rows in set (0.00 sec)

mysql> 


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK