一、事务的定义
一组操作要么全部成功,要么全部失败,目的是为了保证数据的一致性
二、事务的ACID特性
- 原子性(Atomicity):当前事务的操作要么同时成功,要么同时失败。原子性有undo log日志来保证
- 一致性(Consistency):使用事务的最终目的,有业务代码正确逻辑保证
- 隔离性(Isolation):在事务并发执行是,他们内部的操作不能互相干扰
- 持久性(Durability):一旦提交了事务,他对数据库的改变应该是永久性的。持久性由redo log日志来保证
三、事务隔离性
InnoDB引擎中,定义了四种隔离级别供我们使用,级别越高事务隔离性越好,但性能就越低,二隔离性是由MYSQL的各种锁以及MVCC机制来实现的
- read uncommitted(读未提交):脏读
- read committed(读已提交):不可重复读
- repeatable read(可重复读):脏写
- serializable(串行)
read uncommitted
读取所有未提交的数据,会出现脏读的情况
set transaction isolation level read uncommitted; begin; -- balance = 100 update acid set balance = balance + 500 where id = 1; -- 暂时不执行commit commit;
set transaction isolation level read uncommitted; begin; -- balance = 600 select * from acid where id = 1; commit;
read committed
读取所有已提交的数据
set transaction isolation level read committed; begin; -- balance = 100 update acid set balance = balance + 500 where id = 1; -- balance = 600 select * from acid where id = 1; commit; -- 暂不执行下面的第二部分 begin; update acid set balance = balance + 500 where id = 1; -- balance = 1100 select * from acid where id = 1; commit;
set transaction isolation level read committed; begin; -- balance = 600 select * from acid where id = 1; -- 执行第二部分 balance = 1100 select * from acid where id = 1; -- 暂时不执行commit commit;
repeatable read
读取事务开始时的数据,会出现脏写的情况
set transaction isolation level repeatable read; begin; -- balance = 100 update acid set balance = balance + 500 where id = 1; -- balance = 600 select * from acid where id = 1; commit; -- 暂不执行下面的第二部分 begin; update acid set balance = balance + 500 where id = 1; -- balance = 1100 select * from acid where id = 1; commit;
set transaction isolation level repeatable read; begin; -- balance = 600 select * from acid where id = 1; -- 执行第二部分balance = 600 select * from acid where id = 1; -- 暂时不执行commit commit;
serializable
串行进行读写操作,读写不能一起执行,速度很慢
set transaction isolation level serializable; begin; -- balance = 100 update acid set balance = balance + 500 where id = 1; -- 暂时不执行commit 后面的select不会执行 commit;
-- 等待事务提交才能开始执行 set transaction isolation level repeatable read; -- balance = 600 begin; select * from acid where id = 1; commit;
锁
读锁(共享锁、S锁):select ... lock in share mode;
读锁是共享的,多个事务可以同时读取同一个资源,但不允许其他事务修改
写锁(排它锁、X锁):select ... for update;
写锁是排它的,会阻塞其他的写锁和读锁,update、delete、insert都会加写锁
MVCC机制
MVCC(Multi-Version Concurrency Control)多版本并发控制,就可以做到读写不阻塞,且避免了类似脏读这样的问题,主要通过undo日志链来实现
read committed(读已提交),语句级快照
repeatable read(可重复度),事务级快照
解决脏写
1.添加VERSION字段
update acid set balance = 300 version = 2 where id = 1;
-- 未更新成功重复提交、乐观锁
update acid set balance = $(300) + 100 and version = 1 where version = 2 and id = 1
2.使用SQL语句直接对字段进行加减操作
在事务里update会自动加锁阻塞、不会出现脏写问题
update acid set balance = balance + 100 where id = 1;
事务优化
大事务
大事务问题
- 并发情况下,数据库连接池容易被撑爆
- 锁定太多数据,造成大量的阻塞和锁超时
- 执行时间长,容易造成主从延迟
- 回滚所需要的时间变长
- undo log膨胀
- 容易导致死锁
长事务优化
- 将查询等数据操作放到事务外
- 事务中避免远程调用,远程调用要设置超时时间,防止事务等待时间太久
- 事务中避免一次性处理太多数据,可以拆分为多个事务分次处理
- 更新等涉及加锁的操作尽可能放在事务靠后的位置
- 能异步处理的尽量异步处理
- 应用侧(业务代码)保证数据一致性,非事务执行