其他分享
首页 > 其他分享> > 事务机制的分析

事务机制的分析

作者:互联网

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

  

 

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

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