深入浅出mysql笔记--20章锁问题

关系型数据库中,数据组织涉及到两个最基本的结构:表与索引。表中存储的是完整记录,一般有两种组织形式:堆表(所有的记录无序存储),或者是聚簇索引表(所有的记录,按照记录主键进行排序存储)。索引中存储的是完整记录的一个子集,用于加速记录的查询速度,索引的组织形式,一般均为B+树结构。

create table t1 (a int primary key, b int, c int, d int, e varchar(20));

 create index idx_t1_bcd on t1(b, c, d); 

insert into t1 values (4,3,1,1,’d’);

insert into t1 values (1,1,1,1,’a’);

insert into t1 values (8,8,8,8,’h’):

insert into t1 values (2,2,2,2,’b’);

insert into t1 values (5,2,3,5,’e’);

insert into t1 values (3,3,2,2,’c’);

insert into t1 values (7,4,5,5,’g’);

insert into t1 values (6,6,4,4,’f’);

 

t1表的存储结构如下图所示(只画出了idx_t1_bcd索引与t1表结构,没有包括t1表的主键索引):

 

深入浅出mysql笔记--20章锁问题_第1张图片

 

 

简单分析一下上图,idx_t1_bcd索引上有[b,c,d]三个字段(注意:若是InnoDB类的聚簇索引表,idx_t1_bcd上还会包括主键a字段),不包括[a,e]字段。idx_t1_bcd索引,首先按照b字段排序,b字段相同,则按照c字段排序,以此类推。记录在索引中按照[b,c,d]排序,但是在堆表上是乱序的,不按照任何字段排序。

1、MyISAM 表锁,要一次获得所有的锁,当用到lock tables时要把表的别名也要锁定。

2、MyISAM并发插入 concurrent_insert设置为1或2时,插入到表尾。

3、MyISAM写锁优先级高读锁。

4、事务:原子性、一致性(逻辑的完整性)、隔离性、持久性。并发带来的问题:更新丢失、脏读、不可重复读、幻读。

5、事务隔离级别:read uncommitted、已提交读 read committed、可重复读 repeatable read、可序列化 serializable。

6、InnoDB  共享锁(S)、排他锁(X)、意向共享锁(IS)、意向排他锁(IX)。

7、对update delete insert 会自动给涉及数据集加排他锁(X)、对于普通select不会加任何锁。

8、显示加锁

共享锁(s)select * from table_name where ... lock in share mode  应用于更新容易导致死锁

排他锁(X)select * from table_name where ... for update

9、InnoDB 行锁:Record lock 对索引加锁、Gap lock对索引项之间的间隙、第一条记录前的间隙或最后一条记录后的间隙加锁。Next-key lock:前两种的组合,对记录及其前面的间隙加锁。

10、在不通过索引条件查询修改时,InnoDB 会锁定表中的所有记录。

11、由于行锁是对索引加锁,不是针对记录加锁,所以如果不是访问的同行记录,但是使用相同的索引健,是会导致锁冲突的。

12、mysql是先对索引加锁,根据索引项找到对应的记录项,再对记录项加锁。当表有多个索引时,不论是主键索引、唯一索引、或普通索引,InnoDB都会用行锁对数据加锁。

13、Next-Key锁,当用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会对符合条件的加Next-key锁。使用相等条件对一个不存在的记录进行加锁,也会加上Next-Key锁。当采用非唯一索引时在Repeatable read级别,进行请求加锁不管是范围条件还是相等条件都会加Next-Key锁。  Next-Key锁是为了防止幻读。

需要验证 在Read commite级别是进行范围的修改是否会加Next-key锁????有时间都实践一下??

  1. id非唯一索引+RC

 

相对于组合一、二,组合三又发生了变化,隔离级别仍旧是RC不变,但是id列上的约束又降低了,id列不再唯一,只有一个普通的索引。假设delete from t1 where id = 10; 语句,仍旧选择id列上的索引进行过滤where条件,那么此时会持有哪些锁?同样见下图:

深入浅出mysql笔记--20章锁问题_第2张图片

根据此图,可以看到,首先,id列索引上,满足id = 10查询条件的记录,均已加锁。同时,这些记录对应的主键索引上的记录也都加上了锁。与组合二唯一的区别在于,组合二最多只有一个满足等值查询的记录,而组合三会将所有满足查询条件的记录都加锁。

  1. 组合七:id非唯一索引+RR

 

还记得前面提到的MySQL的四种隔离级别的区别吗?RC隔离级别允许幻读,而RR隔离级别,不允许存在幻读。但是在组合五、组合六中,加锁行为又是与RC下的加锁行为完全一致。那么RR隔离级别下,如何防止幻读呢?问题的答案,就在组合七中揭晓。

 

