当前位置:首页 » 《随便一记》 » 正文

MySQL学习第二部分:事务的理解

24 人参与  2022年08月25日 13:23  分类 : 《随便一记》  评论

点击全文阅读


当MySQL的CURD不加控制,会有什么问题?

以买车票为例
在这里插入图片描述

CURD满足什么属性,能解决上述问题?

1.买票的过程得是原子的吧
2.买票互相应该不能影响吧
3.买完票应该要永久有效吧
4.买前,和买后都要是确定的状态吧

因此引出了事务的概念!

什么是事务?

说白了我们为完成某个功能使用的每条sql语句都是事务,有的事务是一条sql语句组成,有的事务是多条sql语句组成,
比如我想让A的钱转给B那我们就要执行两条sql语句,一条把A的余额减少,一条把B的余额增加,这两条加起来才是一个完整的事务

为避免上述类似火车票问题,一个完整的事务,绝对不是简单的 sql 集合,还需要满足如下四个属性:
在这里插入图片描述

为什么会出现事务

在这里插入图片描述

MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务!MyISAM 不支持。

事务提交方式(1.自动提交 2.手动提交)

查看事务提交方式

mysql> show variables like 'autocommit';+---------------+-------+| Variable_name | Value |+---------------+-------+| autocommit | ON |+---------------+-------+1 row in set (0.41 sec)

用 SET 来改变 MySQL 的自动提交模式

mysql> SET AUTOCOMMIT=0; #SET AUTOCOMMIT=0 禁止自动提交Query OK, 0 rows affected (0.00 sec)mysql> show variables like 'autocommit';+---------------+-------+| Variable_name | Value |+---------------+-------+| autocommit | OFF |+---------------+-------+1 row in set (0.00 sec)
mysql> SET AUTOCOMMIT=1; #SET AUTOCOMMIT=1 开启自动提交Query OK, 0 rows affected (0.00 sec)mysql> show variables like 'autocommit';+---------------+-------+| Variable_name | Value |+---------------+-------+| autocommit | ON |+---------------+-------+1 row in set (0.01 sec)

事务常见操作方式

创建测试表

在这里插入图片描述

正常演示 - 证明事务的开始与回滚

mysql> show variables like 'autocommit'; -- 查看事务是否自动提交。我们故意设置成自动提交,看看该选项是否影响begin+---------------+-------+| Variable_name | Value |+---------------+-------+| autocommit | ON |+---------------+-------+1 row in set (0.00 sec)mysql> start transaction; -- 开始一个事务begin也可以,推荐beginQuery OK, 0 rows affected (0.00 sec)mysql> savepoint save1; -- 创建一个保存点save1Query OK, 0 rows affected (0.00 sec)mysql> insert into account values (1, '张三', 100); -- 插入一条记录Query OK, 1 row affected (0.05 sec)mysql> savepoint save2; -- 创建一个保存点save2Query OK, 0 rows affected (0.01 sec)mysql> insert into account values (2, '李四', 10000); -- 在插入一条记录Query OK, 1 row affected (0.00 sec)mysql> select * from account; -- 两条记录都在了+----+--------+----------+| id | name | blance |+----+--------+----------+| 1 | 张三 | 100.00 || 2 | 李四 | 10000.00 |+----+--------+----------+2 rows in set (0.00 sec)mysql> rollback to save2; -- 回滚到保存点save2Query OK, 0 rows affected (0.03 sec)mysql> select * from account; -- 一条记录没有了+----+--------+--------+| id | name | blance |+----+--------+--------+| 1 | 张三 | 100.00 |+----+--------+--------+1 row in set (0.00 sec)mysql> rollback; -- 直接rollback,回滚在最开始Query OK, 0 rows affected (0.00 sec)mysql> select * from account; -- 所有刚刚的记录没有了Empty set (0.00 sec)

非正常演示1 - 证明未commit,客户端崩溃,MySQL自动会回滚(隔离级别设置为读未提交)

--终端 Amysql> show variables like 'autocommit'; -- 依旧自动提交+---------------+-------+| Variable_name | Value |+---------------+-------+| autocommit | ON |+---------------+-------+1 row in set (0.00 sec)mysql> select * from account; -- 当前表内无数据Empty set (0.00 sec)mysql> begin; -- 开启事务Query OK, 0 rows affected (0.00 sec)mysql> insert into account values (1, '张三', 100); -- 插入记录| Variable_name | Value |+---------------+-------+| autocommit | ON |+---------------+-------+1 row in set (0.00 sec)mysql> begin; --开启事务Query OK, 0 rows affected (0.00 sec)mysql> insert into account values (1, '张三', 100); -- 插入记录Query OK, 1 row affected (0.00 sec)mysql> select * from account; --数据已经存在,但没有commit,此时同时查看终端B+----+--------+--------+| id | name | blance |+----+--------+--------+| 1 | 张三 | 100.00 |+----+--------+--------+1 row in set (0.00 sec)mysql> Aborted -- ctrl + \ 异常终止MySQL--终端Bmysql> select * from account; --终端A崩溃前+----+--------+--------+| id | name | blance |+----+--------+--------+| 1 | 张三 | 100.00 |+----+--------+--------+1 row in set (0.00 sec)mysql> select * from account; --数据自动回滚Empty set (0.00 sec)

