事务
事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。
转账案例
张三向李四转账1000。
第一步:查询张三账户余额。
第二步:张三账户减少1000元。
第三步:李四账户增加1000元。
第一步到第三步是一个事务,如果没有事务管理,在第二步执行结束之后出现了异常,导致程序终止退出了,就出现问题了。
张三账户余额减少了1000,但是李四账户并没增加1000。钱去哪儿了?
这就体现出事务的重要性了。
mysql中的事务
MySQL中的事务提交方式默认为自动提交,为了事务的灵活性,我们需要就将MySQL的事务设置为手动提交
方式一
#查询事务提交方式(默认为1,自动提交;0为手动提交
SELECT @@autocommit;
SET @@autocommit = 0;
#方式一
#转账操作(张三给李四转账1000)
#1.查询张三余额
SELECT * FROM account WHERE NAME = '张三';
#2.张三余额减少1000
UPDATE account SET money = money - 1000 WHERE NAME = '张三';
#模拟异常
#程序抛出异常...
#3.李四余额增加1000
UPDATE account SET money = money + 1000 WHERE NAME = '李四';
#提交事务
COMMIT;
#回滚事务
ROLLBACK;
首先执行第1、2、3条sql语句,如果程序没有出现异常,再执行commit命令;如果命令执行期间出现了异常,则执行RollBack命令将数据回滚。
方式二
#方式二
#开启事务 start transaction 或 begin;
START TRANSACTION;
#转账操作(张三给李四转账1000)
#1.查询张三余额
SELECT * FROM account WHERE NAME = '张三';
#2.张三余额减少1000
UPDATE account SET money = money - 1000 WHERE NAME = '张三';
#模拟异常
#程序抛出异常...
#3.李四余额增加1000
UPDATE account SET money = money + 1000 WHERE NAME = '李四';
COMMIT;
ROLLBACK;
和方式一的区别就是开启事务的方式
START TRANSACTION;
#或
BEGIN
事务的四大特性(ACID)
既然谈到了事务,那么就必然离不开事务的四大特性
- 原子性(atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败。比如在同一个事务中的SQL语句,要么全部执行成功,要么全部执行失败。 - 一致性(consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
换一种方式理解就是:事务按照预期生效,数据的状态是预期的状态。
举例说明:张三向李四转100元,转账前和转账后的数据是正确的状态,这就叫一致性,如果出现张三转出100元,李四账号没有增加100元这就出现了数据错误,就没有达到一致性。 - 隔离性(isolation)
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。 - 持久性(durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
并发事务存在的三个问题
- 脏读
事务A读到了事务B未提交的数据 - 不可重复读
事务A1读取id为1的数据,然后事务B1修改了id为1的数据,事务A2再次读取id为1的数据,发现两次结果不一致,这就是不可重复读。(A1、A2是指一个事务中的两次sql操作) - 幻读
事务A1读取id为1的数据,发现为null,然后事务B1添加id为1的数据。此时事务A2向DB中添加id为1的数据,发现已经存在了。而查询id为1的数据仍然为空。这个id为1的数据对于事务A来说就像幽灵一样。
为了解决脏读,不可重复读,幻读,InnoDB存储引擎提供了四个隔离级别。
事务的隔离级别
- 读未提交(read uncommitted)
- 读已提交(read committed)
- 可重复读(repeatable read)
- 串行化(serializable)
下图为隔离级别与可以防止的事务。
√代表会发生,×代表不会发生
读未提交
在读未提交的隔离级别下,脏读、不可重复读、幻读都有可能出现。
读已提交
在读已提交的隔离级别下,脏读不会出现。
脏读是指事务A读到了事务B未提交的数据。
在读已提交的隔离级别下,事务A只会读到事务B提交后的数据。
可重复读
在可重复读的隔离级别下,脏读和不可重复读不会出现
不可重复读是指事务A1读取id为1的数据,然后事务B1修改了id为1的数据,事务A2再次读取id为1的数据,发现两次结果不一致。(A1、A2是指一个事务中的两次sql操作)
在可重复读的隔离级别下,事务A中的不同的查询操作都会在本事务的范围之类,不会受到事务B的影响。
串行化
所有并发事务问题都不会发生,因为它会给每一个进入数据库的事务加“锁”,只有等当前事务结束释放锁后,下一个事务才会进入。所以就避免了并发事务问题。
查询和设置隔离级别
需要注意的是,mysql中的默认存储引擎是innodb,它默认的隔离级别是可重复读。
#查询隔离级别
select @@Transaction;
#设置隔离级别
#set [session|global] Transaction isolation level {read uncommitted|read committed|repeatable read|serializable}
set session transaction isolation level repeatable read