说一说 MySQL的锁机制(行锁、表锁、间隙锁、Next-Key Lock)

锁的操做类型分类
  • 读锁共享锁,多个读操做能够对同一份数据同时进行而不会互相影响。数据库

  • 写锁排他锁,在写操做未完成以前,会阻止其余的写锁与读锁。并发

锁的操做粒度分类
  • 表锁: 偏向于读,MyiSAM
  • 行锁:偏向于写,InnoDB

MyiSAM

  • 在进行SELECT 操做前,MyiSAM会给涉及到的表加读锁。这个时候其余Session能够正常对未加锁的表进行操做。可是对加了读锁的表,只能对其进行查询(共享锁),对其修改则会阻塞,等待至表解锁后,才会生效。高并发

  • Session1 Session2
    给TABLE_A加读锁 无操做
    能够对TABLE_A进行查询,不能对TABLE_A进行修改 能够对TABLE_A进行查询,不能对TABLE_A进行修改
    查询修改 TABLE_B 报错 查询修改 TABLE_B正常
    修改TABLE_A报错 修改TABLE_A阻塞
    Unlock tables;解锁 TABLE_A被修改
  • 在进行写操做前,MyiSAM会给涉及到的表加写锁。这个时候其余Session能够正常对未加锁的表进行操做。可是对加了写锁的表,对其进行读或写,都会阻塞,直至写锁释放后,才会进行相应操做。post

    • Session1 Session2
      给TABLE_A加写锁 无操做
      能够对TABLE_A进行修改,不能对TABLE_A进行查询 不能对TABLE_A进行查询,修改
      不能对TABLE_B进行操做 能够对TABLE_B进行操做
      查询TABLE_A报错 查询或者修改TABLE_A阻塞
      Unlock tables;解锁 TABLE_A被查询出结果或被修改
  • MyiSAM的读写锁调度是写优先,这也是MyiSAM不适合做为以写为主的引擎。由于写锁后,其余线程不能进行任何操做,大量的写入操做会使得查询很可贵到锁,从而形成永久堵塞。优化



IMPORTANTspa

通俗地讲:.net

  • 读锁就是:我要备份,后面来的先别乱动(修改)东西;线程

  • 写锁就是:我要修改,后面来的别急,排队(无论你是要读仍是要改,都得等我此次改完)。设计



InnoDB

InnoDB的锁机制为行锁,行锁支持事务。code



事务的ACID属性:
  • A 原子性(Atomic):指一个事务为最小单位不可再往下划分,要么全执行,要么都不执行。
  • C 一致性(Consistency):指数据在事务执行以前是一致的,执行以后也是一致的,即:不会破坏数据库的完整性(完整性:逻辑与业务上的符合逻辑即为完整性)。
  • I 隔离性(Isolation):指事务在执行的过程不会被外界的其余事务或数据库操做干扰,数事务执行过程当中的中间态对外不可见。
  • D 持久性(Durability):事务执行完成以后对数据库的影响是持久的,不会回滚。


并发事务带来的问题:
  • 更新丢失:多个事务同时更新同一行的数据,而且不知道其余事务的存在,致使最后有的更新失效(丢失)了。

  • 脏读:事务在更新数据的过程当中(已经修改了数据还没有提交),去读取数据,读到了中间态的数据,这些数据不符合一致性要求,即为脏读。

  • 不可重复读:在一次的事务操做中,对某一数据进行了两次(及以上)的读操做,此时有另外一个事务在两次读操做的中间修改了数据,形成了两次读取的数据不一样,即不能够重复读(否则数据会不同)。

  • 幻读:在一次的事务操做中,先读取了几行数据后,另外一个事务又增长或删除了数据,在此以后,此事务又去读取数据,发现数据凭空生成或消失,跟幻觉同样,即幻读。