非正常演示2 - 证明commit了,客户端崩溃,MySQL数据不会再受影响,已经持久化

--终端 Amysql> show variables like 'autocommit'; -- 依旧自动提交+---------------+-------+| Variable_name | Value |+---------------+-------+| autocommit | ON |+---------------+-------+1 row in set (0.00 sec)mysql> select * from account; -- 当前表内无数据Empty set (0.00 sec)mysql> begin; -- 开启事务Query OK, 0 rows affected (0.00 sec)mysql> insert into account values (1, '张三', 100); -- 插入记录Query OK, 1 row affected (0.00 sec)mysql> commit; --提交事务Query OK, 0 rows affected (0.04 sec)mysql> Aborted -- ctrl + \ 异常终止MySQL--终端 Bmysql> select * from account; --数据存在了,所以commit的作用是将数据持久化到MySQL中+----+--------+--------+| id | name | blance |+----+--------+--------+| 1 | 张三 | 100.00 |+----+--------+--------+1 row in set (0.00 sec)

非正常演示3 - 对比试验。证明begin操作会自动更改提交方式,不会受MySQL是否自动提交影响

-- 终端 Amysql> select *from account; --查看历史数据+----+--------+--------+| id | name | blance |+----+--------+--------+| 1 | 张三 | 100.00 |+----+--------+--------+1 row in set (0.00 sec)mysql> show variables like 'autocommit'; --查看事务提交方式+---------------+-------+| Variable_name | Value |+---------------+-------+| autocommit | ON |+---------------+-------+1 row in set (0.00 sec)mysql> set autocommit=0; --关闭自动提交Query OK, 0 rows affected (0.00 sec)mysql> show variables like 'autocommit'; --查看关闭之后结果+---------------+-------+| Variable_name | Value |+---------------+-------+| autocommit | OFF |+---------------+-------+1 row in set (0.00 sec)mysql> begin; --开启事务Query OK, 0 rows affected (0.00 sec)mysql> insert into account values (2, '李四', 10000); --插入记录Query OK, 1 row affected (0.00 sec)mysql> select *from account; --查看插入记录,同时查看终端B+----+--------+----------+| id | name | blance |+----+--------+----------+| 1 | 张三 | 100.00 || 2 | 李四 | 10000.00 |+----+--------+----------+2 rows in set (0.00 sec)mysql> Aborted --再次异常终止-- 终端Bmysql> select * from account; --终端A崩溃前+----+--------+----------+| id | name | blance |+----+--------+----------+| 1 | 张三 | 100.00 || 2 | 李四 | 10000.00 |+----+--------+----------+2 rows in set (0.00 sec)mysql> select * from account; --终端A崩溃后,自动回滚+----+--------+--------+| id | name | blance |+----+--------+--------+| 1 | 张三 | 100.00 |+----+--------+--------+1 row in set (0.00 sec)

非正常演示4 - 证明单条 SQL 与事务的关系

