transaction
简介
- 事务:一个最小的不可再分的工作单元;通常一个事务对应一个完整的业务(例如银行账户转账业务,该业务就是一个最小的工作单元)
- 一个完整的业务需要批量的DML(insert、update、delete)语句共同联合完成
- 事务只和DML语句有关,或者说DML语句才有事务。这个和业务逻辑有关,业务逻辑不同,DML语句的个数不同
事务的四大特征
- 原子性(A):事务是最小单位,不可再分,当执行的语句中间出错时会进行回滚
- 一致性©:事务要求所有的DML语句操作的时候,必须保证同时成功或者同时失败
- 隔离性(I):事务A和事务B之间具有隔离性
- 持久性(D):是事务的保证,事务终结的标志(内存的数据持久到硬盘文件中)
创建transaction
use sql_store;
start transaction;
insert into orders(customer_id,order_date,status)
values(1,'2019-01-01',1);
insert into order_items
values(last_insert_id(),1,1,1);
-- 手动进行提交,保证修改存入数据池
commit
-- rollback 回滚事务的所有操作
对于对同一个表操作的两个DML事务,一定会先执行完一个再执行另外一个
isolation
为了解决读写的并发性(concurrency),对于事务来说,原子性保证了其写的并发性,但在某些情况下(一个读一个写)需要自己来进行处理来保持两个事务的隔离性(isolation)。
- 事物A和事物B之间具有一定的隔离性
- 隔离性有隔离级别(4个)
- 读未提交:read uncommitted
- 读已提交:read committed
- 可重复读:repeatable read
- 串行化:serializable
- read uncommitted
- 事物A和事物B,事物A未提交的数据,事物B可以读取到
- 这里读取到的数据叫做“脏数据”
- 这种隔离级别最低,这种级别一般是在理论上存在,数据库隔离级别一般都高于该级别
- read committed
- 事物A和事物B,事物A提交的数据,事物B才能读取到
- 这种隔离级别高于读未提交
- 换句话说,对方事物提交之后的数据,我当前事物才能读取到
- 这种级别可以避免“脏数据”
- 这种隔离级别会导致“不可重复读取”
- Oracle默认隔离级别
- repeatable read
- 事务A和事务B,事务A提交之后的数据,事务B读取不到
- 事务B是可重复读取数据,在初始就形成一个数据快照,以那个快照为基准执行
- 这种隔离级别高于读已提交
- 换句话说,对方提交之后的数据,我还是读取不到
- 这种隔离级别可以避免“不可重复读取”,达到可重复读取
- 比如read uncommitted和read uncommitted读到数据是同一个
- MySQL默认级别
- 虽然可以达到可重复读取,但是会导致“幻像读”
- 所谓幻像读即是事务A通过提交的数据某一行已经满足事务B的where条件(假如存在),但对于事务B来说满足条件的那一行还是无法被执行。
- serializable
- 事务A和事务B,事务A在操作数据库时,事务B只能排队等待
- 这种隔离级别很少使用,吞吐量太低,用户体验差
- 这种级别可以避免“幻像读”,每一次读取的都是数据库中真实存在数据,事务A与事务B串行,而不并发
对于隔离等级能够预防的问题
- 查看现在的隔离等级
show variable like 'transaction_isolation';
- 改变隔离等级(serializable)
//只会对后面一个事务起作用
set transaction isolation level serializable;
//在当前session or connection未来所有事务都起作用
set session transaction isolation level serializable;
//对所有session or connection未来所有事务都起作用
set global transaction isolation level serializable;
DeadLock
死锁的概念,举例一下在Mysql中出现的情况
- session1
use sql_store;
start transaction;
update customers set state = 'VA' where customer_id = 1;
update orders set status = 1 where order_id = 1;
commit;
- session2
use sql_store;
start transaction;
update orders set status = 1 where order_id = 1;
update customers set state = 'VA' where customer_id = 1;
commit;
当session1执行完第三句的时候,mysql对customers 加锁,此时session2执行完地句对orders 进行加锁,session1获取不到orders 的锁一直等待,session2获取不到customers 的锁等待形成死锁。
解决方法
- 保证两个事务对表的操作顺序一直
- 减少每个事务的持续时间