ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

事务机制的分析

2022-08-15 14:01:27  阅读:144  来源: 互联网

标签:分析 事务 记录 查询 索引 退化 机制 id


工作的时候,发现有个接口超时了:

  

 

可以看出报错信息是锁超时的了。

sql具体信息如下:

 

 此sql经由sharding jdbc改写,所以首先怀疑到此插件上。 因使用版本不能看到相应改写代码,因此只能查看数据库的信息

show processlist;

select * from information_schema.PROCESSLIST where info is not null;

可以看到当运行的时候会有个sql卡住。

 

 更新的时候卡住,并且有锁,说明其他事务上了锁,和当前的sql有冲突。  需要找证据,顺着代码向前追溯。

 

 

 这行代码是对当前集合id的更新。

更新就上锁了嘛? 此时需要复习一下mysql的锁机制。

mysql 分为当前读和快照读。

  当前读: 像select for update,update,insert,delete这些操作都是当前读,就是读取当前最新版本,读取时还要保证其他并发事务不能修改当前记录,对读取的记录进行加锁。

  快照读:不加锁的select操作就是快照读,不加锁的非阻塞读。快照读的实现基于MVCC,在很多情况下,避免了加锁操作,降低了开销;

  快照读是通过MVCC来是实现的,简单介绍一下MVCC解决什么问题、怎么解决问题以及当前存在的问题。

  MVCC是一种用来解决读-写冲突的无锁并发控制,也就是为事务分配单向递增的事务ID,为每个修改保存一个版本。读操作只读该事务开始前的数据库的快照。  MVCC为数据库解决了以下问题:

  在并发读写数据库时,可以做到在读操作时不用阻塞写操作, 写操作也不用阻塞读操作,但是写写操作需要互斥,提高了数据库并发读写的性能。

  同时还可以解决脏读、幻读(并不能解决),不可重复读(这个要看隔离事务级别,RC的依然不能解决,因为每次当前读都需要更新视图)。

  MVCC 主要依赖记录中的3个隐藏字段、undo日志(版本链),ReadView来实现。

  首先是隐藏字段:

  1. DB_TRX_ID:6byte,最近修改事务ID:记录创建这条记录/最后一次修改该记录的事务ID。

  2. DB_ROLL_PTR: 7Byte回滚指针,指向这条记录的上一个版本(存储于rollback segment,版本链)

  3. DB_ROW_ID: 如果没有主键或者唯一索引,会自动以DB_ROW_ID产生一个聚簇索引。

  首先通过版本链,不同事务或者相同事务对同一记录的修改,就会导致该记录的undo log成为一条记录版本线性表。 

  其次通过ReadView(读视图) 在该事务执行快照读的那一刻,会生成数据库系统当前的一个快照读,记录并维护系统当前活跃事务的ID。

  ReadView 遵循一个可见性算法,主要是将要被修改最新记录的DB_TRX_ID取出来,与系统当前其他活跃事务的ID去对比ReadView,通过比较看是否符合可见性。
  ReadView 其实就是个对象,其中包含四个属性:

  m_ids: 在生成ReadView 时当前系统中活跃的去写事务的事务id的列表

  min_trx_id 表示 m_ids中最小值

  max_trx_id 表示生成ReadView时,系统中应该分配给下一个事务的id值

  creator_trx_id 表示生成该ReadView的事务id。

  如何判断版本链中哪个版本可见

  小于最小可见、大于最大不可见,中间的,在里面的都不可见,里面以外的,已提交的就可见了。

 

其实光介绍MVCC是没有意义的,还需要根据不同的隔离级别进行判断:

1. RC情况下:

  RC是可以使用MVCC的,在一个事务里面每次进行一次快照读就会更新一次 ReadView,因此每次都更新,导致之前已提交的事务的内容的变得可见,因此活跃事务中没它了,因此对同一个数据进行读取的时候,变得不可重复读。 但是为啥还要加MVCC呢,如果不加的话,就会读写互斥,加了以后当前读不影响其他事务。  因此其只能做到读已提交,做不到可重复读,更不用说幻读。

