一、数据库事务定义
数据库事务是一组数据库操作,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。
二、事务的四大特性
1、原子性:事务是最小的执行单位,不允许分割。事务中的全部操作在数据库中是不可分割的,要么全部完成,要么全部不执行。例如转账的这两个关键操作(将张三的余额减少10元,将李四的余额增加10元)要么全部完成,要么全部失败。
2、 一致性: 确保从一个正确的状态转换到另外一个正确的状态,这就是一致性。例如转账业务中,将张三的余额减少10元,中间发生断电情况,李四的余额没有增加10元,这个就违反一致性。
3、隔离性:并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的。如张三在A事务中查询自己的账户余额,张三老婆在事务B查询张三账户余额的影响,满足了事务的隔离性。
4、持久性:对于任意已提交事务,系统必须保证该事务对数据库的改变不被丢失,即使数据库出现故障。如张三向银行卡存进了300块钱,后续银行的数据库服务器宕机了,数据依然存在于数据库磁盘中。
三、并发事务产生的问题
MySQL 服务端是允许多个客户端连接的,这意味着 MySQL 会出现同时处理多个事务的情况。在处理并发事务时会产生:脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题。
1、脏读:一个事务读到了另一个未提交事务修改过的数据。
举例:假设有 T1 和T2 这两个事务同时在处理,事务 T1先开始从数据库中读取A,然后再执行更新操作,如果此时事务 T1 还没有提交事务,而此时正好事务 T2 也从数据库中读取A,那么事务 T2读取到的A是刚才事务 T1更新后的数据,即使没有提交事务。
2、不可重复读:在一个事务内多次读取同一个数据,如果出现前后两次读到的数据不一样的情况。
举例:假设有 T1 和T2 这两个事务同时在处理,事务 T1先开始从数据库中读取A=19,事务T2同时开始读取A=20,然后T1再执行更新操作,此时A更新为19后提交事务,然后事务T2 再次从数据库中读取A=19,两次读取到的数据不一致。
3、幻读:在一个事务内多次查询某个符合查询条件的记录数量,如果出现前后两次查询到的记录数量不一样的情况。
举例:假设有 T1 和T2 这两个事务同时在处理,事务 T1先开始从数据库中读取表A中account大于100的记录数为5,此时事务T2向表A插入一套account为200的记录并提交事务,然后T1再查询表A中account大于100的记录数为6,事务T1中同样的查询记录数的sql语句得到了两个不同的结果。
四、数据库的隔离级别
事务并发引起的问题与数据库的隔离性有关,在mysql数据库中有四大隔离级别:
1、读未提交(read uncommitted),指一个事务还没提交时,它做的变更就能被其他事务看到;
2、读提交(read committed),指一个事务提交之后,它做的变更才能被其他事务看到;
3、可重复读(repeatable read),指一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,MySQL InnoDB 引擎的默认隔离级别;
4、串行化(serializable );会对记录加上读写锁,在多个事务对这条记录进行读写操作时,如果发生了读写冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行;
事务并发在不同的隔离级别下产生不同的问题:
所以,要解决脏读现象,就要升级到读提交以上的隔离级别;要解决不可重复读现象,就要升级到可重复读的隔离级别。不过,要解决幻读现象不建议将隔离级别升级到串行化,因为这样会导致数据库在并发事务时性能很差。
同样的并发事务,在不同隔离级别下的数据体现:
在「读未提交」隔离级别下,事务 T2 修改余额后,虽然没有提交事务,但是此时的余额已经可以被事务 T1 看见了,于是事务 T1中余额 ACCOUNT1 查询的值是 50,余额 ACCOUNT2 、ACCOUNT3自然也是 50了;
在「读提交」隔离级别下,事务T2 修改余额后,因为没有提交事务,所以事务 T2 中余额 ACCOUNT1 的值还是 100 ,等事务 T2 提交完后,最新的余额数据才能被事务 T1 看见,因此额 ACCOUNT2、ACCOUNT3 都是 50;
在「可重复读」隔离级别下,事务 T1只能看见启动事务时的数据,所以余额 ACCOUNT1、余额 ACCOUNT2 的值都是 100,当事务 A 提交事务后,就能看见最新的余额数据了,所以余额 ACCOUNT3 的值是50;
在「串行化」隔离级别下,事务 T2 在执行将余额 100 万修改为 200 万时,由于此前事务T1执行了读操作,这样就发生了读写冲突,于是就会被锁住,直到事务 T1提交后,事务 T2才可以继续执行,所以从 T1的角度看,余额 ACCOUNT1、 ACCOUNT2 的值是 100 ,余额 V3 的值是 50。