--实验一-- 终端Amysql> select * from account;+----+--------+--------+| id | name | blance |+----+--------+--------+| 1 | 张三 | 100.00 |+----+--------+--------+1 row in set (0.00 sec)mysql> show variables like 'autocommit';+---------------+-------+| Variable_name | Value |+---------------+-------+| autocommit | ON |+---------------+-------+1 row in set (0.00 sec)mysql> set autocommit=0; --关闭自动提交Query OK, 0 rows affected (0.00 sec)mysql> insert into account values (2, '李四', 10000); --插入记录Query OK, 1 row affected (0.00 sec)mysql> select *from account; --查看结果,已经插入。此时可以在查看终端B+----+--------+----------+| id | name | blance |+----+--------+----------+| 1 | 张三 | 100.00 || 2 | 李四 | 10000.00 |+----+--------+----------+2 rows in set (0.00 sec)mysql> ^DBye --ctrl + \ or ctrl + d,终止终端--终端Bmysql> select * from account; --终端A崩溃前+----+--------+----------+| id | name | blance |+----+--------+----------+| 1 | 张三 | 100.00 || 2 | 李四 | 10000.00 |+----+--------+----------+2 rows in set (0.00 sec)mysql> select * from account; --终端A崩溃后+----+--------+--------+| id | name | blance |+----+--------+--------+| 1 | 张三 | 100.00 |+----+--------+--------+1 row in set (0.00 sec)-- 实验二--终端Amysql> show variables like 'autocommit'; --开启默认提交+---------------+-------+| Variable_name | Value |+---------------+-------+| autocommit | ON |+---------------+-------+1 row in set (0.00 sec)mysql> select * from account;+----+--------+--------+| id | name | blance |+----+--------+--------+| 1 | 张三 | 100.00 |+----+--------+--------+1 row in set (0.00 sec)mysql> insert into account values (2, '李四', 10000);Query OK, 1 row affected (0.01 sec)mysql> select *from account; --数据已经插入+----+--------+----------+| id | name | blance |+----+--------+----------+| 1 | 张三 | 100.00 || 2 | 李四 | 10000.00 |+----+--------+----------+2 rows in set (0.00 sec)mysql> Aborted --异常终止--终端Bmysql> select * from account; --终端A崩溃前+----+--------+----------+| id | name | blance |+----+--------+----------+| 1 | 张三 | 100.00 || 2 | 李四 | 10000.00 |+----+--------+----------+2 rows in set (0.00 sec)mysql> select * from account; --终端A崩溃后,并不影响,已经持久化。autocommit起作用+----+--------+----------+| id | name | blance |+----+--------+----------+| 1 | 张三 | 100.00 || 2 | 李四 | 10000.00 |+----+--------+----------+2 rows in set (0.00 sec)

总结上述实验(重要)证明了事务的 原子性、持久性

在这里插入图片描述

事务操作注意事项

1.如果没有设置保存点,也可以回滚,只能回滚到事务的开始。直接使用 rollback(前提是事务还没有提交)
2.如果一个事务被提交了(commit),则不可以回退(rollback)
3.可以选择回退到哪个保存点
4.InnoDB 支持事务, MyISAM 不支持事务
5.开始事务可以使 start transaction 或者 begin

事务的隔离性(难点)

隔离级别(重要)

在这里插入图片描述
在这里插入图片描述

查看与设置隔离性

在这里插入图片描述
在这里插入图片描述

隔离级别个人总结(很重要!!)

在这里插入图片描述

在这里插入图片描述

为啥隔离性要有四个级别?

在这里插入图片描述

一致性

在这里插入图片描述

隔离性的底层( 难点)

数据库并发的场景有三种:
读-读 :不存在任何问题,也不需要并发控制
读-写 :有线程安全问题,可能会造成事务隔离性问题,可能遇到脏读,幻读,不可重复读
写-写 :有线程安全问题,可能会存在更新丢失问题,比如第一类更新丢失,第二类更新丢失
这里面最重要的环节就是读-写因为其他两种很容易控制安全问题,难点在于读-写

读-写

多版本并发控制( MVCC )是一种用来解决 读-写冲突 的无锁并发控制。
为事务分配单向增长的事务ID,为每个修改保存一个版本,版本与事务ID关联,读操作只读该事务开始前的数据库的快照。
所以 MVCC 可以为数据库解决以下问题(重要)
1.在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能
2.同时还可以解决脏读,幻读,不可重复读等事务隔离问题

理解 MVCC 需要知道三个前提知识:

3个记录隐藏字段
undo 日志
Read View

3个记录隐藏列字段

在这里插入图片描述
在这里插入图片描述

undo 日志(说白了就是 MySQL 中的一段内存缓冲区,用来保存日志数据的就行,比如我的CURD记录)

模拟 MVCC

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这样,我们就有了一个基于链表记录的历史版本链。所谓的回滚,无非就是用历史数据,覆盖当前数据。
上面的一个一个版本,我们可以称之为一个一个的快照。

当前读与快照读

select读取,是读取最新的版本呢?还是读取历史版本?
1.当前读:读取最新的记录,就是当前读。增删改,都叫做当前读,select也有可能当前读,比如:select lock in sharemode(共享锁)
2.快照读:读取历史版本(一般而言),就叫做快照读。

什么决定了,select是当前读,还是快照读呢?隔离级别!

ReadView结构(难点)(看图按创建快照读的时间线思想理解理解)

在这里插入图片描述

通过案例理解快照读的机制

在这里插入图片描述
在这里插入图片描述

RR级别与RC级别的本质区别在于Read view生成机制区别(重要!!!!!!!!!!!!!,这也就是前面总结的为什么RR与RC本质区别的原因,是因为Read view机制不同导致的)

RR级别(mysql使用的)

在这里插入图片描述

RC级别

在这里插入图片描述


点击全文阅读


本文链接:http://zhangshiyu.com/post/44696.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1