1. 什么是事务
事务是一组DML
语句组成的,这些语句在逻辑上存在相关性,这一组DML
语句要么全部成功,要么全部失败,是一个整体。
是实际应用的角度,例如要删除某个学生的信息(姓名、学院、班级、学号、成绩…),在程序员角度就是多条
sql
语句,但是在上层,这就是一个封装起来的删除操作,这些sql
语句被封装成了事务。
但是在mysql
中,不止有一个事务在运行,在同一时刻,可能会有大量的请求被包装成事务,每条事务至少包含一条sql
语句,如果大量的sql
要访问同样的数据,这个数据没有被保护,肯定会出问题。
所有为了让事务更好的执行mysql
会帮我们维护四大属性(ACID):
- 原子性(Atomicity):一个事务的所有操作要么全部完成,要么全部失败,不会结束在中间某个环节。如果在执行过程发生错误,就会被回滚(rollback),要么自动回滚,要么用户手动回滚,回到事务开始前的状态。
- **持久性(Durability):**事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
- **隔离性(Isolation):**防止多个由于事务并发执行,而交替修改同一部分的数据,从而导致读写不一致问题。事务隔离有不同的隔离级别
- **一致性(Consistency):**事务在开始之前和结束之后,数据库的完整性没有被破坏,结果是可预期的。做到前三点,就能保证一致性。
所以所谓的事务就是在ACID
四大属性的加持之下,有一条或者多条sql
语句构成的称之为事务。
mysql
要帮不同的客户端处理不同的请求,这就决定了mysql
在运行期间内部会存在着大量的事务,这些事务也要被管理起来,即先描述再组织,来了一批sql
,将这sql
打包成事务对象,然后放入事务执行列表当中。
为什么要有事务?
事务并不是天然就有的,是编写者设计出来的,本质上是应用程序访问数据库的时候,事务能够简化上层的编程模型,开发者不用去考虑各种的潜在问题。
所以事务的本质是为了应用层服务的,而不是天生就有的。
2. 事务版本支持
不是所有的存储引擎都支持事务
show engines \G
3. 事务提交方式
事务常见提交方式:
- 自动提交
- 手动提交
mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
默认自动提交。
通过set
修改是否自动提交:
mysql> set autocommit=0; #禁止自动提交
Query OK, 0 rows affected (0.01 sec)
mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | OFF |
+---------------+-------+
1 row in set (0.00 sec)
mysql> 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.00 sec)
4. 事务常见操作方式
4.1 准备工作
将mysql
的默认隔离级别设置成读未提交:
mysql> set global transaction isolation level read uncommitted;
设置完毕之后重启一下客户端才会生效
mysql> select @@tx_isolation;
+------------------+
| @@tx_isolation |
+------------------+
| READ-UNCOMMITTED |
+------------------+
创建测试表:
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;
4.2 事务的开始、回滚、提交
4.21 正常情况
开启事务:
start transaction;
#或者
begin;
设置保存点:
mysql> insert into account values (1, '张三',1111.1);
Query OK, 1 row affected (0.00 sec)
mysql> savepoint s2;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into account values (2, '李四', 2222.2);
Query OK, 1 row affected (0.00 sec)
mysql> savepoint s3;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into account values (3, '王五', 3333.3);
Query OK, 1 row affected (0.00 sec)
数据回滚:
mysql> rollback to s3;
Query OK, 0 rows affected (0.00 sec)
mysql> rollback to s2;
Query OK, 0 rows affected (0.00 sec)
也可也直接全部回滚
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> insert into account values (4, '赵六', 4444.4); Query OK, 1 row affected (0.00 sec) mysql> insert into account values (5, '田七', 5555.5); Query OK, 1 row affected (0.00 sec) mysql> rollback; Query OK, 0 rows affected (0.00 sec)
结束(提交)事务:
mysql> commit;
事务提交之后就无法再回滚了
4.22 非正常情况
事务运行期间,客户端直接奔溃了
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into account values (2, '李四', 2222.2);
Query OK, 1 row affected (0.00 sec)
mysql> insert into account values (3, '王五', 3333.3);
Query OK, 1 row affected (0.00 sec)
mysql> insert into account values (4, '赵六', 4444.4);
Query OK, 1 row affected (0.00 sec)
mysql> Aborted # ctrl + \
事务中间异常(异常退出或者客户端直接关闭)了,mysql
自动回滚。
默认情况事务的自动提交是自动打开的
mysql> show variables like 'autocommit'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ 1 row in set (0.01 sec)
当我们手都
begin
启动事务的时候,异常的时候,会自动回滚,和这个自动提交并没有关系
即手动开启的事务,一定要手动提交
4.3 单条sql与事务的关系
单条sql
其实就会被看作一个事务,如果开启了自动提交(默认是这个),那无需手动;如果自动提交关闭了,则需要手动的commit
提交。
以上的例子验证的是事务的原子性(rollback)和持久性(commit)
5. 事务隔离级别
5.1 什么是隔离性
- 还是那句话
mysql
作为服务器端,可能会被多个客户端进行并发访问。 - 事务由一条或者多条
sql
构成,意味着事务会有执行前、执行中、执行后的阶段,执行的时候出现问题可以随时回滚;所以事务对用户表现的特性就是原子的,只有执行前和执行后。 - 站在技术角度,事务有执行过程,也就表明事务各种执行
sql
的时候,会出现互相影响的情况。 - 为保证事务执行的过程尽量不受干扰,就要有隔离性。
- 允许事务受不同程度的干扰,就要有隔离级别。
5.2 隔离级别
- **读未提交(read uncommitted):**所有的事务都可以看到其他事务没有提交的执行结果,这种相当于没有隔离(上面的示例就是这种隔离级别)
- **读提交(read committed):**大多数数据库默认的隔离级别,一个事务只能看到其他事务提交之后的结果。
每次读都更新read_view
- **可重复读(repeatable read):**这是
mysql
的默认隔离级别,当各种事务都提交之后,才能看到结果
只更新一次read_view
- **串行化(serializable):**最高的隔离级别,强制事务排序,让事务不可能出现冲突
5.3 事务隔离级别的设置与查看
查看隔离级别:
mysql> select @@global.tx_isolation; # 全局隔离级别
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| READ-UNCOMMITTED |
+-----------------------+
1 row in set, 1 warning (0.00 sec)
mysql> select @@session.tx_isolation; # 当前会话隔离级别(默认继承全局)
+------------------------+
| @@session.tx_isolation |
+------------------------+
| READ-UNCOMMITTED |
+------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> select @@tx_isolation; # 同上
+------------------+
| @@tx_isolation |
+------------------+
| READ-UNCOMMITTED |
+------------------+
1 row in set, 1 warning (0.00 sec)
设置隔离级别:
语法:
set [session | global] transaction isolation level {read uncommitted | read committed | repeatable read | serializable}
mysql> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
只有当前会话受到影响,其他客户端不受影响。
全局和当前会话隔离级别不一样,采用当前会话的隔离级别。
在会话中重新设置全局隔离级别,需要重启客户端才能生效
尽量保持隔离级别一致(采用全局作为初始值)
5.4 隔离级别
5.41 读未提交
一个事务在执行中,读到另一个执行中事务的更新(或其他操作)但是未commit的数据,这种现象叫做脏读(dirty read)。
5.42 读提交
一个事务未commit
,另一个事务读不到,commit
之后才可以读到
但这样就会导致另一个事务执行期间,同一个select
语句可能会查询到不同的结果,这种现象叫做不可重复读(non repeatable read)
一个事务提交了,应该让其他事务看到,但事务是原子的,不应该在其他事务运行时看到
5.43 可重复读
一般的数据库在可重复读情况的时候,无法屏蔽其他事务insert
的数据,因为隔离性是通过加锁完成的,而insert
插入的数据并不存在,所以无法屏蔽这类问题。
虽然大部分内容是可重复读的,但是insert
的数据在可重复读情况被读取出来,导致多次查找时,会多查找出来新的记录。这种现象,叫做幻读(phantom read)。
mysql
在RR级别的时候,解决了幻读问题
5.44 串行化
串行化将所有操作加锁,事务一个一个执行,虽然很安全,但是效率很低
删除的时候,由于还有事务在执行,所有会删除失败。如果这个事务commit
,则会轮到。
5.55 总结
- 隔离级别越高,安全性越高,但是数据库的并发性能也就越低。具体看实际需求。
- 不可重复读的重点是修改和删除
幻读的重点在与新增 mysql
的默认隔离级别是可重复读(RR),一般情况不要修改
隔离级别 | 脏读 | 不可重复读 | 幻读 | 加锁读 |
---|---|---|---|---|
读未提交 | √ | √ | √ | 不加锁 |
读提交 | × | × | √ | 不加锁 |
可重复读 | × | × | × | 不加锁 |
串行化 | × | × | × | 加锁 |
×
会发生该问题;√
不会发生该问题
6. 一致性
一致性并不是技术层面的概念,而是使用层面的概念。
- 事务执行的结果,必须使数据库从一个一致性状态,变到另一个一致性状态。当数据库只包含事务成功提交的 结果时,数据库处于一致性状态。如果系统运行发生中断,某个事务尚未完成而被迫中断,而改未完成的事务 对数据库所做的修改已被写入数据库,此时数据库就处于一种不正确(不一致)的状态。因此一致性是通过原子性来保证的。
- 其实一致性和用户的业务逻辑强相关,一般MySQL提供技术支持,但是一致性还是要用户业务逻辑做支撑,也就是,一致性,是由用户决定的。
- 而技术上,通过AID保证C