前言
mysql中的事务是用来确保我们在操作数据库时,整个操作过程要么全部成功,要么全部失败,不会出现前半部分成功了,后半部分却失败的情况;
比如我们日常生活中常见的转账操作,如果没有事务的支持,就可能出现你卡里的钱都被扣了,但是小王那边却没收到的情况;
事务这个功能是在数据库的引擎层面实现的,InnoDB支持事务,MyISAM不支持;
下面我们就介绍下事务相关的概念
目录
- 事务的启动方式
- 事务的隔离级别
- 事务隔离的实现
正文
1. 事务的启动方式
启动方式分为显式启动和隐式启动;
- 显式启动
通过 begin
或者start transaction
启动事务;
通过 commit
提交事务;
通过 rollback
回滚事务;
显式启动的好处就是启动和提交(回滚)总是配套出现的,如果忘记提交也会很容易查出来;
这样就减少了长事务的出现;
- 隐式启动
不需要通过begin
或者 start transaction
启动事务;
当你执行一条SQL语句时(比如select * from t
),一个事务就启动了;
此时如果你 set autocommit = 0
,那么事务不会自动提交,需手动 commit 提交 或者 rollback回滚;这就可能导致长事务的出现(因为没有配套的启动事务命令,所以会容易忘记提交命令);
而如果 set autocommit = 1
,事务就自动提交,此时无需手动commit;每条语句都会自动启动一个事务,语句执行成功后会自动提交事务,语句执行失败后会自动回滚事务;
但是设置autocommit=1
会存在一个问题:就是事务失去了它的作用,无法保证数据的一致性,比如转账操作;
推荐的做法:建议都用显式启动的方式来 操作事务;同时 set autocommit = 1,这样就可以防止隐式启动带来的长事务问题;
2. 事务的隔离级别
有4种:读未提交、读提交、可重复读、串行化;
- 读未提交(READ-UNCOMMITED)
定义:当前事务运行期间,可以读到其他事务还未提交的数据
比如我现在启动一个事务A,去读取id=1这条数据,刚开始读到的结果为1;
这时候我再开启一个事务B,去更新id=1这行数据;
此时,我再去事务A中读取数据,就会读到最新的数据;
下面我们用例子来演示下:
先修改隔离级别为读未提交:
mysql> set transaction_isolation='READ-UNCOMMITTED';
Query OK, 0 rows affected (0.00 sec)
查看当前的隔离级别:可以看到设置成功
mysql> show variables like 'transaction_isolation';
+-----------------------+------------------+
| Variable_name | Value |
+-----------------------+------------------+
| transaction_isolation | READ-UNCOMMITTED |
+-----------------------+------------------+
1 row in set (0.05 sec)
显式启动事务A,同时查询id=1的数据:
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test where id = 1;
+----+------+
| id | name |
+----+------+
| 1 | 1 |
+----+------+
1 row in set (0.00 sec)
显式启动事务B,同时更新id=1的数据:
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> update test set name = 2 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
再次回到事务A,查询id=1的数据:
mysql> select * from test where id = 1;
+----+------+
| id | name |
+----+------+
| 1 | 2 |
+----+------+
1 row in set (0.01 sec)
可以看到,虽然我们没有提交事务B,但是在事务A中还是可以看到最新的数据,这就是读未提交的功劳;
这种隔离级别的特点就是永远都可以读到最新的数据;缺点也很明显,就是在一个事务操作中,会读到一个数据的不同版本,这样就会给人一种数据紊乱的错觉;
下面几种隔离级别的例子类似,都是先改隔离级别的参数,然后在多个事务中进行读写操作
- 读提交
定义:当前事务运行期间,可以读到其他事务提交过的数据,没提交的读不了
- 可重复读(默认)
定义:当前事务运行期间,读到的数据始终保持一致,即事务启动时读到的是什么数据,那么在整个事务期间都是读到这个数据,不会再变
- 串行化
定义:从字面意思来理解,有点类似单线程运行,比如现在有一个事务在对一行数据进行写操作,那么另一个事务就必须等待前一个事务提交后才可以继续操作;
3. 事务隔离的实现
事务的隔离主要是通过视图的概念来实现的;
假设现在事务的隔离级别是可重复读:
mysql> show variables like 'transaction_isolation';
+-----------------------+-----------------+
| Variable_name | Value |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set (0.05 sec)
那么当事务启动时,就会创建一个视图,用来记录当前的数据;
此时不管其他事务怎么更新数据,前面启动的事务都不会受到影响;
这时我们就可以说通过不同的视图,实现了事务的隔离;
上面我们用读未提交的隔离级别举了一个例子,因为读未提交的隔离级别是最低的,所以看不出隔离的效果;
感兴趣的可以用默认的隔离级别(可重复读)重复上面的例子,就会看到不管怎么改,之前启动的事务都不会受到新事务的影响;
总结
事务的启动方式有显式启动,隐式启动;推荐显式启动,通过begin启动,commit提交,rooback回滚;同时 set autocommit = 1,这样就可以防止隐式启动带来的长事务问题;
事务的隔离级别有四种:读未提交、读提交、可重复读、串行化;隔离级别依次递增;
事务的隔离是通过创建不同的视图来实现的:不同时刻启动的事务,会创建不同的视图;
评论区