MySQL锁:InnoDB行锁须要避免的坑

 

前言

  换了工做以后,接近半年没有发博客了(一直加班),emmmm.....今天好不容易有时间,记录下工做中遇到的一些问题,接下来应该重拾知识点了。由于新公司工做中MySQL库常常出现查询慢,锁等待,节点挂掉........等一系列问题。致使每一个程序员头都很大,一味抱怨“为何我就查一条数据这么卡”,"我TM加了索引的啊,怎么还怎么慢"...........我想默默说的是,大部分MySQL出现锁等待,查询奇慢的状况基本都是由于SQL写的很差(有坑),或者数据表设计的不完善。对,不用想!这些全部的坑很大一部分都是本身形成的。那么是什么缘由形成的,大部分只是抱怨,而不去关注MySQL的一些细节问题,好比:MySQL行锁的细节,什么状况下会使用表锁等。因此今天先讨论记录下InnoDB特有的行锁的一些细节,增强认识。html

  InnoDB不一样于MyISAM最大的两个特色就是:一是支持事务,二是支持行锁;毋庸置疑,由于这两个特性大部分都采用InnoDB引擎,其中的支持行锁就是InnoDB适合多并发优点所在,可是行锁的一些细节没有深刻理解过的话,可能会形成必定的误解,形成“看似命中索引,走行锁,结果倒是表锁,最终致使锁等待状况”。程序员

 


1、InnoDB行锁的实现方式

  经过给索引上的索引项加锁来实现的,也就意味着:只有经过索引条件检索数据,InnoDB才使用行级锁,不然,InnoDB将使用表锁。这一点在实际应用中特别须要注意,否则的话可能致使大量的锁冲突,从而影响引起并发性能数据库

  实验一:对没有索引的加锁,致使表锁

   1)准备工做:建tab_no_index表,表中无任何索引,并插入数据并发

    

 

   2)Session_1: 咱们给id=1的行加上排它锁(for update),因为id没有索引,其实是表级锁post

    

   3)Session_2:咱们给id=2的行加上排它锁(for update),因为id没有索引,因此去申请表级锁,可是却出现了锁等待!原因就是在没有索引的状况下,InnoDB只能使用表锁性能

    

    备注:MySQL中的for update 仅适用于InnoDB(由于是只有此引擎才有行级锁),而且必须开启事务,在begin与commit之间才生效。for update是在数据库中上锁用的,能够为数据库中的行上一个排它锁。当一个事务的操做未完成时候,其余事务能够对这行读取可是不能写入或更新,只能等该事务Rollback, Commit, Lost connection…优化

   实验二:对有索引的键值加锁,会对全部涉及到的数据行加锁

    1)准备工做:对id建索引以下url

     

    2)Session_1:此时id是有索引的,咱们对id=1 and name=1的一行加排它锁;spa

    

    3)Session_2:访问不一样于Session_1的id=1, name=5行,可是索引键值是同样的,照样等待锁,锁冲突了。设计

    

   实验三:多个索引时,不一样的事务可使用不一样的索引锁定不一样的行,不论什么索引,InnoDB都会使用行锁对数据加锁(对有索引的行数据)

    1)准备工做:对tab_no_index追加name索引:alter table tab_no_index add index name(name);

    

    2)Session_1:开启事务对id=1的行加排它锁,即对name=1与name=5两个数据加锁。

    

    3)Session_2:开启事务对name=2行加锁,由于该数据没有被加锁,索引能够得到锁

    

    4)Session_3:再对name=5的数据进行加锁,因为该数据记录已被Session_1锁定,因此等待得到锁。

   

  注意事项:即使使用了索引,但仍是要看MySQL具体对SQL的执行计划,不必定能使用到

    如咱们对实验三对name='2'进行加锁,误觉得name是int类型,原本name是有索引的,可是最后结果致使表锁:

    

  

  具体请看MySQL的索引状况。具体能够参考以前个人一篇博文MySQL优化(1)--------经常使用的优化步骤MySQL优化(2)--------经常使用优化

 

2、间隙锁(Next-Key锁)

  当用范围条件而不是相等条件检索数据,并请求共享或者排它锁的时候,InnoDB会给符合条件的已有数据记录的索引项加锁;对于不在范围内的但并不存在的记录,叫作“间隙(GAP)”,InnoDB也会对这个间隙加锁,这就是所谓的间隙锁

  如:select * from where id>100 for update 对id大于100的数据对加锁,可是此时数据中id只有1,2….100,101,不只对存在的101的记录加锁,还会对大于101不存在的数据的间隙加锁。

   此外,对使用相等条件请求给一个不存在的记录加锁,InnoDB也会使用间隙锁,以下:

  Session_1:对不存在的id=6的记录加锁

  

 

  Session_2:插入id=6的记录,也会出现锁等待

  

 

3、何时使用表锁?

  对于InnoDB表,在绝大部分状况下都应该使用行锁,由于事务和行锁每每是咱们之因此选择InnoDB表的理由,但在个别状况下也使用表级锁;

  1)事务须要更新大部分或所有数据,表又比较大,若是使用默认的行锁,不只这个事务执行效率低,并且可能形成其余事务长时间等待和锁冲突;

  2)事务涉及多个表,比较复杂,极可能引发死锁,形成大量事务回滚。

使用表锁须要注意几点:

  1)使用LOCK TABLES虽然能够给InnoDB加表级锁,表级锁不是InnoDB存储引擎层管理的,而是由其上一层MySQL Server负责的

  2)在用LOCK TABLES对InnoDB表加锁时须要注意,要将Autocommit设置为0,不然MySQL不会给表加锁;事务结束前,不要用UNLOCK TABLES释放表锁,由于UNLOCK_TABLES隐含提交事务;COMMIT或ROLLBACK并不能释放用LOCK TABLES加表级锁。

   SET AUTOCOMMIT=0;

   LOCK TABLES table1 WRITE, table2 READ,...;

   [do something....]

   COMMIT;

   UNLOCK TABLES;

 

 


 

总结:

  • 从设计之初,就应该创建良好的索引机制,避免对关键字段搜索时形成表锁;

  • 避免长时间事务未提交等状况,致使锁冲突,死锁等状况

  • 不要总是抱怨数据库有问题,应该从自身写的SQL分析出发,学会分析(数据库不行大部分是由于SQL写的有问题,没错,是自身问题)

  • 不要老是以为这是DBA该作的事,开发者应该学会基本的SQL常识(如MySQL的最左索引,回表,索引覆盖等知识),学会基本的优化步骤。

主要参考资料:《深刻浅出MySQL》(有须要PDF电子书的伙伴能够评论或者私信我)

相关文章
相关标签/搜索