关于mysql事务的一些理解

1.什么是事务?

保证用户的数据操作动作对数据是“安全的

2.为什么需要事务处理机制

因为并发下,数据操作的会产生数据不同步问题。
首先在mysql中的事务操作可以分为四类:“读-读”,“读-写”,“写-读”,“写-写”,在并发情况下,除了“读-读”不影响数据的一致性,其他都会产生一定的问题。
具体来说,
(1)写前读后--可能读到其他事务没有提交的数据--也就脏读
(2)读前写后--两种情况:
两次读读到不同的数据--就是不可重复读
两次带条件,查询范围数量不同,修改条目不一致--就是幻读
(3)写写问题
更新丢失-两次写成功,做了数据覆盖
脏写-两次写,最先写操作被回滚

在这里插入图片描述

3.怎么解决并发的问题?

在这里插入图片描述

(1)TO-基于时间戳对事务提交顺序排序

	基于时间戳对事务提交顺序排序的并发控制技术。事务+时间戳,数据项+时间戳,比较作用于在事务的时间戳(Ti)和作用在数据项时间戳Tj的大小。如果Ti<Tj,则事务调度器必须保证所产生的并发调度等价于事务Ti先于事务Tj的某个串行调度。

(2)CO-提交操作的顺序进行排序

	Commitment ordering(或Commit ordering,CO):提交排序是对提交操作的顺序进行排序,这种方式是“冲突可串行化”。
	在于在事务结束前,读或写等操作不互相阻塞,只是在提交阶段判断是否存在“冲突行为”,以决定并发地事务的提交顺序。这个主要是用于分布式事务。

(3)串行化图形检测 --优先级排序

	事务的调度任务根据优先级线程一个有向图,有向图的方法:高优先级指向低优先级,必须是串行化图形,不能能形成环,不然就破坏了冲突可串行化。

(4)2PL两阶段封锁

两个阶段,增长和缩减。同一个事务内,所有的数据线要么同时加锁,要么同时解锁,不能交叉进行。
- 增长阶段,growing phase--加锁。
不管同一个事务内需要在多少个数据项上加锁,所有的加锁操作都只能在同一个阶段完成,在这个阶段内,不允许对对已经加锁的数据项进行解锁操作,即加锁和解锁操作不能交叉执行(同一个事务内) 。这一条是说在同一个事务内部的事情。
- 缩减阶段,shrinking phase -- 解锁

数据项上锁的类型
数据项+S锁
1.读-读是可以并发的
“两个事务并发读同一个数据项” – 不阻塞
2.“读–写并发是不允许”
“读锁阻塞其他事务写本数据项” – 阻塞 – 数据项+X锁
(1)读操作 – 禁止其他事务读取
(2)写操作 – 禁止其他事务写

(5)MVCC-多版本控制(重点)

多版本并发控制技术 (Multiversion concurrency control(MCC or MVCC)),写操作生成一个数据项的新版本,读操作读事务开始节点获取的快照。
(1)读-读 是允许并发
(2)读-写 写-读:不会相互阻塞,通过多版本可以暂时做到并发,幻读问题需要配合加锁策略解决。
(3)写-写:必须阻塞,可以减轻脏写和数据丢失,但不能避免“写偏序”,”数据异常”

(6)基于索引的并发控制技术 (Index concurrency control)

在索引树上对索引页采取封锁手段,以维护索引树的一致性;同时,可以确保避免幻象异常,如MySQL的InnoDB存储引擎以B+树作为存储的基本结构(即索引组织表),可以在索引树上直接施加“next-key locking”进行范围锁定,以避免在谓词限定的“谓词空间”内新数据被插入或旧数据被删除。

4.当前读和快照读

多版本并发控制(Multiversion concurrency control,MVCC)技术主要解决读并发的问题,在了解mvcc之前需要了解两个概念,当前读和快照度。

a) 当前读

