数据库的事务
什么是事务 :
事务就是要保证一组数据操作,要么全部成功要么全部失败 , 在 MYSQL中 , 事务支持实在引擎层实现的 , 我们知道 MYSQL是支持多引擎的系统,
但并不是所有的引擎都支持事务 , 比如 MySQL 的原生 myLsam引擎就不支持事务,这也是它被 InnoDB取代的重要原因之一
学习前先了解事务系统主要包含知识点 :
事务的启动,事务的提交,事务的回滚,多版本控制,垃圾清理,回滚段以及相应的参数和监控方法
事务的特性 :ACID
原子性 : 指的是整个事务要么全部成功,要么全部失败 , InnoDB通过undolog保证rollback的时候能找到之前的数据
一致性 : 在任何时刻,数据都是一致的,保证不会读到中间状态的数据 , 在InnoDB中主要通过crash recovery和double write buffer的机制保证数据的一致性
隔离性 : 多个事务可以同时对同一数据进行操作,但是相互不影响。InnoDB中,有四种隔离级别。默认是RR隔离级别
持久性 : 事务commit的数据在任何情况下都不能丢。InnoDB通过redolog保证已经commit的数据一定不会丢失
多事务同时执行可能产生的问题 :
脏读 : 当数据库中一个事务A正在修改一个数据但是还未提交或者回滚,另一个事务B 来读取了修改后的内容并且使用了,之后事务A提交了,此时就引起了脏读。(发生场景: 读未提交的的隔离级别)
不可重复读 : 在一个事务A中多次操作数据,在事务操作过程中(未最终提交),事务B也才做了处理,并且该值发生了改变,这时候就会导致A在事务操作的时候,发现数据与第一次不一样了。 就是不可重复读。(发生场景:读未提交、读提交的隔离级别.)
幻读 : 一个事务按相同的查询条件重新读取以前检索过的数据,
却发现其他事务插入了满足其查询条件的新数据,这种现象就称为幻读。幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,比如这种修改涉及到表中的“全部数据行”。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入“一行新数据”。那么,以后就会发生操作第一个事务的用户发现表中还存在没有修改的数据行,就好象发生了幻觉一样.
一般解决幻读的方法是增加范围锁RangeS,锁定检索范围为只读,这样就避免了幻读。(发生场景:读未提交、读提交、可重复读的隔离级别.)
事务的隔离级别 :
读未提交 : 一个事务未提交,他所做的变更可被别的事务看到
读已提交 : 一个事务提交后,它所做的变更才可被别的事务所看到
可重复读 : 一个事务执行过程中所看到的数据是一致的 , 未提交的更改对其他的事务是不可见的
串行化(序列化) : 对应一个记录会加读写锁 , 出险冲突的时候,后访问的事务必须等前一个事务执行完成后才能继续执行
底层实现 :
在实现上,数据库里面会创建一个视图 , 访问的时候以视图的逻辑结果为准 , 在"可重复读" 隔离级别下 , 这个视图是在事务启动时创建的 , 整个事务存在期间都用这个视图,
在"读已提交"隔离级别下, 这个视图是在每个 sql 语句执行的时候创建的
在"读未提交"隔离级别下直接返回记录上的的最新值 , 没有视图概念
"串行化"隔离级别下直接用加锁的方式来避免并行访问
事务隔离实现 :
每条记录在更新的时候都会同时记录一条回滚操作 , 同一条记录在系统中可以同时存在多个版本 , 这也是数据库多版本并发控制(MVCC)
回滚日志什么时候删除 :
系统会判断当没有事务需要用到这些回滚日志的时候 , 回滚日志会被删除
(什么时候不需要了 : 当系统里没有比这个回滚日志更早的 read-view的时候)
配置方式 :
transaction-isolation (show variables 查看当前事务级别 )
事务的启动方式 :
1. 显示启动事务语句 , begin 或者 start transaction , 提交 commit , 回滚rollback
begin; -- 开启一个事务
update table set A = A - 1;
update table set B = B + 1;
-- 其他读写逻辑....
commit; -- 提交事务
2. set autocommit=0 , 该命令会把这个线程的自动跳关掉 , 这样只要执行一个 select 语句,事务就会启动 , 并不会自动提交 , 直到主动 commit 或 rollback 或断开链接
建议使用 : 如果考虑多一次交互问题 , 可以使用 commit work and chain 语法(提交事务并自动启动下一个事务) ,
在autocommit = 1 的情况下用 begin 显示启动事务
扩展知识 : 可以在smrt库的tx这个表中查询长事务,比如下面这个语句,用于查找持续时间超过60s的事务。
select * from smrt.tx where TIME_TO_SEC(timediff(now(),trx_started))>60
(不断持续学习更新中.......)