思考
隔离性与隔离级别
1.事务的特性ACID(原子性、一致性、隔离性、持久性)
2.脏读:一个事务读取到其他事务update的数据后未提交的数据
3. 不可重复读:一个事务读取了其他事务update或delete后已经提交的数据,针对其他事务已经修改或删除已经提交的操作。
4.幻读: 一个事务读取到其他事务insert已经提交的数据
5.SQL四种隔离级别:读未提交(RU)、读提交(RC)、可重复读(RR)、串行化(serializable)
读未提交是指,一个事务还没提交时,它做的变更就能被别的事务看到。
读提交是指,一个事务提交之后,它做的变更才会被其他事务看到。
可重复读是指,一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
串行化,顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
事务隔离的实现
1.每条记录在更新的时候也会同时记录一条回滚操作,记录最新值,通过回滚状态可以得到前一个状态的值
也就是同一条记录在系统中存在多个版本,这就是数据库多版本并发控制(MVCC)
**前者记录在redo log,后者记录在undo log,undo log记录的是更新的逆过程。(这个过程感觉会造成空洞现象,之前我们有个数据库多次回滚造成索引不连续)
**
2.回滚日志在不需要的时候将会被删除,在当前系统不需要用到这些回滚日志。
3.长事务的弊端:在这个长事务提交之前,所有的回滚记录都要保留,这就会造成大量占用存储空间。
事务的启动方式
1.显示启动:begin或start transaction
2.set autocommit=0,启动一个select语句就会启动事务,此时你必须提交这个事务,否则就会变成长事务。
实践
select * from information_schema.innodb_trx ;
结果:空
set autocommit=0
select * from admin
select * from information_schema.innodb_trx ;
3.减少begin开销
**在 autocommit 为 1 的情况下,用 begin 显式启动的事务,如果执行 commit 则提交事务。如果执行 commit work and chain,则是提交事务并自动启动下一个事务,这样也省去了再次执行 begin 语句的开销。同时带来的好处是从程序开发的角度明确地知道每个语句是否处于事务中。
**
查询长事务语句超过60s的长事务
select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60
4.Mysql两个视图的概念
**在 MySQL 里,有两个“视图”的概念:一个是 view。它是一个用查询语句定义的虚拟表,在调用的时候执行查询语句并生成结果。创建视图的语法是 create view … ,而它的查询方法与表一样。另一个是 InnoDB 在实现 MVCC 时用到的一致性读视图,即 consistent read view,用于支持 RC(Read Committed,读提交)和 RR(Repeatable Read,可重复读)隔离级别的实现。它没有物理结构,作用是事务执行期间用来定义“我能看到什么数据”。
**
5."快照"在MVCC里的工作--
这个快照是基于整库的。
数据表中的一行记录,其实可能有多个版本 (row),每个版本有自己的 row trx_id。
其实v1 v2 v3并不是物理上真实存在的,这些都是通过undo log逆向推出来的。
数组里面事务 ID 的最小值记为低水位,当前系统里面已经创建过的事务 ID 的最大值加 1 记为高水位。
1.落在绿色区域,表示这个版本是已提交的事务或者是当前事务自己生成的(可见)
2.落在红色区域,将来启动事务生成的(不可见)
3.落在黄色区域,意思是:事务启动在收集“活跃的事务ID”时,有些事务提交了。所以黄色区域没有这个事务ID(可见)。
浅谈:绿色区域和红色区域没有太大问题,黄色区域:当前事务【1,2,4,5】低水位线是1 高水位线是6,而3正好落在1到6之间的黄色部分,但是3不在数组中,所以3是其他事务的提交trx_id,所以是可见的。
问题
1.可重复读的状态下,其中一个事务要等另一个事务获取到行锁才能更新数据,它读到的值是什么?
我操作的是本地的admin表--可重复读
事务A
start TRANSACTION with consistent SNAPSHOT;
select role from admin where id = 1;
commit;
事务B
start TRANSACTION with consistent SNAPSHOT;
update admin set role=role+1 where id = 1;
select role from admin where id = 1;
commit;
事务C
update admin set role=role+1 where id =1
事务A的结果是1 事务B是3
更新逻辑
更新语句是先读后写,而这个读就是当前读
结论
InnoDB 的行数据有多个版本,每个数据版本有自己的 row trx_id,每个事务或者语句有自己的一致性视图。
普通查询语句是一致性读,一致性读会根据 row trx_id 和一致性视图确定数据版本的可见性。
对于可重复读,查询只承认在事务启动前就已经提交完成的数据;
对于读提交,查询只承认在语句启动前就已经提交完成的数据;而当前读,总是读取已经提交完成的最新版本。
摘要来自:极客时间--Mysql45讲