当前读读取的是记录的最新版本
读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁
SQL:update , insert ,delete ,select xx from xx for update ,  in share mode 
当前读会等待,不会返回数据的历史版本
实现机制:在当前读使用锁,加上锁了,别的事务要修改只能等待,自然也就没有各种问题了

b) 快照读

读取的历史版本,前提不是串行化的隔离级别
像不加锁的select操作就是快照读,即不加锁的非阻塞读;快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读;
之所以出现快照读的情况,是基于提高并发性能的考虑,快照读的实现是基于多版本并发控制,即MVCC,可以认为MVCC是行锁的一个变种,但它在很多情况下,避免了加锁操作,降低了开销;既然是基于多版本,即快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本。
SQL:select * from where xxx  -- 不加锁的非阻塞读
实现机制:mvcc,必要是会加锁
mysql RR 下会产生幻读问题,避免幻读要使用的mvcc+间隙锁。  
快照读下数据的可见返回 = mvcc + 当前事务加锁的行  

隔离级别
RR–读取历史数据
RC-读取最新数据

5.MVCC的实现原理,三部分

第一部分:行记录的隐藏字段
第二部分:undolog
第三部分:readview 和 可见性原则。

第一部分:行记录的隐藏字段

DB_ROW_ID,默认生成的主键,6byte,如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引。
DB_TRX_ID ,6byte,最近修改(修改/插入)事务ID:记录创建这条记录/最后一次修改该记录的事务ID,注意:最新而不是当前的事务id。
DB_ROLL_PTR,7byte,回滚指针,上一个历史数据历史版本,会有多个历史版本,所以是一个指向undolog历史版本地址链表。
在这里插入图片描述

第二部分:undolog

什么是undolog
undo log保存历史版本。相当于是一个链表,最新的历史版本保存在链首,链尾是保存最早的旧记录
undolog的两种操作日志:
(1)insert undo log
事务在insert新记录时产生的undo log, 只在事务回滚时需要,并且在事务提交后可以被立即丢弃
(2)update undo log
事务在进行update或delete时产生的undo log; 不仅在事务回滚时需要,在快照读时也需要;所以不能随便删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被purge线程统一清除

第三部分:readview 和 可见性原则。

快照读操作的时候生产的读视图(Read View)

Read View

ReadView 四个字段
-creator_trx_id 生成该ReadView的事务的事务id
	由于只有在对表中记录做改动(增删改)时才会为事务分配事务id,所以在一个读取数据的事务中的事务id默认为0;

-trx_list/m_ids 当前活跃的事务id列表
	在生成ReadView时当前系统中活跃的所有事务的事务id列表,活跃的是指当前系统中那些尚未提交的事务

-up_limit_id/min_trx_id 活跃列表中事务id的最小值
	低水位
	生成ReadView时当前系统中活跃的读写事务中最小的事务id,也就是m_ids中的最小值

-lower_limit_id/max_trx_id 即将分配的下一个事务id
	高水位
	表示生成ReadView时系统中应该分配给下一个事务的id值
	由于事务id一般是递增分配的,所以max_trx_id就是m_ids中最大的那个id再加上1;
ReadView 源码:

在这里插入图片描述

可见性原则:

1、版本的trx_id==ReadView中的creator_trx_id,表示当前读事务正在读取被自己修改过的记录,该版本可以被当前事务访问;
2、版本trx_id < up_limit_id(min_trx_id),表明生成该版本的事务在当前事务生成READVIEW前已经提交了,所以该版本可以被当前事务访问;
3、版本的trx_id > lower_limit_id(max_trx_id),表明生成该版本的事务在当前事务生成ReadView后才开启的,该版本不可被当前事务访问;
4、版本的trx_id在ReadView的min_trx_id和max_trx_id之间,那就需要判断一下trx_id属性值是不是在trx_list(m_ids)中。如果在这个范围内,说明创建ReadView时该事务还处于活跃状态,该版本不可以被当前事务访问;如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被当前事务访问;在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/tsj11514oo/article/details/117406481
今日推荐