mysql事务特征及其隔离级别,这下清楚了吧

mysql事务总结

事务是什么 ?

事务(transaction)是数据库操作中不可分割的工作单元,事务能够保证一个项目的完整性 。

如何控制事务

在mysql中,事务的自动提交状态是默认开启的。如何查询自动提交的状态呢?

mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
|            1 |
+--------------+

自动提交有什么作用呢?简单来说,如果开启自动提交,当我们执行一条mysql语句,其效果会直接产生,不能撤销。
如何没有开启 自动提交,当我执行一条语句时 ,并没有真正的执行,可以通过回滚(rollback)撤销, 如果确定要执行,需要通过commit确认。
关闭自动提交

mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)

新建测试用表

mysql> create table bank (
    -> id int primary key ,
    -> name varchar(20),
    -> money int );
Query OK, 0 rows affected (0.08 sec)

插入一条数据

mysql> insert into bank values (1 ,'张三', 1000);
Query OK, 1 row affected (0.00 sec)

查看一下

mysql> select * from bank;
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | 张三 |   1000 |
+----+------+-------+

回滚

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

再查看 ,没有数据,撤销成功。

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

commit 之后无法回滚

mysql> insert into bank values (1 ,'张三', 1000);
Query OK, 1 row affected (0.00 sec)

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

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

mysql> select * from bank;
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | 张三 |   1000 |
+----+------+-------+
1 row in set (0.00 sec)

当 autocommit = 1 时就不能回滚了,但是我们还可以使用begin 或者start transaction 开启一个事务 ,使其可以回滚。
比如比如张三在淘宝给女朋友买了一个口红400元 ,向淘宝店的银行账户转账 400;其实是同时发生了两个更新语句,我们希望这两个语句必须同时执行成功,或者同时失败,不然就出事了。事务可以保证语句结果一致。

mysql> set autocommit =1 ;
Query OK, 0 rows affected (0.09 sec)

mysql> insert into bank values(2 , '淘宝店', 1000);
Query OK, 1 row affected (0.07 sec)

mysql> start transaction;   --开启事务--
Query OK, 0 rows affected (0.00 sec)

mysql> update bank set money = money - 400 where id = 1;
Query OK, 1 row affected (0.00 sec)
mysql> update bank set money = money +  400 where id = 2;
Query OK, 1 row affected (0.00 sec)

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

mysql> select * from bank;
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | 张三   | 1000  |
|  2 | 淘宝店 | 1000  |
+----+--------+-------+
2 rows in set (0.00 sec)

可以看到,虽然开启自动提交, 在事务中, 还是可以让事务中语句回滚,这就保证了事务中的语句同时发生或者同时不发生。
但是一旦手动commit提交,便不可以回滚,如下

mysql> update bank set money = money - 400 where id = 1;
Query OK, 1 row affected (0.07 sec)
mysql> update bank set money = money + 400 where id = 2;
Query OK, 1 row affected (0.04 sec)

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

mysql> select * from bank;
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | 张三   | 600   |
|  2 | 淘宝店 | 1400  |
+----+--------+-------+

总结:

自动提交:
查看自动提交状态:select @@autocommit;
设置自动提交状态:set autocommit =0 ; 或者 set autocommit =1 ;
手动开启事务 : begin; 或者 start transaction;
手动提交:
@@AUTOCOMMIT = 0 时,使用commit; 命令提交事务。
事务回滚
@@AUTOCOMMIT = 0 时,使用 rollback; 命令回滚事务。

事务的四大特征ACID

  1. A (Atomicity) 原子性指事务是最小单位, 不可以再分割。
  2. C(Consistency) 一致性指要求同一事务的语句需要同时成功或者失败
  3. I (Isolation) 隔离性指不同事务之间是具有隔离的
  4. D(durability) 持久性指事务一旦结束, 就会产生永久的影响 。

事务隔离性的四种级别

原子性, 一致性, 持久性都比较容易理解, 下面详细分析隔离性 , 事务之间的隔离分为四种等级

  • read uncommitted 字面意思就是读未提交的 ,也就是说事务A可以读事务B没有commit的语句
  • read committed 读已提交的, 事务A只能读取事务B已经committed的语句
  • repeatable read 可被重复读 事务A与事务B之间不共享数据
  • serializable 串行化,所有的事务都会按照固定顺序执行,执行完一个事务后再执行下一个事务。

那么如何设定隔离级别呢 ? set global transaction isolation level 隔离级别 ;
先查看mysql 默认隔离级别 select @@global.transaction_isolation ;

