一事务定义:一组操作要么全部成功,要么全部失败,目的是为了保证数据最终的一致性。
二、事务的原理是什么?
事务的ACID特性:
原子性(Atommicity):当前事务的操作要么同时成功,要么同时失败。原子性由undo log日志来实现。(比如,下单时候,会1生成订单、2减库存。入过库存不足,那么就回滚生成得订单。生成订单时undo log 会记录insert语句,和delete id=1语句,当减库存失败时,执行delete语句)
一致性(Consistency):使用事务的最终目的,由其它3个特性以及业务代码正确逻辑来实现。(比如,减库存时,try {减库存 }catch(打印日志)没有抛异常,也不会回滚。)
隔离性(lsolation):在多个事务并发执行时,他们内部的操作不能互相干扰,隔离性由MySQL的各种锁以及MVCC机制来实现。
持久性(Durability):一旦提交了事务,它对数据库的改变就应该是永久性的.。持久性由redo log日志来实现
事务的隔离级别:innoDB 引擎中,定义了四种隔离级别。级别越高事务隔离性越好。隔离性是由Mysq的各种锁以及MVCC机制来实现的。
1read uncommit(读未提交):脏读(update语句后,没有执行commit,也能读到新数据,一旦rellback,就会撤销update。造成脏读:事务A读取到了事务B已经修改但尚未提交的数据)
比如:set tx_isolation='read-uncommitted';
begin;
update account set balance = balannce +500 where id =1;
ROLLBACK;
commit;
2read commit(读已提交):oracle默认,不可重复读:事务A内部的相同查询语句在不同时刻读出的结果不一致,不符合隔离性
3repeatable read(可重复读):Mysql默认用这个级别,幻读(事务A在修改语句后读到了事务B提交的新增数据。比如事务A第一次查是3条记录,库里加了第4条记录,然后事务A修改了id=4的记录后,再查是4条记录了).select查询的是快照(当时查询时库里的数据,历史版本),update修改的是最新数据。修改之后再查是查修改后的数据,其他没有修改的数据还是读之前快照里的数据。修改语句在事务里有行锁(属于悲观锁),未提交前,其他事务不可修改。
4serializable(串行):解决上面的所有问题,包括脏写。有个事务在查询着,其他事务就不能修改。不过性能较低,一般不用。它的实现原理就是:在所有的select语句后面加了一把读锁。
常看当前数据库的事务隔离级别: show variables like 'tx_isolation';
设置事务隔离级别语句:set tx_isolation='read-uncommitted';
以上是基于Mysql5.7的,Mysql 8的语句 为:
常看当前数据库的事务隔离级别: show variables like 'isolation';
设置事务隔离级别语句:set transation_isolation='repeatable-read';
注意:在sql里面更新数据,就不会出现脏写的情况。不要在java里计算。
乐观锁,在表里加字段version,每次修改就+1.
比如:set tx_isolation='read-committed';
begin;
select id,version,balance from account where id = 1;(1,1)
java:1000+5=1500
update account set balance =1500 where id =1 and version = 1;
commit;
在前面加while,如果版本不存在,就再执行一次。直至成功。
可以在读未提交、读已提交中使用。
锁的分类:
从性能上分为乐观锁(用版本对比来实现)和悲观锁
从对数据库操作的类型分,分为读锁和写锁(都属于悲观锁)
读锁(共享锁,S锁(Shared)):select ...lock in share mode
读锁是共享的,多个事务可以同时读取同一个资源,但不允许其他事务修改。
写锁(排它锁,X锁(eXclusive)):select... for update;
是排他的,当前写操作没有完成前,它会阻断其他写锁和读锁,update、delete、insert都会加写锁。
从对数据操作的粒度分,分为表锁和行锁。
事务的优化:
大事务的影响:
1并发情况下,连接池容易被撑爆
2锁定太多的数据,会造成大量的阻塞和锁超时
3执行时间长,会造成主从延迟
4回滚需要的时间比较长
5undo log会膨胀
6容易导致死锁
事务优化实践原则:
1将查询等数据准备操作放到事务外(如果是RC读已提交 级别可以,RR可重复读 不行)
2事务中避免远程调用,远程调用要设置超时。
3事务中避免一次性处理太多数据,可以拆分多个事务分次处理。
4更新等涉及加锁的操作尽可能放在事务靠后的位置(update 放事务后面,避免其他的事务等待)
5能异步处理的尽量异步处理
6应用层(业务代码)保证数据一致性,非事务执行(在代码里写tay{1,2,3}catch()代码控制业务回滚)