文章标题:
MySQL事务:全面理解与解析
一、CURD无控制会出现什么状况
(1)由于MySQL中存储着数据,所以极有可能会被多个客户端访问,这样一来,mysqld或许会同时接收到多个关于CURD的请求。(2)而且MySQL内部采用多线程来开展数据存储等相关工作,所以必然存在数据并发访问的情形,这会引发多个请求并发时可能出现的异常结果。
举个例子,同行转账时,本应是我账户减100,你的账户加100,但要是我账户减100的过程中出现网络或数据库问题,没来得及给你账户加100,就会出现中间状态,也就是我账户少了100可你账户没增加。这时候就需要回滚操作,让我的账户恢复原先的金额,仿佛没进行过这次转账。高并发场景下很容易碰到这类问题,所以MySQL得有应对此类问题的办法。
二、CURD需满足哪些属性来解决上述问题?
- 买票这类过程得是原子性的,要么成功抢到票,要么没抢到,不能出现中间状态后还保留部分结果。
- 买票时彼此之间不能相互干扰,我买票的操作不应该影响你买票的流程,得相互隔离。
- 买完票的结果得长久有效,购买成功的状态必须持久保存。
- 买票前后的状态得是明确的,买之前就是没买的状态,买之后就是已买的状态,不能有不确定的状况。
三、什么是事务?
事务是由一组DML语句构成的,这些语句在逻辑上相互关联(单独一条语句没什么意义,比如转账至少得有两条SQL语句,我账户减100和你账户加100,合起来才有转账的逻辑,所以看待事务要站在MySQL上层,它是完成一个有业务含义的动作的多条SQL语句集合,这组DML语句要么全部成功执行,要么全部失败,是一个整体。MySQL提供机制来达成这样的效果,并且事务规定不同客户端看到的数据不一样。
事务是要做的事或者已经做的事,主要用于处理操作量大、复杂度高的数据。 打个比方,你毕业了,学校教务系统后台MySQL里不需要你的数据了,要删除你的所有信息,包括基本信息、各科成绩、在校表现等,这就需要多条MySQL语句共同完成,这些操作合起来就构成一个事务。
正如前面所说,一个MySQL数据库里,不止你一个事务在运行,同一时刻可能有大量请求被包装成事务向MySQL服务器发起事务处理请求。每条事务至少有一条SQL,最多有很多SQL,如果大家都访问同样的表数据,不加保护肯定出问题。 而且,因为事务由多条SQL构成,可能会出现执行到一半出错或者不想再执行的情况,那已经执行的部分怎么办呢?
所以,完整的事务不只是简单的SQL集合,还得满足以下四个属性:
1、原子性:一个事务中的所有操作,要么全部完成,要么全部不完成,不会停在中间环节。 事务执行过程中出了错,会回滚到事务开始前的状态,就像这个事务没执行过一样。
2、一致性:在事务开始之前和结束以后,数据库的完整性不会被破坏。(一种状态变为另一种状态结果是可预期的) 这意味着写入的数据得完全符合所有预设规则,包含数据的精确度、串联性以及后续数据库能自发完成预定工作。
3、隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。 事务隔离有不同级别,像读未提交、读提交、可重复读和串行化。
4、持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
MySQL只要保证了原子性、一致性和持久性,就能在一定程度上实现隔离性,但还需要上层用户配合。
上面这四个属性,简称为ACID。
原子性(Atomicity,或称不可分割性)
一致性(Consistency)
隔离性(Isolation,又称独立性)
持久性(Durability)
MySQL得给不同客户端处理不同的事务请求,所以运行时自身内部必然有大量事务,它得把事务按先描述后组织的形式管理起来,所以MySQL会把这些事务打包描述成对象,放到事务执行列表里,帮忙解决执行事务时可能出现的一系列问题
四、为何会有事务
事务是MySQL开发者设计出来的(不是天生就有的,而是应用层有需求才设计的),本质是为了应用程序访问数据库时,能简化编程模型,不用考虑各种潜在错误和并发问题(你只需要提出你的需求,别的由它处理)。想想用事务时,要么提交,要么回滚,不用操心网络异常、服务器宕机、同时更改一个数据等问题对吧?所以事务本质是为应用层服务的,不是伴随数据库系统天生就有的。
备注:后面把MySQL中的一行信息称作一行记录
五、事务的版本支持
在MySQL中,只有使用InnoDB数据库引擎的数据库或表才支持事务,MyISAM不支持。
查看数据库引擎:
mysql> show engines \G -- 行显示
*************************** 1. row ***************************
Engine: InnoDB -- 引擎名称
Support: DEFAULT -- 默认引擎
Comment: Supports transactions, row-level locking, and foreign keys--支持事务、行级锁,外键
Transactions: YES -- 支持事务
XA: YES
Savepoints: YES -- 支持事务保存点
*************************** 2. row ***************************
Engine: MRG_MYISAM
Support: YES
Comment: Collection of identical MyISAM tables
Transactions: NO
XA: NO
Savepoints: NO
*************************** 3. row ***************************
Engine: MEMORY --内存引擎
Support: YES
Comment: Hash based, stored in memory, useful for temporary tables
Transactions: NO
XA: NO
Savepoints: NO
*************************** 4. row ***************************
Engine: BLACKHOLE
Support: YES
Comment: /dev/null storage engine (anything you write to it disappears)
Transactions: NO
XA: NO
Savepoints: NO
*************************** 5. row ***************************
Engine: MyISAM
Support: YES
Comment: MyISAM storage engine
Transactions: NO -- MyISAM不支持事务
XA: NO
Savepoints: NO
*************************** 6. row ***************************
Engine: CSV
Support: YES
Comment: CSV storage engine
Transactions: NO
XA: NO
Savepoints: NO
*************************** 7. row ***************************
Engine: ARCHIVE
Support: YES
Comment: Archive storage engine
Transactions: NO
XA: NO
Savepoints: NO
*************************** 8. row ***************************
Engine: PERFORMANCE_SCHEMA
Support: YES
Comment: Performance Schema
Transactions: NO
XA: NO
Savepoints: NO
*************************** 9. row ***************************
Engine: FEDERATED
Support: NO
Comment: Federated MySQL storage engine
Transactions: NULL
XA: NULL
Savepoints: NULL
9 rows in set (0.00 sec)
六、事务的提交方式
事务的提交方式常见有两种:自动提交、手动提交
查看事务提交方式:
show variables like 'autocommit';
用 SET 来改变 MySQL 的自动提交模式:
SET AUTOCOMMIT=0; #SET AUTOCOMMIT=0 禁止自动提交
mysql> SET AUTOCOMMIT=1; #SET AUTOCOMMIT=1 开启自动提交
七、事务常见操作方式
简单银行用户表
## Centos 7 云服务器,默认开启3306 mysqld服务
netstat -nltp
为了便于演示,我们将mysql的默认隔离级别设置成读未提交
set global transaction isolation level READ UNCOMMITTED;
设置了却没有用 ,因为需要重启终端才可以
select @@tx_isolation;
创建测试表
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;
7.1 正常演示 - 证明事务的开始与(定向)回滚
mysql> show variables like 'autocommit'; -- 查看事务是否自动提交。我们故意设置成自动提交,看看该选项是否影响begin 从这一行往后所有的语句都属于这个事务
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
1 row in set (0.00 sec)
mysql> start transaction; -- 开始一个事务begin也可以,推荐begin
Query OK, 0 rows affected (0.00 sec)
mysql> savepoint save1; -- 创建一个保存点save1(根据需求设置保存点)
Query 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; -- 创建一个保存点save2
Query 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; -- 回滚到保存点save2(定向回滚)
Query 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)
commit;--就是把该事务给提交了 无法回滚
但是一般我们很少手动rollback,事务大多数都是为了非正常情况
7.2 非正常演示1 - 证明未commit,客户端崩溃,MySQL自动会回滚(隔离级别设置为读未提交)
-- 终端A
mysql> select * from account; -- 当前表内无数据
Empty set (0.00 sec)
mysql> show variables like 'autocommit'; -- 依旧自动提交
+---------------+-------+
| 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
--终端B
mysql> 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)
7.3 非正常演示2 - 证明commit了,客户端崩溃,MySQL数据不会在受影响,已经持久化
```sql
--终端 A
mysql> 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
--终端 B
mysql> select * from account; --数据存在了,所以commit的作用是将数据持久化到MySQL中
+----+--------+--------+
| id | name | blance |
+----+--------+--------+
| 1 | 张三 | 100.00 |
+----+
文章整理自互联网,只做测试使用。发布者:Lomu,转转请注明出处:https://www.it1024doc.com/12523.html