总结:更新丢失为多个事务几乎同时修改数据出现的问题;脏读为一个事务在修改数据,另外一个事务读取(SELECT)事务出现的问题;不可重复读为一个事务在查询数据,中间有另外一个事务修改(UPDATE)了数据的问题;幻读为一个事务在查询数据,另外一个事务在插入或删除(INSERT or DELETE)数据的问题。



事务的隔离级别:
  • 未提交读Read Uncommitted:最低级别,只能避免不读到物理修改数据过程的数据,数据的逻辑修改的中间态依然存在,破坏了数据一致性,上述问题同时存在。
  • 已提交读Read Committed:语句级,保证了语句的原子性,只能读到已经提交的内容,可是在修改数据过程当中并无加锁,为何只会读到已经提交的数据内容呢?,这是使用了“快照读”的方式优化了,使得咱们在修改数据的同时,查询不会被阻塞,能够完成高并发的查询,大大提升了效率;可是由于修改的过程当中没有加锁,则会出现两次查询数据的过程当中,数据中间被其余事务修改或者增添数据了,形成不可重复读和幻读。
  • 可重复读Repeated Read:事务级,MySQL默认隔离级别,从名字看就知道了,避免了不可重复读的问题。普通的查询一样是经过“快照读”的方式,来避免脏读的出现,在此基础上又加入了一个在事务开启的同时,不能对涉及到的数据行进行修改,从而避免了“同一次事务中读到的数据不一致”的不可重复读的问题,可是没有避免幻读(在InnoDB中解决了幻读「间隙锁加行锁)。
  • 可序列号Serializable:最高级别,事务级,串行执行事务,即一个一个排队执行事务,这种级别下,全部的并发事务问题都会被避免,可是因为从并行操做变成了串行排队操做,效率大大下降。


已提交读与可重复读是实际开发中最常用到的两种事务隔离级别,这二者主要是经过MVCC(Multi Version Concurrency Control对并发事务问题的解决。

  • Read Commited的作法是在事务的每一条SQL语句执行前生成一个快照,此时其余并发事务去读取这个数据时,避免了脏读的出现。
  • Repeated Read的作法是在事务的第一次查询前生成一个快照,以后在这一次事务的读取过程当中,都去读取这一次快照,从而避免了脏读和不可重复读。


总结锁与隔离级别与并发问题的关系:

在默认的隔离级别下,咱们在对某几行数据进行修改或者查询的时候,只会锁住这几行数据不被修改,从而保证避免了不可重复读的出现;而咱们即便对整张表全部行都进行操做了,那也是锁住了这张表的全部行,而不是锁住这张表,不能阻止表插入新的行,从而依然会出现幻读的状况(间隙锁+行锁的Next-Key Lock解决了此问题),而最高的隔离级别则是经过将事务串行化,咱们在执行查询事务的同时是不可能有其余事务来插入数据的,从而避免了幻读的出现。



间隙锁(Gap Lock)

当咱们在查询语句时,条件为范围查询时,InnoDB无论这个区间是否有数据,都会将其锁住,向这个区间的“间隙”(不存在的行)插入或删除数据都会阻塞。



Next-Key Lock

Next-Key Lock = Record Lock + Gap Lock InnoDB在默认隔离级别(Repeated Read)下,使用Next-Key Lock的方案解决了幻读的问题。

即在进行范围性的SELECT时,咱们先对已经存在的Records加上Record Lock,再对此区间的间隙加上Gap Lock,从而解决了幻读的问题。



索引失效会使得行锁变成表锁

缘由:索引失效致使的全表扫描,使得从行锁->表锁。


如何锁定一行

select … for update 语句




优化建议

  • 尽量让全部数据的检索都经过索引完成,避免无索引行锁升级为表锁。
  • 合理设计索引,尽可能缩小锁的访问。
  • 尽量减小检索条件 避免间隙锁的危害。
  • 尽可能控制事务大小,减小锁定资源量和时间长度。
  • 尽量低级别事务隔离。


InnoDB与MyiSAM最大的不一样点:是InnoDB支持事务、行锁、外键,MyiSAM不支持。具体文章
相关文章
相关标签/搜索