2. RR情况下:

  RR使用MVCC,事务在第一次快照读生成一次ReadView,以后都不更新,这就保证了别的事务提交的事务并不影响我读取数据,因此其能保证可重复读,但是其能保证幻读吗?

  答案是不能哦,幻读一般分为两种方式: 一种是范围查询,一种是更新一个查询不到的数据,但是却更新成功,并且能读取了。
  首先是解决范围查询的问题。

  select * from A where a>100        语句1

  update A set colum = "default";    语句2

  select * from A  where a>100        语句3 

  当别的事务插入一个 a =101 并在语句2之前提交,此时语句3查询就会多出来一个。老子出现幻觉了?

  那怎么解决这个问题呢?

  需要在语句1加上for update 让mysql加间隙锁,使得相应的记录,此时就保证了别的事务插入的时候就会阻塞,但是保证了当前事务不会出现幻读情况。

  第二种情况也是类似,新加入的记录被当前事务操作,变得可见,查询的时候出了幻觉,和之前查询的结果并不一样。 

  因此在RR情况下,幻读的解决是需要严谨的使用sql来解决。

刚才提到的间隙锁是个什么情况呢?

  首先间隙锁在RC是不存在的:

  

 

  当插入id =12的时候,是阻塞住的

 

   

 

 

   当插入id =13的时候,没有阻塞,说明RC隔离级别下是没有间隙锁的,只有RR隔离级别下是有间隙锁。

  

 

 

接下来介绍一下记录锁、间隙锁、临键锁的区别

记录锁:record lock,即锁住一条记录

间隙锁:gap lock,即锁定一个区间,左开右开

临键锁:记录锁+间隙锁锁定的区间,左开右闭

例子如下:

A表:

id      a       b

0       0       0

8       8       8

16    16     16

id是主键索引,a是普通列 ,b是普通索引

唯一索引等值查询:

  1. 当查询记录存在的,在用唯一索引进行等值查询时,next-key lock 会退化为记录锁  (主键索引也是唯一索引)

  2. 当查询记录不存在时,在用唯一索引进行等值查询时,next-key lock 会退化为间隙锁

  行数据存在加行锁,行数据不存在加间隙锁

 

索引等值查询,需要访问到第一个不满足条件的值,此时的next-key lock会退化为间隙锁

普通索引等值查询:   等值会退化。

  1. 当查询的记录存在时,除了会加next-key lock外,还额外加间隙锁,相当于加两把锁 (] 和()    索引等值查询,需要访问到第一个不满足条件的值,此时的next-key lock会退化为间隙锁。

  存在就加两个,有一个退化。  

  2. 当查询记录不存在时,只会加next-key lock,然后会退化为间隙锁,也就是说只会加一把锁()还是退化了的

   不存在就加一个退化的锁

 

总结。等值会退化。

 

比较有争议的,版本不一样加的锁不太一样

唯一索引范围查询:

  select * from t_test where id >=8 and id <9 for update

  因为是大于等于,首先找到等于,其第一行 id =8 加next-key lock (0,8] 但因id是唯一索引,该记录是存在的,因此会退化为记录锁,因此只会对这id= 8这一行加锁。

  但是因为是范围查询,索引就要找下一个区间,下一个区间是16, 所以需要加锁(8,16]。 (低版本范围查询不会退化   MySQL version 5.6.47)

  也有版本将锁范围加成了 (8,16)。                                                                                        (高版本的范围查询会退化,MySQL8.0.18版本)

  因此所有的加锁范围变成了 id=8 和(8,16) 这个没有争议。  一个是行锁,一个间隙锁,两个都退化了
    

普通索引范围查找:

  非唯一索引和主键索引的范围查询的加锁也有所不同,不同之处在于普通索引范围查询,next-key lock 不会退化为间隙锁和记录锁。

  select * from t_test where b >=8 and b <9 for update

  最开始要找第一行是b=8 因此next-key lock(0,8],但是由于b不是唯一索引,

  所以加锁为(0,8],(8,16]                                                        (低版本范围查询不会退化   MySQL version 5.6.47)
  有的说是有退化,导致加锁区间为 (0,8],(8,16)                    (高版本的范围查询会退化,MySQL8.0.18版本)

  这个就有争议了,有的是后一个退化了,有的没有退化,可能是版本不一样导致的。

 

话峰一转,此时需要了解一下事务的传递机制,且听下次分析

  

 

 

  

  

 

  

 

 

 

 

  

标签:分析,事务,记录,查询,索引,退化,机制,id
来源: https://www.cnblogs.com/followers/p/16588083.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有