组合七,Repeatable Read隔离级别,id上有一个非唯一索引,执行delete from t1 where id = 10; 假设选择id列上的索引进行条件过滤,最后的加锁行为,是怎么样的呢?同样看下面这幅图:

深入浅出mysql笔记--20章锁问题_第3张图片

此图,相对于组合三:[id列上非唯一锁,Read Committed]看似相同,其实却有很大的区别。最大的区别在于,这幅图中多了一个GAP锁,而且GAP锁看起来也不是加在记录上的,倒像是加载两条记录之间的位置,GAP锁有何用?

 

其实这个多出来的GAP锁,就是RR隔离级别,相对于RC隔离级别,不会出现幻读的关键。确实,GAP锁锁住的位置,也不是记录本身,而是两条记录之间的GAP。所谓幻读,就是同一个事务,连续做两次当前读 (例如:select * from t1 where id = 10 for update;),那么这两次当前读返回的是完全相同的记录 (记录数量一致,记录本身也一致),第二次的当前读,不会比第一次返回更多的记录 (幻象)。

14、什么时候使用表锁.1)、对表的大部分数据都要更新,表锁效率比较高。2)、事务设计多个表比较复杂防止死锁。

15、死锁产生的原因????

MyISAM总是一次获得所有的锁,所以不会有死锁。

1、InnoDB访问表顺序造成的死锁。A:开启事务对表a加行锁,B:开启事务对表b加行锁,A这时需要对表b加行锁等待事务B释放;B这时需要对表a加行锁等待事务A释放,产生相互等待。

2、对同一张表数据操作顺序不一致。事务A对行1加锁等待行2加锁;事务B对行2加锁等待行1加锁。

3、通过获得共享锁再升级到排斥锁时容易导致死锁。、

4、对不存在的记录,在repeatable read级别下都会加排他Next-Key锁成功。然后再同时插入会导致死锁。

例如进行删除操作,但是原本就没有记录,这时会加上范围的Next-key锁。进行删除时要先判断是否存在,并加锁再删除。

5、不同索引时,相关索引加锁顺序不同,导致死锁。在一条更新语句里A语句:直接用了非主键索引;语句B用了主键索引和非主键索引,可能会导致A语句在非主键索引加锁后,请求在主键索引加锁等待;B语句先在主键索引加锁后,请求在非主键索引加锁等待。  在分析死锁之前需要查询一下mysql的执行计划,看看是否用到了索引,用到了哪个索引,对于没有用索引的操作会采用表级锁。如果操作用到了主键索引会先在主键索引上加锁,然后在其他索引上加锁,否则加锁顺序相反。在并发度高的应用中,批量更新一定要带上记录的主键,优先获取主键上的锁,这样可以减少死锁的发生。

电商无论前台后台的程序,都不应该存在仅根据非主键的几个字段一查就要update/delete的场景。即使有,也应该改为先把要更新的记录查出来然后逐条按主键id更新。

具体案例参考:https://blog.csdn.net/wwd0501/article/details/78052149

具体案例参考:https://blog.csdn.net/lzy_lizhiyang/article/details/52678446

index merge死锁:如果sql where里同时使用了type和status,因为type和status上都有单字段索引,所以explain会发现使用了index merge

有的sql使用的索引是先idx_type再idx_status,有的先idx_status再idx_type


6、不同索引加锁对应的主键加锁顺序不同导致死锁。A加锁 (1234)B加锁(4321);


7、如果走二级索引,边扫描边对二级索引行加X锁及间隙加GAP锁,然后再根据二级索引里的主键信息去扫描聚簇索引对主键行加X锁。

     案例:https://yq.aliyun.com/articles/332485?utm_content=m_38908 有些不明白??不明白如何会用到同一主键的记录???

8、Next-Key导致的死锁。在非唯一索引下进行并发更新后插入。或者删除记录,如果记录不存在会加Next-Key锁,避免死锁:存在才删除,尽量不去删除不存在的记录。

16、避免死锁的方法:

1、尽量用较低的隔离级别

2、精心设计索引,尽量使用索引去访问数据,使加锁更加精确。

3、合理使用事务尽量使用小事务。

4、修改记录时直接申请排他锁,不要先申请共享锁再申请排他锁。

5、访问同一组表时约定访问顺序,避免死锁。

6、修改数据时,使用相等条件访问数据,避免采用访问条件访问数据,可以避免Next-Key锁。

7、特定批量修改同一表大量数据用表锁。

8、可以更加二级索引查询出数据主键再根据主键进行修改和删除。

  1. UPDATE open_notify_queue t0 , (SELECT queue_id FROM open_notify_queue WHERE next_consume_time < 1474773160584) t1  
  2. SET STATUS=3  
  3. WHERE t0.queue_id = t1.queue_id 





你可能感兴趣的:(数据库)