mysql> select @@global.transaction_isolation;  --默认隔离级别是  REPEATABLE-READ   --global是全局的,不加就是会话级别的
+--------------------------------+
| @@global.transaction_isolation |
+--------------------------------+
| REPEATABLE-READ                |
+--------------------------------+

修改隔离级别为可读未提交的

mysql> set global transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)

现在张三觉得上次的口红不错,决定回购口红, 淘宝店和张三同时开启事务
张三开启事务转账 (但张三没有commit)

mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> update bank set money = money - 400 where id = 1 ;
Query OK, 1 row affected (0.00 sec)

张三转完了提醒淘宝单发货,淘宝点开启事务查余额

mysql> begin;
mysql> select * from bank;
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | 张三   | 200   |
|  2 | 淘宝店 | 1400  |
+----+--------+-------+
2 rows in set (0.00 sec)

到账了,发货 ;这时张三和对象吵架了,不买了,撤销操作 ;

mysql> rollback ;
mysql> select * from bank;
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | 张三   | 600   |
|  2 | 淘宝店 | 1400  |
+----+--------+-------+

这就造成了淘宝店主读到了不存在的余额, 一个事务读取到另外一个事务还未提交的数据 ,称为脏读 。这在实际开发中是不允许出现的,通过修改隔离级别可以解决这个问题

mysql> set global transaction isolation level read committed ;
Query OK, 0 rows affected (0.00 sec)

这时候,淘宝店只能读到张三提交的数据, 避免了脏读。虽然 read committed 让我们只能读取到其他事务已经提交的数据,但还是会出现问题,就是在读取同一个表的数据时,可能会发生前后不一致的情况(因为其他事务提交了数据)。这被称为不可重复读现象 ( read committed) 。
为了避免这种情况, 将隔离级别升级,

mysql> set global transaction isolation level repeatable read ;
Query OK, 0 rows affected (0.00 sec)

当前事务A开启后,不管事务B是否提交数据, 那么在事务A这条线上,都不会查询到事务B的操作数据。相当于当前事务开辟出一条单独的线程。但是事务B的操作是确实生效的 , 比如在repeatable read 级别下,银行端发现张三是法外狂徒,把张三的卡给注销了, 同时张三在查询,没有看到钱少了,但已经不能消费了 。

mysql> begin;   ----银行端事务---
mysql> delete from bank where id = 1 ;
Query OK, 1 row affected (0.00 sec)
mysql> select * from bank;
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  2 | 淘宝店 | 1400  |
+----+--------+-------+
1 row in set (0.00 sec)

张三查余额 ,准备吃小龙虾 ,手机支付发现···

mysql> begin;   ---张三开启事务---
mysql> select * from bank;
+----+--------+-------+
| id | name   | money |
+----+--------+-------+
|  1 | 张三   | 600   |
|  2 | 淘宝店 | 1400  |
+----+--------+-------+

mysql> update bank set money = money -100 where id = 1;
Rows matched: 0  Changed: 0  Warnings: 0       ---Rows matched: 0  没有该账户了

这种现象也被称为幻读,一个事务提交的数据,不能被其他事务读取到。
串行化可以解决这个问题,串行化使得事务排队执行 , 这样就不会上述的问题, 但性能较差 。

mysql> set global transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)

当一个事务在开启时

mysql> begin;  ---窗口1 
Query OK, 0 rows affected (0.00 sec)

另一个事务开启后,无法执行会卡住,

mysql> start transaction; --另一个窗口2 事务也开启 
Query OK, 0 rows affected (0.00 sec)
mysql> update bank set money = money -100 where  id = 2 ;

等窗口1结束后

mysql> commit; ---窗口1 
Query OK, 0 rows affected (0.00 sec)

窗口2立刻执行

mysql> update bank set money = money -100 where  id = 1 ;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0  Changed: 0  Warnings: 0

这就是事务串行化,行化的意思就是:假设把所有的事务都放在一个串行的队列中,那么所有的事务都会按照固定顺序执行,执行完一个事务后再继续执行下一个事务的写入操作 ( 这意味着队列中同时只能执行一个事务的写入操作 )。

总结
隔离性是指事务之间的关系,分为四种,安全性越来越高, 性能越来越低

感谢您在茫茫的网络世界中阅读了本文, 希望没有浪费您宝贵的时间,期待您指出文中的不足!

猜你喜欢

转载自blog.csdn.net/weixin_43705953/article/details/107203188
今日推荐