事务概述
事务是一个或一组sql语句组成的一个执行单元,这个执行单元要么全部执行成功,要么全部执行失败。用于保证数据的完整性。
如账户转账,张三给李四转500,对应的sql语句应该是update张三的 余额-500,并且update李四的余额+500,如果先更新完张三的数据,然后出现了异常,导致李四的余额修改不了,那么就出现数据上的问题了,应该使用事务来解决这一问题,当中间出现异常后回滚,让张三的update失效;如果没有出现异常就提交事务。
通过SHOW ENGINES;查看mysql中支持的存储引擎,注意只有innodb支持事务
事务的创建分为:
- 隐式事务:事务没有明显的开启和结束的标记,如insert、update、delete语句执行时在默认情况下就是一个事务,因为默认是自动提交事务的,所有执行完后就立马自动提交事务了
- 显式事务:事务具有明显的开启和结束的标记,前提是需要先设置自动提交事务为禁用,手动的提交事务或回滚事务
设置方法为:
select @@autocommit; #如果为1自动提交事务,为0需要手动提交事务
set autocommit=0; #设置不自动提交事务
该设置只是针对当前会话有效,不是针对全局的。
使用事务的步骤:
- 步骤一:开启事务
set autocommit=0;
start transaction; 开启事务,可选的,不写也可以,因为关闭了自动提交事务后,默认就开启了事务,如果没有关闭就需要开启。
- 步骤二:编写事务中的sql语句
- 步骤三:结束事务
commit;提交事务、rollback;回滚事务
事务的四大特性
事务的ACID属性为:
- 原子性(Atomicity):原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
- 一致性(Consistency):事务完成后,必须使所有的数据都保持一致状态,如转账的例子中双方的余额总和总是相同且正确的。
- 隔离性(lsolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。
- 持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。通过事务日志来保证,包括重做日志和回滚日志
并发事务问题
对于同时运行的多个事务,当这些事务访问数据库中相同的数据时,如果没有采取必要的隔离机制,就会导致各种并发问题:
| 问题 | 描述 |
|---|---|
| 脏写 | 一个事务修改了另一个事务未提交的数据,该事务可能被回滚。 |
| 脏读 | 一个事务读到另外一个事务还没有提交的数据。 |
| 不可重复读 | 一个事务先后读取同一条记录,但两次读取的数据不同,称之为不可重复读。 |
| 幻读 | 一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又 发现这行数据已经存在,好像出现了"幻影”。新插入的行为幻影记录 |
- 脏写
当事务二修改一条数据还没提交时,如果事务一此时也修改了相同的数据,且立即提交了,此时事务二如果回滚,那么事务一的更新将不复存在,这种现象就为脏写。

- 脏读
如下图,当事务二修改了id为1的数据后,事务一查询id为1的数据,查询的结果是修改后的数据,此时事务二可能会回滚事务,导致事务一查询的数据是一个脏数据,临时的数据。

- 不可重复读
如下图,事务一中,两次查询相同id的结果是不同的,叫做不可重复读,侧重于在同一个事务中。

- 幻读
如下图,当事务一第一次查询到没有id为1的数据时,准备插入前,事务二恰好插入了id为1的数据,且提交了事务,因为id是主键所以事务一会插入失败,此时因为解决了不可重复读的问题,事务一再次查询id为1的数据,还是查不到,然后就是插不进去,导致幻读。侧重于插入记录。

严重程度:脏写>脏读>不可重复读>幻读
脏写在所有隔离级别中都不会发生,如果某个事务想修改另一个事务还未提交的修改,就必须阻塞,等待那个事务结束。
事务的隔离级别
用于解决并发事务问题的。数据库提供的四种隔离级别:
| 隔离级别 | 描述 |
READ UNCOMMITTED (读未提交数据) | 允许事务读取未被其他事务提交的变更,约束最小,速度最快 |
READ COMMITTED (读已提交数据) | 只允许事务读取被其他事务提交的变更,可以避免脏读 |
REPEATABLE READ (可重复读) | 确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,即使其它事务对以前的读取记录更新了,依然能读取到更新前的数据,可以避免脏读、不可重复读 |
| SERIALIZABLE(串行化) | 确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作,并发问题都能解决,性能低下 |
mysql默认是REPEATABLE READ,可以通过下面的命令查询当前的隔离级别
select @@transaction_isolation通过下面的命令改变隔离级别,Session是会话级别的改变,Global是全局改变
set [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE]演示:
用两个命令行打开不同的会话
1.演示在read uncommitted下的读脏数据,如下

2.演示read committed

如上发现read committed解决了读脏数据,但是在第3、4步的查询发现,在同一个事务中连续两次查询的同一列数据居然不相同了,出现不可重复读的问题。
3.演示repeatable read

不可重复读解决了,但是幻读还存在,如下

通过手动加锁,可以解决幻读问题
4.演示serializable,解决幻读问题

该隔离级别性能比较低,但是涉及到了很多数据库锁的概念,可以深入了解一下
总结
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| Read Committed | √ | √ | √ |
| Read Uncommitted | × | √ | √ |
| Repeatable Read | × | × | √ |
| Serializable | × | × | × |
注意:事务隔离级别越高,数据越安全,但是性能越低。