0
点赞
收藏
分享

微信扫一扫

《MySQL——事务》


目录

  • ​​事务的必要性​​
  • ​​MySQL中如何控制事务​​
  • ​​手动开启事务​​
  • ​​事务的四大特征​​
  • ​​事务的四大特征​​
  • ​​事务开启方式​​
  • ​​事务手动提交与手动回滚​​
  • ​​事务的隔离性​​
  • ​​脏读现象​​
  • ​​不可重复读现象​​
  • ​​幻读现象​​
  • ​​串行化​​
  • ​​一些补充​​
  • ​​使用长事务的弊病​​
  • ​​`commit work and chain`的语法是做什么用的?​​
  • ​​怎么查询各个表中的长事务?​​
  • ​​如何避免长事务的出现?​​
  • ​​事务隔离是怎么通过read-view(读视图)实现的?​​
  • ​​参考​​


事务的必要性

mysql中,事务是一个最小的不可分割的工作单元。事务能够保证一个业务的完整性。
比如我们的银行转账:

-- a -> -100
UPDATE user set money = money - 100 WHERE name = 'a';

-- b -> +100
UPDATE user set money = money + 100 WHERE name = 'b';

如果程序中,只有一条语句执行成功了,而另外一条没有执行成功,就会出现前后不一致。就会有人白嫖。
因此,在执行多条有关联 SQL 语句时,事务可能会要求这些 SQL 语句要么同时执行成功,要么就都执行失败。
也就是说事务具有原子性。

MySQL中如何控制事务

1、mysql是默认开启事务的(自动提交)
默认事务开启的作用:

-- 查询事务的自动提交状态
SELECT @@AUTOCOMMIT;
+--------------+
| @@AUTOCOMMIT |
+--------------+
| 1 |
+--------------+

当我们执行一个sql语句时候,效果会立即体现出来,且不能回滚。
回滚举例

CREATE DATABASE bank;

USE bank;

CREATE TABLE user (
id INT PRIMARY KEY,
name VARCHAR(20),
money INT
);

INSERT INTO user VALUES (1, 'a', 1000);

SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
+----+------+-------+

执行插入语句后数据立刻生效,原因是 MySQL 中的事务自动将它提交到了数据库中。那么所谓回滚的意思就是,撤销执行过的所有 SQL 语句,使其回滚到最后一次提交数据时的状态。

在 MySQL 中使用 ROLLBACK 执行回滚:
由于所有执行过的 SQL 语句都已经被提交过了,所以数据并没有发生回滚。

-- 回滚到最后一次提交
ROLLBACK;

SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
+----+------+-------+

将自动提交关闭后,可以数据回滚:

-- 关闭自动提交
SET AUTOCOMMIT = 0;

-- 查询自动提交状态
SELECT @@AUTOCOMMIT;
+--------------+
| @@AUTOCOMMIT |
+--------------+
| 0 |
+--------------+

现在我们测试一下:

INSERT INTO user VALUES (2, 'b', 1000);

-- 关闭 AUTOCOMMIT 后,数据的变化是在一张虚拟的临时数据表中展示,
-- 发生变化的数据并没有真正插入到数据表中。
SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
| 2 | b | 1000 |
+----+------+-------+

-- 数据表中的真实数据其实还是:
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
+----+------+-------+

-- 由于数据还没有真正提交,可以使用回滚
ROLLBACK;

-- 再次查询
SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
+----+------+-------+

可以使用​​COMMIT​​将虚拟的数据真正提交到数据库中:

INSERT INTO user VALUES (2, 'b', 1000);
-- 手动提交数据(持久性),
-- 将数据真正提交到数据库中,执行后不能再回滚提交过的数据。
COMMIT;

-- 提交后测试回滚
ROLLBACK;

-- 再次查询(回滚无效了)
SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
| 2 | b | 1000 |
+----+------+-------+

总结

1、查看自动提交状态: ​​select @@AUTOCOMMIT;​​​ 2、设置自动提交状态: ​​set AUTOCOMMIT = 0;​​ 3、手动提交: 在 @@AUTOCOMMIT = 0 时,可以使用​​commit​​ 命令提交事务
4、事务回滚: 在 @@AUTOCOMMIT = 0 时,可以使用​​rollback​​ 命令回滚事务

事务给我们提供了一个可以反悔的机会,假设在转账时发生了意外,就可以使用 ​​ROLLBACK​​ 回滚到最后一次提交的状态。假设数据没有发生意外,这时可以手动将数据​​COMMIT​​ 到数据表中。

手动开启事务

可以使用​​BEGIN​​​ 或者 ​​START TRANSACTION​​ 手动开启一个事务。

