📃个人主页:island1314
sql 语句构成的 sql 集合体,这个集合体合在一起共同要完成某种任务。MySQL通过多线程实现存储工作,因此在并发访问场景中,事务确保了数据操作的一致性和可靠性。 事务还规定 不同的客户端看到的数据是不相同的
CURD 操作(创建、更新、读取、删除)可能导致数据不一致的问题。例如,在火车票售票系统中,两个用户同时尝试购买最后一张票,可能造成同一张票被卖出两次的现象

CURD满足什么属性,能解决上述问题?
一个 MySQL 数据库,可不止你一个事务在运行,同一时刻,甚至有大量的请求被包装成 事务,在向 MySQL 服务器发起事务处理请求。而每条事务至少一条 SQL ,最多很多 SQL ;这样如果大 家都访问同样的表数据,在不加保护的情况,就绝对会出现问题。甚至,因为事务由多条 SQL 构成,那 么,也会存在执行到一半出错或者不想再执行的情况,那么已经执行的怎么办呢?
所有,一个完整的事务,绝对不是简单的 sql 集合,还需要满足如下四个属性:
Read uncommitted )、读提交(read committed)、可重复读( repeatable read )和串行化 ( Serializable )刚才说的是多个 sql 在 交叉执行 可能会出现 并发问题 ,进而导致数据不一致,进而导致数据完整性,这都知道。但是对事务的理解不能光站在程序员角度理解,一定要站在数据库使用者角度考虑。
MySQL 编写者设计出来,但是 事务 并不是天然就有的,而是在用一段时间发现要有这个 事务。本质是为了当应用程序访问数据库的时候,事务 能够简化我们的编程模型,不需要我们去考虑各种各样的潜在错误和并发问题。sql 给我,我帮你封装成事务,帮你去运行。可以想一下当我们使用事务时,要么提交,要么回滚,我们不会去考虑网络异常了,服务器宕机了,同时更改一个数据怎么办对吧?这些问题统统不考虑。备注:我们后面把 MySQL 中的一行信息,称为 一行记录
总结
InnoDB 数据库引擎的数据库或表才 支持事务,MyISAM 不支持。mysql> show engines \G;
# ...
*************************** 5. row ***************************
Engine: MyISAM
Support: YES
Comment: MyISAM storage engine
Transactions: NO
XA: NO
Savepoints: NO
# ...
*************************** 7. row ***************************
Engine: InnoDB
Support: DEFAULT
Comment: Supports transactions, row-level locking, and foreign keys
Transactions: YES
XA: YES
Savepoints: YES
# ...
9 rows in set (0.01 sec)查看:
show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+设置:
set autocommit=0; -- 设置为手动提交
set autocommit=1; -- 设置为自动提交linux下 mysql 是一个命令行式的客户端进程。但不仅如此 mysql 客户端还有很多其他客户端如图形化界面版的,还有其他语言版的。
mysql 是一套网络服务进程也就意味着除了本地主机,远端主机也可以连接 myql换句话说 mysql 服务器可能会被多个客户端同时访问
root@VM-8-10-ubuntu:/home/lighthouse# netstat -nltp; # 需要切换成 root 用户
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN 13847/mysqld
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 823/sshd: /usr/sbin 为了更好做事务方面演示,我们将 mysql 的默认隔离级别设置成读未提交,隔离级别后面我们专门具体说。
设置全局事务隔离级别 读为提交
set global transaction isolation level READ UNCOMMITTED;需要重启终端,进行查看,可以看到
mysql> quit
Bye
# 需要重启终端,进行查看
mysql> select @@tx_isolation; # 5.0 版本
mysql> select @@transaction_isolation; # 8.0 版本
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| READ-UNCOMMITTED |
+-------------------------+
REPEATABLE-READ # 默认是这个mysql 客户端,为什么这么做呢,我们主要是为了研究事务,研究事务就要研究多个客户端并发访问的情况。mysql 是有隔离性和隔离级别的,所以目前把隔离级别跳到最低,一个 mysql 做操作,另一个 mysql 就能看到。【案例】:
① 创建一个员工表
create table if not exists account(
id int primary key,
name varchar(50) not null default '',
blance decimal(10,2) not null default 0.0
)ENGINE=InnoDB DEFAULT CHARSET=UTF8;两个 mysql客户端 可以并发访问这张表,准备工作全部就绪,下面我们来做试验!
mysql> show processlist;
+------+-----------------+-----------+--------+---------+---------+------------------------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+------+-----------------+-----------+--------+---------+---------+------------------------+------------------+
| 5 | event_scheduler | localhost | NULL | Daemon | 4481367 | Waiting on empty queue | NULL |
| 2385 | root | localhost | learn3 | Query | 0 | init | show processlist |
| 2386 | root | localhost | learn3 | Sleep | 41 | | NULL |
+------+-----------------+-----------+--------+---------+---------+------------------------+------------------+⭕正常演示 - 事务的开始与回滚
② 启动事务
start transaction; -- 方式一
begin; -- 方式二③ 设置保存点
savepoint s1;④ 回滚到保存点
rollback s1;设置和回滚使用如下:

⑤ 提交事务
commit;⑥ 回滚事务
rollback;直接回滚到最开始!然后数据就全没了。即使是结束事务,回归到单 sql 也是没有的。
非正常操作
下面都是默认开启 自动提交 的,而且 演示 1 和 演示 2 表开始的时候均无数据(empty)
① 非正常演示1: 证明未 commit ,客户端崩溃,MySQL自动会回滚(隔离级别设置为读未提交)
-- 终端A
begin; -- 开启事务
insert into account values (1, '张三', 100); -- 插入记录
mysql> select * from account; -- 数据已经存在,但没有commit,此时同时查看终端B
+----+--------+--------+
| id | name | blance |
+----+--------+--------+
| 1 | 张三 | 100.00 |
+----+--------+--------+让 A 中止,然后再查看A终止前后 B 的数据,如下:
mysql> Aborted -- ctrl + \ 异常终止MySQL
-- 终端B
mysql> select * from account; -- 终端 A 崩溃前
+----+--------+--------+
| id | name | blance |
+----+--------+--------+
| 1 | 张三 | 100.00 |
+----+--------+--------+
1 row in set (0.00 sec)
mysql> select * from account; -- 终端 A 崩溃后
Empty set (0.00 sec)上面我们发现 数据自动进行了回滚
② 非正常演示2 - 证明 commit 了,客户端崩溃,MySQL数据不会在受影响,已经持久化
-- 终端 A
mysql> begin; -- 开启事务
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
-- 终端 A 终止后,切换终端 B 查看数据
mysql> select * from account;
+----+--------+--------+
| id | name | blance |
+----+--------+--------+
| 1 | 张三 | 100.00 |
+----+--------+--------+
1 row in set (0.00 sec)相比于上面这里我们在异常终止终端 A 之前,已经把数据 commit 了,然后再查看 B 发现数据还在,没有回滚 – 持久化保存
commit 的作用是将数据持久化到MySQL中③ 非正常演示3 - 对比演示 1。证明 begin 操作会自动更改提交方式,不会受 MySQL 是否自动提交影响
-- 终端 A,历史是有数据的
mysql> select * from account;
+----+--------+--------+
| id | name | blance |
+----+--------+--------+
| 1 | 张三 | 100.00 |
+----+--------+--------+
-- 事务 A 开始时: 是默认自动提交的查
mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
1 row in set (0.00 sec)
-- 关闭自动提交
mysql> set autocommit=0;
mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | OFF |
+---------------+-------+然后再开始进行事务操作,如下:
mysql> begin; -- 开启事务
mysql> insert into account values (2, '李四', 10000); -- 插入记录
-- 在终端 A 中查看插入记录
mysql> select *from account;
+----+--------+----------+
| id | name | blance |
+----+--------+----------+
| 1 | 张三 | 100.00 |
| 2 | 李四 | 10000.00 |
+----+--------+----------+然后再次让终端 A 异常终止,查看 A 终止前后 B 的数据,如下:
mysql> Aborted -- 再次异常终止 A
-- 切换到终端 B
mysql> 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 是类似的,可以知道 begin 与是否设置 set autocommit 无关,证明正确
④ 非正常演示4 - 证明单条 SQL 与事务的关系【autocommit】
实验一:
-- 终端 A,开始时是有数据的
mysql> select * from account;
+----+--------+--------+
| id | name | blance |
+----+--------+--------+
| 1 | 张三 | 100.00 |
+----+--------+--------+
mysql> set autocommit=0; -- 关闭自动提交
mysql> insert into account values (2, '李四', 10000); -- 插入记录
Query OK, 1 row affected (0.00 sec)
mysql> select *from account; -- 在终端 A 中查看结果,已经插入。此时可以在查看终端B
+----+--------+----------+
| id | name | blance |
+----+--------+----------+
| 1 | 张三 | 100.00 |
| 2 | 李四 | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)
mysql> ^D Bye -- ctrl + \ or ctrl + d,终止终端
-- 终端B
mysql> 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 |
+----+--------+--------+实验二:
-- 终端A
mysql> show variables like 'autocommit'; -- 开启默认提交
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
1 row in set (0.00 sec)
mysql> insert into account values (2, '李四', 10000);
mysql> select * from account; -- 数据已经插入
+----+--------+----------+
| id | name | blance |
+----+--------+----------+
| 1 | 张三 | 100.00 |
| 2 | 李四 | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)
mysql> Aborted -- 异常终止
-- 终端B
mysql> select * from account; -- 终端A崩溃前
+----+--------+----------+
| id | name | blance |
+----+--------+----------+
| 1 | 张三 | 100.00 |
| 2 | 李四 | 10000.00 |
+----+--------+----------+
mysql> select * from account; -- 终端A崩溃后,并不影响,已经持久化。autocommit起作用
+----+--------+----------+
| id | name | blance |
+----+--------+----------+
| 1 | 张三 | 100.00 |
| 2 | 李四 | 10000.00 |
+----+--------+----------+结论:
MySQL 会自动回滚未提交的事务。begin 或者 start transaction,事务便必须要通过 commit 提交,才会持久化,与是否设置 set autocommit 无关 InnoDB 每一条 SQL 语言都默认封装成事务,自动提交。如果关闭自动提交,单条SQL语句也需要手动提交或回滚【select 有特殊情况,因为 MySQL 有 MVCC】从上面的例子,我们能看到事务本身的原子性(回滚),持久性(commit)
注意事项
InnoDB 支持事务, MyISAM 不支持事务start transaction 或 begin