换了工做以后,接近半年没有发博客了(一直加班),emmmm.....今天好不容易有时间,记录下工做中遇到的一些问题,接下来应该重拾知识点了。由于新公司工做中MySQL库常常出现查询慢,锁等待,节点挂掉........等一系列问题。致使每一个程序员头都很大,一味抱怨“为何我就查一条数据这么卡”,"我TM加了索引的啊,怎么还怎么慢"...........我想默默说的是,大部分MySQL出现锁等待,查询奇慢的状况基本都是由于SQL写的很差(有坑),或者数据表设计的不完善。对,不用想!这些全部的坑很大一部分都是本身形成的。那么是什么缘由形成的,大部分只是抱怨,而不去关注MySQL的一些细节问题,好比:MySQL行锁的细节,什么状况下会使用表锁等。因此今天先讨论记录下InnoDB特有的行锁的一些细节,增强认识。html
InnoDB不一样于MyISAM最大的两个特色就是:一是支持事务,二是支持行锁;毋庸置疑,由于这两个特性大部分都采用InnoDB引擎,其中的支持行锁就是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行,可是索引键值是同样的,照样等待锁,锁冲突了。设计
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锁定,因此等待得到锁。
如咱们对实验三对name='2'进行加锁,误觉得name是int类型,原本name是有索引的,可是最后结果致使表锁:
具体请看MySQL的索引状况。具体能够参考以前个人一篇博文MySQL优化(1)--------经常使用的优化步骤、MySQL优化(2)--------经常使用优化
当用范围条件而不是相等条件检索数据,并请求共享或者排它锁的时候,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的记录,也会出现锁等待
对于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;