-- 使用 BEGIN 或者 START TRANSACTION 手动开启一个事务
-- START TRANSACTION;
BEGIN;
UPDATE user set money = money - 100 WHERE name = 'a';
UPDATE user set money = money + 100 WHERE name = 'b';

-- 由于手动开启的事务没有开启自动提交,
-- 此时发生变化的数据仍然是被保存在一张临时表中。
SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 900 |
| 2 | b | 1100 |
+----+------+-------+

-- 测试回滚
ROLLBACK;

SELECT * FROM user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
| 2 | b | 1000 |
+----+------+-------+

当然事务开启之后,使用commit提交后就不能回滚了。

事务的四大特征

事务的四大特征

A 原子性:事务是最小的单位,不可以分割
C 一致性:事务要求同一事务中的sql语句,必须要保证同时成功或者同时失败
I 隔离性:事务1 和事务2 之间是具有隔离性的
D 持久性:事务一旦结束(commit or rollback),就不可以返回

事务开启方式

1、修改默认提交 set autocommit = 0;
2、begin
3、start transaction

事务手动提交与手动回滚

手动提交:​​commit​​​ 手动回滚:​​rollback​

事务的隔离性

事务的隔离性:

1、read uncommitted; 读未提交的
2、read committed; 读已经提交的
3、repeatable read; 可以重复读
4、serializable; 串行化

脏读现象

在​​read uncommitted​​​的隔离级别下:
脏读:一个事务读到了另外有一个事务没有提交的数据
实际开发不允许脏读出现。
如果有两个事务 a、b
a事务对数据进行操作,在操作的过程中,事务并没有被提交,但是b可以看见a操作的结果。b看到转账到了,然后就不管了。后面a进行rollback操作,钱又回去了,完成白嫖。

不可重复读现象

在​​read committed​​​的隔离级别下:
小王一开始开启了一个事务,然后提交了几个数据,然后出去抽烟。
在他抽烟的时候,小明在其他电脑上开启了一个事务,然后对那个表提交了一个数据。
小王烟抽完了,然后统计表中数据,发现不对劲。前后不一致了。

幻读现象

在​​repeatable read;​​​的隔离级别下:
事务a和事务b同时操作一张表,事务a提交的数据也不能被事务b读到,就可以造成幻读。
可以观察如下步骤:
小明 在杭州 开启一个事务;
小王 在北京 开启一个事务;
小明 对table进行插入数据操作,然后commit;然后查看表,发现操作成功
小王在对table进行插入之前也查看表,然而并没有小明插入的数据,于是乎他插入了同样的一条数据,数据库报错。
小王很是疑惑,这就是幻读现象。

串行化

在​​serializable​​​的隔离级别下:
当user表被事务a操作的时候,事务b里面的写操作是不可以进行的,会进入排队状态(串行化)。
“读-读”在串行化隔离级别允许并发。
直到事务a结束之后,事务b的写入操作才会执行。
串行化的问题是性能特差。
一般来说,隔离级别越高,性能越差。
MySQL默认隔离级别是:​​​repeatable read;​

一些补充

使用长事务的弊病

存储空间上来说:
长事务意味着系统里面会存在很老的事务视图。由于这些事务随时可能访问数据库里面的任何数据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间。
长事务还占用锁资源,也可能拖垮整个库。

​commit work and chain​​的语法是做什么用的?

提交上一个事务,并且再开启一个新的事务。它的功能等效于:​​commit + begin​​。

怎么查询各个表中的长事务?

这个表中记录了所有正在运行的事务信息,里面有事务的开始时间。可以从这里看出哪些事务运行的时间比较长。

select * from information_schema.innodb_trx;

如何避免长事务的出现?

数据库方面:



a.设置autocommit=1,不要设置为0。
b.写脚本监控information_schemal.innodb_trx表中数据内容,发现长事务,kill掉它。
c.配置SQL语句所能执行的最大运行时间,如果查过最大运行时间后,中断这个事务


SQL语句方面:

设置回滚表空单独存放,便于回收表空间

业务代码方面:

1、检查业务逻辑代码,能拆分为小事务的不要用大事务。
2、检查代码,把没有必要的select语句被事务包裹的情况去掉

事务隔离是怎么通过read-view(读视图)实现的?

每一行数有多个版本,当我们要去读取数据的时候,要判断这个数据的版本号,对当前事务而言,是否可见,如果不可见,则要根据回滚日志计算得到上一个版本。如果上一个版本也不符合要求,则要找到再上一个版本,
直到找到对应正确的数据版本。


举报

相关推荐

事务——MySQL

MySQL【事务】

【MySQL】事务

mysql事务

《MySQL》事务

Mysql 事务

Mysql事务

0 条评论