事务基础:隔离与安全
创建时间:xx
修改时间:2021年11月11日09:38:22、2021年11月29日21:43:05、2022年6月28日
文章目录
——————————————————————————————————————————
问:什么是事务?
答:保证一组逻辑单元操作的同时成功或失败的概念。
事务隔离
问:事务分哪几种?具体怎么理解?
答:
读未提交:未提交事务的数据操作可被其他事务读取;
读已提交:只有已提交的数据操作才能被其他事务读取;
可重复读:只能读取到自己启动那一刻时的数据+自己的操作数据;
串行化:全局对数据的操作保持线性(通过加读写锁实现);
PG:事务AB的泳道和例图解释
读未提交:v1 = 2 v2 = 2 v3 = 2
读已提交:v1 = 1 v2 = 2 v3 = 2
可重复读:v1 = 1 v2 = 1 v3 = 2 在事务执行过程中,其看到的数据为事务启动那一刻的状态
串行化: v1 = 1 v2 = 1 v3 = 2 串行化读写的时候会对数据加读写行锁,后访问的事务需要等前一个访问的事务提交后才能执行
问:没相应的事务隔离,会发生什么问题?
答:没相应的事务隔离,所产生的问题也是建立在得到的结果与用户想要的目的有偏差的情况下。
以下的P代表一个事务(一个线程)
脏读:数据操作被其他操作干扰,读到不干净(全局不应该存在)的数据(P1修改工资为20k,P2读到工资是20k,但P1结束前把工资又改回去了。所以P2脏读)
不可重复读:两次读取不一致(P1读到工资是10k,P2修改工资为20k,P1再读的时候发现工资变为了20k,这里,就导致一个事务中无法重复读同一行数据)(2022-6-28)
幻读:两次读取的范围反馈行数不一致(P1读取10k-12k范围的人是10人,再读时却变为了12人)
第一类更新丢失:P1读取到工资为10k,P2读取到工资为10k,P1修改为11k,P2修改为12k,P2提交事务,P1回滚事务,此时P1回滚导致P2的更新丢失。(2022-6-28)
第二类更新丢失:P1读取到工资为10k,P2读取到工资为10k,P1修改为11k,P2修改为12k,P2提交事务,P1提交事务,此时P1的更新导致P2的更新丢失。(2022-6-28)
可以发现:事务隔离问题往往是多方面同时出现,是对不同客户不同需求方面的缺憾而针对性的描述
问:事务隔离的基本原理是什么?
答:从概念上进行描述,不同的隔离级别针对不同的视图逻辑创建规则,MySQL根据创建好的视图决定事务内的操作对其他事务的影响。
可重复读:事务启动时创建视图;
读提交:每次SQL执行时创建视图;
读未提交:没有视图概念;
串行化:对数据行加锁;
问:从事务隔离的原理需要注意什么?
答:oracle的默认隔离级别是“读提交”,MySQL的默认隔离级别是“可重复读”;
问:如何设置事务的隔离级别?
答:
show variables like '%transaction_isolation%';
-- 设置read uncommitted级别:
set session transaction isolation level read uncommitted;
-- 设置read committed级别:
set session transaction isolation level read committed;
-- 设置repeatable read级别:
set session transaction isolation level repeatable read;
-- 设置serializable级别:
set session transaction isolation level serializable;
事务隔离的的具体实现——多版本并发控制(MVCC)
PS:这里是简单版解释,后面的XX会有详细解释
问:事务回滚是什么?
答:举例:
事务的先后操作:事务A -> 事务B -> 事务N -> 事务C
回滚段的串联: 将2改为1 <- 将3改为2 <- 将4改为3 <- 当前值4
回滚段即上述的改动,每次更新时,都会记录当前记录的回滚段情况(记录假如回滚的话,执行什么语句)
在可重复读情况下,事务A在修改记录后,重新查询当前记录时,
首先,得到当前值4,判定不属于当前视图可见,则往前追溯,最后判定事务A值为2
问:回滚段什么时候清理?
答:会保留数据最早的事务之后的回滚段(涉及到事务id,后续的详细篇章解释)。
精简解释:数据的回滚段会保留到整个事务系统中仍处于活跃状态的最早的事务id的相关数据版本。
问:事务的回滚段有什么启发?
答:解释了事务的可重复读的隔离原理,也为其他的一些问题理解提供了理论依据(count性能、热点数据访问等等)。
问:事务的回滚段有什么需要注意的?
答:
如果事务过长,且操作的是热点数据,可能导致后续事务操作性能骤降;
过长的回滚段会占用空间和锁资源,因此不建议使用长事务。
PS:在5.5之前,回滚日志与数据字典共同存放于ibdata文件,清理回滚段,但占用空间并没有减少(涉及表删除数据的原理——空洞,后面会联系说明)。
事务启动
问:事务有哪几种启动方式?分别具有什么特点?
答:
1、显示声明启动
-- 1
begin;
-- 2
start transaction;
-- 3(立即启动事务,不需要等到第一条sql执行)
start transaction with consistent snapshot
-- 手动提交/回滚
commit;
rollback;
2、隐式启动
不同的MySQL客户端默认设置不同,如果set autocommit = 0,则本线程的自动提交事务被关闭,即使是查询语句也必须手动commit或rollback
隐式提交有默认配置部分MySQL客户端设置为0,需要额外注意,建议默认自动提交事务,按需再显示声明控制事务。
问:如果经常需要一个事务关闭后再启动下一个事务,但像减少连接交互的话,有什么好办法没?
答:
-- 提交事务并启动下一个事务
commit work and chain
问:怎么查询长事务?
答:通过information_schema表可以查询
select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60