表锁
表锁偏向MyISAM存储引擎,开销小,加锁快;无死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
读锁
其他未持有锁的session可读,写阻塞
持有锁的session可读不可写
session_1 | session_2 |
---|---|
获得表mylock的READ锁 | 连接终端 |
当前session可以查询该表记录 | 其他session也可以查询该表的记录 |
当前session不能查询其他没有锁定的表 | 其他session可以查询或者更新未锁定的表 |
当前session中插入或者更新锁定的表都会提示错误 | 其他session插入或者更新锁定的表会阻塞直到获得锁 |
释放锁 | Session_2获得锁,插入操作完成 |
写锁
未持有锁的session:读写均被阻塞
持有锁的session:可读写
session_1 | session_2 |
---|---|
获得表mylock的WRITE锁定 | 待session_1开启写锁后,session_2再连接终端 |
当前session对锁定表的查询+更新+插入操作都可以执行 | 其他session对锁定表的查询操作被阻塞,需要等待锁被释放 |
释放锁 | session_2获得锁,查询返回 |
表锁小结
MyISAM在执行查询语句时,会自动给涉及的所有表加读锁,在执行增删查改时,会自动给涉及的表加写锁。
- 对MyISAM表的读操作(加读锁),不会阻塞其他进程对同一表的读请求,但会阻塞对同一表的写请求。只有当锁被释放后,才会执行其他进程的写操作
- 对MyISAM表的写操作(加写锁),会阻塞其他进程对同一表的读和写请求。只有当锁被释放后,才会执行其他进程的读写操作。
简而言之,就是读锁会阻塞写,但是不会阻塞读;而写锁会把读和写都阻塞
此外,MyISAM的读写锁调度是写优先,这也是MyISAM不适合做写为主表的引擎。也因为写锁后,其他线程不能做任何操作,大量的更新会使查询很难得到锁,从而造成用于阻塞。因此表锁偏读
分析表锁定
可以通过检查table_locks_waited和table_locks_immediate状态变量来分析系统上的表锁定
show status like 'table%';
- Table_locks_immediate:产生表级锁定的次数,表示可以立即获取锁的查询次数,每立即获取锁值+1
- Table_locks_waited:出先表级锁定争用而发生等待的次数(不能立即获取锁的次数,每等待一次锁+1),此值高说明存在严重的表级锁争用情况
行锁
行锁偏向InnoDB存储引擎,开销大,加锁慢;会出现死锁,但锁粒度最小,发生锁冲突的概率最低,并发度也最高。InnoDB与MyISAM最大不同的两点:一是支持事务;二是采用了行级锁。
Session_1 | Session_2 |
---|---|
关闭事务自动提交 | 关闭事务自动提交 |
更新但是不提交,没有手写commit | Session_2被阻塞,只能等待 |
提交更新 | 解除阻塞,更新正常进行 |
ps:*索引失效,会导致行锁变表锁
间隙锁
当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做间隙。InnoDB会对这个"间隙"加锁,这种锁机制就是所谓的间隙锁"Next-Key锁"
间隙锁的危害
因为Query执行过程中通过范围查找的话,他会锁定整个范围内所有的索引键值,即使这个键值并不存在,这样造成在锁定的时候无法插入锁定键值范围内的任何数据,在某些场景下造成性能的下降。
如何锁定一行
begin;
select xxx .... for update;//锁定一行
commit;
行锁小结
InnoDB存储引擎由于实现了行级锁定,虽然在锁定机制的实现方面所带来的性能损耗可能比表级锁定要更高一点,但是在整体并发处理方面要远远优于MyISAM的表级锁定。当系统并发量较高时,InnoDB的整体性能相比MyISAM会有明显的优势。
但是,InnoDB的行级锁同样也有其脆弱的一面,当我们使用不当时,可能会让InnoDB的整体性能比MyISAM更差。
分析行锁定
通过检查InnoDB_row_lock状态变量来分析系统上的行锁的争夺情况
show status like ‘innodb_row_lock’;
- Innodb_row_lock_current_waits:当前正在等待锁的数量
- Innodb_row_lock_time:从系统启动到现在锁定总时间长度