解析mysql中锁机制

一、MyISAM中锁介绍

MyISAM存储引擎由于实现了表级锁定,MyISAM在执行查询语句(select)前,会自动给涉及的所有表加读锁;在执行增删改操作前,会自动给涉及的表加写锁。

MySQL的表级锁有两种模式:

  • 表共享读锁(table read lock)
  • 表独占写锁(table write lock)

在对MyISAM表进行操作时分析:

  1. 对MyISAM表的读操作(加读锁),不会阻塞其他进程对同一表的读请求,但会阻塞对同一表的写请求。只有读锁释放后,才会执行其他进程的写操作。
  2. 对MyISAM表的写操作(加写锁),会阻塞其他进程对同一表的读和写操作,只有当写锁释放后,才会执行其他进程的读写操作。

总结:读锁会阻塞写,但是不会阻塞读;而写锁会把读和写都阻塞。

手动为表添加锁

lock table read[write];

手动释放锁

unlock tables;

查看哪些表被锁了

show open tables;

表锁的性能分析:

show status like 'table%';
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| Table_locks_immediate      | 135   |
| Table_locks_waited         | 0     |
+----------------------------+-------+

Table_locks_immediate:产生表级锁定的次数,即表示可以立即获取锁的查询次数。每立即获取锁,值+1;

Table_locks_waited:出现表级锁定争用而发生等待的次数(不能立即获取锁的次数,每等待一次,值+1),此值越高说明存在较高的表级锁争用情况;

MyISAM的读写锁调度是写优先,这也是myisam不适合做写为主表的引擎,因为写锁后,其他线程不能做任何操作,大量的更新会使得查询很难得到锁,从而造成永远阻塞。

二、InnoDB中锁的相关问题

介绍:

InnoDB存储引擎由于实现了行级锁定,虽然在锁机制的实现方面所带来的性能损耗可能比表级锁定会要高一些,但是在整体并发处理能力要远远优先于MyISAM的表级锁定。当系统并发量较高时,性能就会有明显优势了。

但是,InnoDB的行级锁定同样也有脆弱的一面,当我们使用不当的时候,可能会让InnoDB整体性能表现比较差。

间隙锁:

当我们使用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做”间隙GAP”

InnoDB也会对这个间隙 加锁,这种锁机制叫做所谓的间隙锁。

危害:

因为Query执行过程中通过范围查找的话,它会锁定整个范围内所有的索引键值,即使这个键值并不存在。导致在锁定的时候无法插入锁定键值范围内的任何数据。在某些场景下对性能有很大危害。

行锁升级为表锁:

当我们在更新某条记录时,由于不正当的操作,导致索引实效,这是行锁就会升级为表锁。

我们先关闭掉InnoDB的自动提交:

set autocommit =0;

我们在A客户端执行更新操作:

update article set name = 'houger' where id ='2';

上述代码我们不小心将主键ID值 加了单引号,导致mysql自定类型转换,此时索引会失效。

我们此时新开一个mysql客户端B,执行其他更新操作:

 update  article set name = 'houger' where id =6;
 -- 阻塞状态

此时sql处于阻塞状态,只有A客户端执行commit操作之后,B客户端才能解除阻塞执行。

InnoDB行锁是通过给索引上的索引项加锁来实现的,因此InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!。

手动为一行添加锁:

我们可以为谋一行添加锁,写法如下:

begin;
select * from tablename where id = n for update;
...
commit;

我们为ID为n的记录添加了行锁,此时其他客户段操作此条记录就会处于阻塞状态,等待commit之后,阻塞解除。

行锁定的状态分析:

mysql> show status like 'innodb_row_lock%';
+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| Innodb_row_lock_current_waits | 0     |
| Innodb_row_lock_time          | 9229  |
| Innodb_row_lock_time_avg      | 4614  |
| Innodb_row_lock_time_max      | 5485  |
| Innodb_row_lock_waits         | 2     |
+-------------------------------+-------+
  • Innodb_row_lock_current_waits:当前正在等待锁定的数量
  • Innodb_row_lock_time:从系统启动到现在锁定总时间长度
  • Innodb_row_lock_time_avg:每次等待所花平均时间
  • Innodb_row_lock_time_max:从系统启动到现在等待最长的一次所花时间
  • Innodb_row_lock_waits:从系统启动到现在总共等待的次数

三、行锁总结

尽可能的让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁;

合理设计索引,尽量减少锁的范围;

尽可能较少检索条件,避免间隙锁的发生;

尽量控制事务大小,减少锁定资源量和时间长度;

发表评论

电子邮件地址不会被公开。 必填项已用*标注