1.为什么需要事务。
其实很简单,是为了杜绝一部分操作执行了,而因为某些因素,导致另外一部分的操作没有执行,导致数据丢失之类的。例如。小明银行账户上有10000元,小张账户上有4000元,现在小明需要转2000给小张。那么
操作1:小明的账户余额10000-2000=8000,
操作2:小张的账户余额4000+2000=6000;
如果数据库刚好完成操作1,不凑巧停电了,三分钟之后来电了,小明的余额为8000,但是小张的余额仍然为4000,那么2000就凭空消失了,是因为操作1执行了,而操作2还没来得及执行导致的。
为了杜绝上述情况就引入了事务。
事务将所有的sql语句当成一个整体,要么全部执行成功,要么一句也不执行。
那么转账就可以这样写
begin事务开始
update 小明用户余额-2000
update 小张用户余额+2000
commit提交事务(事务结束)
因为事务的特性,不会导致数据出现异常,假如只执行了update小明用户余额-2000,这个操作数据库就因为异常而崩溃,那么因为这个事务没有提交,就会通过回滚日志(undo log)对已经修改的操作,回退到上一个版本。
ACID
原子性:整个事务的所有操作要么执行成功,要么全部执行失败后回滚到最初状态。
一致性:数据库中的数据在事务操作前后和事务处理后必须都满足业务规则约束,比如a和b账户总金额在转账前后保存一致。
隔离性:一个事务在提交之前做出的操作是否能为其他事务可见,由于不同的场景需求不同,因此针对隔离性来说有不同的隔离级别。
持久性:一旦提交,事务所做的修改就会永久保存,即使数据库崩溃,修改的数据也不会丢失。
MYSQL中,既可以通过START TRANSACTUIN语句开始一个事务,也可以使用别名BEGIN语句,事务结束可以使用COMMIT语句提交事务或者通过ROLLBACK语句回滚事务撤销修改。
MYSQL事务隔离级别
事务通过锁机制满足隔离性,在InnoDB存储引擎中,有不同的隔离级别,他们有着不同的隔离性。
为什么要设置隔离级别?因为在数据库操作中可能出现以下问题:
1.脏读:当前事务能够看到别的事务中未提交的数据。A事务读取B事务尚未提交的数据并在此基础上操作,而B事务执行回滚,那么A读取到的就是脏数据。
解决办法:如果一个事务提交前,任何其他事务都不能读取其修改过的值。
2.不可重复读:一个事务对同一行数据重复读取两次,但是却得到了不同的结果。事务T1读取某一数据过后,事务T2对其做了修改,当事务T1再次读取该数据得到了不同的值。
解决办法:如果在修改事务完全提交之后才可以读取数据,就可以解决该问题。
3.幻读:在其中一个事务读取到了其他事务新增的数据,仿佛出现了幻影现象。
解决办法:在操作事务完成数据处理之前任何其他事务都不可以添加新数据。
因此为了解决以上情况:数据库提供了4中隔离级别,由低到高分别为
读未提交,读已提交,可重复读取,序列化。
1.读未提交:在这个隔离级别下,即使别的事务所做的修改并未提交,也能看到其修改的数据。其并发性能最高,但是隔离级别最弱,会出现脏读,在水产环境不使用。
2.读已提交:读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行,避免了脏读,但是却可能出现不可重复读。比如事务A先读取了数据,事务B紧接着更新并提交了事务,当事务A再次读取该数据的时候已经发生改变。
3.可重复读:是指在一个事务内多次读取同一数据。假设在一个事务还没有结束时,另外一个事务也访问同一数据,那么在第一个事务中的两次读取数据之间,即使第二个事务对数据进行了修改,第一个事务两次读到的数据也是一样的。这样在一个事务内两次读取到的数据就是一样的,因此称为可重复读。读取数据的事务禁止写事务但允许读事务,而写事务禁止其他一切事务。避免了脏读和不可重复读,但会产生幻读。
4.序列化也叫可串行化:提供严格的事务隔离。它要求事务序列化执行,即事务只能一个接一个的执行,但不能并发执行。仅仅通过行级锁是无法实现的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。序列化是最高的事务隔离级别,同时代价最高,性能很低,一般很少使用。
可以优先考虑设置为读已提交,对于不可重复读和幻读可以采取乐观锁和悲观锁来控制,大多数数据库都默认读已提交这个隔离级别。