基本介绍

锁是计算机协调多个进程或线程并发访问某一资源的机制。

  • MyISAM和MEMORY存储引擎采用的是表级锁(table-level locking)
  • InnoDB存储引擎既支持行级锁(row-level locking),也支持表级锁,但默认情况下是采用行级锁。

表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

MySQL的表级锁有两种模式:表共享读锁(Table Read Lock)和表独占写锁(Table Write Lock)

MyISAM表锁

  • 对MyISAM表的读操作,不会阻塞其他用户对同一表的读请求,但会阻塞对同一表的写请求;
  • 对 MyISAM表的写操作,则会阻塞其他用户对同一表的读和写操作;
  • MyISAM表的读操作与写操作之间,以及写操作之间是串行的

MyISAM在执行查询语句之前,会自动给涉及的所有表加读锁,在执行更新操作前,会自动给涉及的表加写锁,这个过程并不需要用户干预,因此用户一般不需要使用命令来显式加锁

MyISAM存储引擎的读锁和写锁是互斥的,读写操作是串行的。那么,一个进程请求某个MyISAM表的读锁,同时另一个进程也请求同一表的写锁,MySQL如何处理呢?
答案是写进程先获得锁。不仅如此,即使读请求先到锁等待队列,写请求后到,写锁也会插到读锁请求之前!这是因为MySQL认为写请求一般比读请求要重要。这也正是MyISAM表不太适合于有大量更新操作和查询操作应用的原因,因为,大量的更新操作会造成查询操作很难获得读锁,从而可能永远阻塞。

  • 共享读锁(S)之间是兼容的,但共享读锁(S)与排他写锁(X)之间,以及排他写锁(X)之间是互斥的,也就是说读和写是串行的
  • 在一定条件下,MyISAM允许查询和插入并发执行,我们可以利用这一点来解决应用中对同一表查询和插入的锁争用问题
  • MyISAM默认的锁调度机制是写优先,这并不一定适合所有应用,用户可以通过设置LOW_PRIORITY_UPDATES参数,或在INSERT、UPDATE、DELETE语句中指定LOW_PRIORITY选项来调节读写锁的争用
  • 由于表锁的锁定粒度大,读写之间又是串行的,因此,如果更新操作较多,MyISAM表可能会出现严重的锁等待,可以考虑采用InnoDB表来减少锁冲突。

InnoDB的行锁模式及加锁方法s

共享锁(s):又称读锁。

  • 若一个事务对数据对象A加上S锁,则该事务可以读A但不能修改A。其他事务能再对A加S锁,而不能加X锁,直到T释放A上的S锁。
  • 一旦有事务获取读锁,所有事务只能读不能改。

排他锁(x):又称写锁。

  • 允许获取排他锁的事务更新数据,阻止其他事务取得相同的数据集共享读锁和排他写锁。
  • 若事务T对数据对象A加上X锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的锁。
  • 一旦有事务获取写锁(update,delete,insert),本事务能读能改,其他事务不能进行任何操作。

注意:

select语句默认不会加任何锁类型,如果加排他锁可以使用select …for update语句,加共享锁可以使用select … lock in share mode语句。

InnoDB行锁实现方式

InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的。
InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!

  1. 在不通过索引条件查询的时候,innodb使用的是表锁而不是行锁
  2. 创建带索引的表进行条件查询,innodb使用的是行锁
  3. 由于mysql的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是依然无法访问到具体的数据
    1. insert into tab_with_index values(1,'4');
    | session1 | session2 | | —- | —- | | set autocommit=0 | set autocommit=0 | | select from tab_with_index where id = 1 and name=’1’ for update | | | | select from tab_with_index where id = 1 and name=’4’ for update
    虽然session2访问的是和session1不同的记录,但是因为使用了相同的索引,所以需要等待锁 |