[MySQL] 事务管理(一) 事务的基本概念
1.为什么需要事务
1.1 CURD不加控制会有什么问题
我认为要解决上面的问题,CURD必须要有以下的属性
买票的过程必须是原子的
买票相互之间不可以收到影响
买完票需要永久有效
买前,买后的状态必须是确定的
2. 什么是事务?
事务就是一组DML语句组成,这些语句在逻辑上存在相关性,这一组DML语句要么全部成功,要么全部失败,是一个整体。MySQL提供一种机制,保证我们达到这样的效果。事务还规定不同的客户端看到的数据是不相同的。
现在看概念我认为事务就是一组的SQL语句
2.1 事务的属性(ACID)
- 原子性:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成
- 一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏
- 隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(ReadS uncommitted )、读提交(readcommitted)、可重复读(erializable )repeatableread)和串行化
- 持久性:事务处理结束后,对数据的修改就是永久的
3.为什么会出现事务?
本质上就是MySQL是为了简化我们的操作而产生的,那么当数据出现我们上面说的问题的时候,难道需要我们程序员去维护他们的属性吗?所以MySQL就出现了事务来帮助程序员,让我们不需要手动的管理。
4.事务的版本支持
在MySQL中只有innodb数据库引擎才支持事务,MyISAM不支持
查看数据库引擎的操作:
show engines \G
5.事务的提交方式
- 自动提交
- 手动提交
5.1 查看事务的提交方式
show variables like 'autocommit';
5.2 更改事务的提交方式
set autocommit=0;禁止自动提交
set autocommit=1;//设置自动提交
6. 事务常见操作
为了便于演示,我们将mysql的默认隔离级别设置成读未提交。
6.1 事务的开始与回滚
启动事务:两种都可以
begin;
start transaction;
创建一个保存点
savepoint save1
回滚
rollback save1;
演示:
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;
Query OK, 0 rows affected (0.01 sec)
插入数据的时候,在右侧即可查询到,但是我们回滚之后就一条记录也没有了;
注意:如果没有记录rollback点,输入回滚,会直接清除所有的事务操作
有几个客户端崩溃的场景就不演示了直接结论:
- 隔离级别为读为提交的时候,客户端崩溃,没有commit,MySQL会自动回滚到最开始
- 客户端崩溃,有commit,MySQL的数据不会受到影响,已经持久化了。
结论
- 只要输入begin,事务必须通过commit提交,才会形成持久化,与设置是否提交没有关系
- 事务可以手动回滚,当事务异常退出MySQL会自动回滚
- 对于InnoDB 每一条SQL 语言都默认封装成事务,自动提交。
7.事务隔离级别
7.1 理解隔离级别
数据库中,为了保证事务执行过程中尽量不受干扰,就有了隔离性,但是为了完成不同的情况下的任务,mysql允许事务收到不同程度的干扰,这就出现了隔离级别。
7.2 隔离级别
- 读未提交 Read Uncommitted:所有的事务都可以看到别的没有提交的事务的执行,等同于没有隔离性。他会出现很多的并发问题:脏读,幻读,不可重复读的问题。
- 读提交 【Read Committed:一个事务只能看到别的事务提交之后的结果,但是他也是有问题的:会引起不可重复读,即一个事务执行时,如果多次 select, 可能得到不同的结果。
- 可重复读 Repeatable Read:*这是MySQL的默认隔离级别,他保证同一个事务在读取时,会看到同样的数据,*同样他的问题是:幻读
- 串行化 Serializable:这是事务的最高隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决了幻读的问题。它在每个读的数据行上面加上共享锁。(这种级别过于极端,一般很少使用)
7.3 查看与设置隔离性
我们先来熟悉更改查看隔离性的操作,然后再继续理解隔离级别
7.3.1 查看全局隔离级别和会话隔离级别
select @@global.tx_isolation;
select @@session.tx_isolation;
select @@tx.isolation;
7.3.2设置全局隔离级别和会话隔离级别
这里设置会话的隔离性不会影响全局的隔离属性;
设置全局的隔离性会影响会话的隔离性。
注意:一般我们设置完隔离性之后都要重新启动客户端
7.4 读未提交(Read Uncommitted)
前提:启动两个终端,一个进行CURD,另一个负责读取,把隔离性设置为读未提交。
读到终端A更新但是未commit的数据。
这里就是脏读:一个事务在执行中,读取到正在执行中的事务更新但是并没有提交的数据,
7.5 读提交 【Read Committed】
切换到读提交
当我们启动事务之后,更新了张三的blance之后,查看表,发现数据并没有改变,但是当我们提交之后,数据就被改变了。
也就说:同样的读取,在不同时间段中,读取到的内容不同,这种现象就叫做(不可重复读)
那么这是问题吗?
这是有问题的,一个事务读取的数据,一定要是最新的值吗?这是根据实际情况来决定的。
举个例子:
我们是大三的学生,那么我们的学长学姐的找工作情况,我们一般都是可以知道的(学校发出来给做参考),但是我们学长学姐一般情况下他们不会知道我们的就业情况,
7.6 可重复读【Repeatable Read】
可以看到,在A事务更新之后,未提交,事务B中看不到更新的信息。 在A提交之后,还是看不到更新的信息,
只有当B事务也提交之后才能看到相应的更新结果。这就是可重复读
如果是inset操作呢?
在我的机器上,现象和update一样,但是,一般的数据库在可重复读情况的时候,无法屏蔽其他事务insert的数据(因为隔离性实现是对数据加锁完成的,而insert待插入的数据因为并不存在,那么一般加锁无法屏蔽这类问题)会造成虽然大部分内容是可重复读的,但是insert的数据在可重复读情况被读取出来,导致多次查找时,会多查找出来新的记录,就如同产生了幻觉。这种现象,叫做幻读(phantom
read)。很明显,MySQL在RR级别的时候,是解决了幻读问题的
7.7 串行化【serializable】
对所有操作全部加锁,进行串行化,不会有问题,但是只要串行化,效率很低,几乎完全不会被采用
A在执行更新的时候,没有提交,这个时候B端就会被堵塞,直到A端口进行提交,B才会执行结果。
7.8 隔离级别总结
7.9 一致性
- 事务的执行结果,必须使数据库从一个一致状态,改变到另一个一致性的状态。当事务成功提交,数据库处于一致性的状态,但是当事务没有提交被迫中断时,未完成的事务对数据库的修改已经被填入到数据库中,这个时候数据库就处于一种不一致的状态,所以事务的一致性是由原子性来保证的。
- 其实一致性和用户的业务逻辑强相关,一般MySQL提供技术支持,但是一致性还是要用户业务逻辑做支撑,也就是,一致性,是由用户决定的。
- 而技术上,通过AID保证C