对数据操作的类型:
读锁(共享锁)—— 同一数据,多个读操作可以同时进行
写锁(排他锁)—— 写操作未完成前,会阻断其他读锁和写锁
对数据操作的粒度:
表锁(偏读)—— 偏向MyISAM存储引擎,开销小,加锁快,无死锁,锁定粒度大,发生锁冲突的概率最高,并发度最低。
行锁(偏写)—— 偏向InnoDB存储引擎,开销大,加锁慢,会出现死锁,锁定粒度小,发生锁冲突的概率最低,并发度也最高。
一、表锁操作
MyISAM引擎用表锁较多(偏读)
1、表锁语句
(1)加锁:假如给表mylock加读锁,表book加写锁
lock table mylock read,book write;
(2)查看加锁的表,In_use=1表示加了锁
show open tables;
(3)查看Mysql内部表级锁定情况
show status like 'table%';
table_locks_immediate:产生表锁的次数
table_locks_waited:出现表锁争用而发生等待的次数(每等待一次+1),值高说明争用表锁严重
(4)解所有锁
unlock tables;
2、在MyISAM引擎下,表锁冲突演示
【情景1】:在session1为表1加读锁
session1 session2
能读表1 能读表1
不能读其他未锁表,要先释放表1锁 能读或写其他未锁表
不能写表1,报错 写表1会阻塞
【情景2】:在session1为表1加写锁
session1 session2
能读表1 读表1会阻塞
不能读其他未锁表,要先释放表1锁 能读或写其他未锁表
能写表1 写表1会阻塞
结论:读锁会阻塞写,但不阻塞读。写锁会阻塞读写。
MyISAM在select前,会自动给涉及的所有表加读锁,在执行增删改操作前,会自动给涉及表加写锁。
MyISAM的读写锁调度写优先,大量更新会使查询很难得到锁,因此不适合做写为主的引擎。
二、行锁操作
InnoDB与MyISAM的最大不同有两点:一是支持事务,二是采用了行级锁
1、并发问题:
(1)更新丢失 —— 由于事务间不知道其他事务的存在,会发生覆盖其他事务更新的情况,如果在一个程序员完成并提交事务前,另一个程序员不能访问同一文件,则可避免此问题。
(2)脏读 —— 事务A读取到了事务B已修改但未提交的数据,如果B回滚,A读的数据无效。
(3)不可重复读 —— 某事务两次读取的数据不一致。
(4)幻读 —— 事务A读取到事务B提交的新增数据。
隔离级别:
2、在Innodb引擎下,行锁冲突演示,Innodb默认是行锁
【情景1】
session1 session2
set autocommit=0; set autocommit=0;
更新但不提交,未commit 更新同一行时会阻塞,更新不同行不会阻塞
提交 解除阻塞,提交
3、索引失效行锁变表锁
若session1执行的写操作造成索引失效,会从行锁变为表锁,其他session对表写会阻塞
4、间隙锁带来的插入问题
用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会锁定整个范围内所有的索引键值,即使这个键值不存在,而锁定范围时无法在锁定键值范围内插入数据,某些场景下可能存在危害。
5、如何锁定一行
begin;
select * from 表 where 条件 for update;
commit;
#通过for update锁定某行后,其他的操作会被阻塞,直到锁定行的会话提交commit
6、查看行锁信息:
show status like 'innodb_row_lock%';
#Innodb_row_lock_current_waits:当前正在等待锁定的数量
#Innodb_row_lock_time:从系统启动到现在锁定总时间长度
#Innodb_row_lock_time_avg:每次等待所花平均时间
#Innodb_row_lock_time_max:从系统启动到现在等待最长的一次所花的时间
#Innodb_row_lock_waits:系统启动后到现在总共等待的次数
7、结论
Innodb存储引擎由于实现了行级锁定,虽然在锁定机制的实现方面所带来的性能损耗可能比表锁更高一些,但在整体并发处理能力方面远远优于MyISAM的表锁,当系统并发量较高时,Innodb明显优于MyISAM。
优化建议:尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁。
*页锁:开销和加锁时间界于表锁和行锁之间,会出现死锁,锁粒度界于表锁和行锁之间,并发度一般。