赞
踩
从粒度上来说,可以将锁分为全局锁、表锁、页锁和行锁
-- 全局锁,整个数据库处于只读状态,其他操作均阻塞
FLUSH TABLES WITH READ LOCK
-- 释放全局锁
UNLOCK TABLES
mysql> LOCK TABLES 表名 READ|WRITE;
mysql> UNLOCK TABLES
【注意】MySQL不同的存储引擎支持不同的锁机制,所有存储引擎都以自己的方式显现了锁机制,服务器层完全不了解存储引擎中的锁实现。默认情况下表锁和行锁都是自动获得的,不需要额外的命令,但某些情况下用户需要明确的进行行锁或进行事务的控制,以便确保整个事务的完整性,这样就需要使用事务控制和锁定语句来完成。
默认情况下,写锁比读锁具有更高的优先级:当一个锁释放时,这个锁会优先给写锁队列中等候的获取锁请求,然后再给读锁队列中等候的获取锁请求。(因此MyISAM表不适用于有大量更新操作和查询操作的应用,因为大量的更新操作会造成查询操作很难获取读锁,从而可能永远阻塞)
可以设置参数low-priority-updates
,使MyISAM引擎默认给予读请求优先的权利。
MyISAM在执行select语句之前会自动给涉及的表加读锁;在执行更新操作前会自动给涉及的表加写锁,这个过程不需要用户干预,因此一般不需要直接使用lock table给MyISAM表显示加锁。
自动加锁的情况下,MyISAM总是一次获得SQL语句所需要的全部锁,这也正是MyISAM表不会出现死锁的原因
可以通过检查table_lock_waited
和table_locks_immediate
状态变量来分析系统上表锁的争夺,如果table_lock_waited
的值比较高,则说明存在着较严重的表级锁争用情况。
InnoDB实现了以下两种类型的行锁:
为了允许行锁和表锁共存,实现多粒度锁机制,InnoDB还有两种内部使用的意向锁,这两种意向锁都是表锁。
意向锁是InnoDB自动加的,不需用户干预:对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及的数据集加排他锁;对于普通的select语句,InnoDB不会加任何锁。
事务可以通过以下语句显式给记录集加共享锁或排他锁:
select * from table_name where ... lock in share mode;
其他session仍然可以查询记录,并也可以对该记录加share mode的共享锁,但如果当前事务需要对该记录进行更新操作,则很有可能造成死锁
select * from table_name where … for update
其他session可以查询该记录,但是不能对该记录加共享锁或排他锁,而是等待获得锁。
select ... for update
在执行这个语句的时候会将响应的索引访问条目上排他锁,也就是说这个语句对应的锁就相当于update带来的效果。
【使用场景】为确保自己查到的数据是最新数据,并且查到后的数据只允许自己来修改。
【性能影响】业务繁忙的情况下,如果没有及时commit或者rollback可能会造成其他事务长时间等待,从而影响数据库的并发使用效率
select ... lock in share mode
作用是将查找的数据加上一个share锁,这个就是表示其他的事务只能对这些数据进行简单的select操作,并不能够进行DML操作
【使用场景】确保自己查到的数据没有被其他事务正在修改,确保查到的数据是最新数据。但自己不一定能修改数据,因为有可能其他事务对这些数据使用了in share mode
的方式上了S锁。
【性能影响】如果没有及时commit
或者rollback
可能会造成其他事务长时间等待
InnoDB行锁是通过给索引上的索引项加锁来实现的(Oracle是在数据库块中对相应数据行加锁实现)。【意味着】InnoDB只有通过索引条件检索数据才使用行级锁,否则使用表锁。
只有执行计划真正使用了索引才会使用行锁。
当使用范围条件而不是相等条件检索数据并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项枷锁;对于键值在条件范围内但不并不存在的记录叫做“间隙”,InnoDB也会对这个间隙加锁,这种机制叫间隙锁。
显然,这种加锁机制会阻塞符合条件范围内键值的并发插入,往往会造成严重的锁等待。因此实际开发中,尤其是并发插入比较多的应用,应尽量优化业务逻辑,尽量使用相等条件来访问更新数据,避免使用范围条件。
间隙锁的【目的】:防止幻读,满足相关隔离级别要求;满足恢复和复制的需要。
可以通过检查InnoDB_row_lock状态变量来分析系统上的行锁争夺情况:
Innodb_row_lock_waits和innodb_row_lock_avg的值来判断行锁争用情况,值高意味着竞争比较严重。Innodb_row_lock_waits指系统启动到现在总共在等待的次数。
单表加写锁:lock table tb_name write;
其他线程不能对该表进行读写操作,当前线程可以读写
单表加读锁:lock table tb_name read;(本线程只能读,其他线程也只能读)
【产生原因】两个或多个事务在同一资源上相互占用,并请求锁定对方占用的资源从而导致恶行循环。多个事务同时锁定同一个资源是也可能会产生死锁。
【死锁检测】
【锁优化】
A.精心设计索引,并尽量使用索引来访问数据,使锁更加精确从而减少所冲突的机会
B.选择合理的事务大小,小事务发生所冲突的几率也更小
C.最好一次性请求足够级别的锁(修改数据直接申请排他锁)
D.不同程序访问一组表时,尽量约定以相同的顺序访问各表
E.尽量使用相等条件访问数据,这样可以避免间隙锁对并发插入的影响
F.除非必须,查询时不显示加锁
G.对于一些特定的事务可以使用表锁来提高处理速度或减少死锁的可能
【MySQL的解决方案】
A.增加锁超时时间,MySQL默认的等待时间为50秒,设置较高的锁超时时间可以避免死锁问题的发生
B.优化事务,事务中应避免使用长时间的锁等待
C.使用InnoDB引擎,支持行级锁定,减少锁定范围,可以更精细地控制锁定的资源
【MySQL死锁检测】
A.SHOW ENGINE INNODB STATUS
命令,在结果中查找“LATEST DETECTED DEADLOCK”关键字,如果存在该关键字,则表示存在死锁
B.查看当前未提交的事务(如果死锁等待超时,事务可能还没有关闭)
select * from information_schema.INNODB_TRX;
C.查看正在被访问的表
show open tables where in_use > 0;
D.查看当前出现的锁(8.0版本的)
select * from performance_schema.data_locks;
ENGINE
:持有或请求锁的存储引擎。
ENGINE_LOCK_ID
:存储引擎持有或请求的锁的ID。(ENGINE_LOCK_ID, ENGINE)值对是唯一的。
ENGINE_TRANSACTION_ID
:请求锁的事务的存储引擎内部ID。
THREAD_ID
:创建锁的会话的线程ID。
EVENT_ID
:导致锁的Performance Schema事件。
OBJECT_SCHEMA
:包含锁定表的模式(数据库名)。
OBJECT_NAME
:锁定的表的名称。
LOCK_TYPE
:锁的类型,如RECORD(行级锁)或TABLE(表级锁)。
LOCK_MODE
:如何请求锁,如S(共享锁)、X(排他锁)等。
LOCK_STATUS
:锁请求的状态,如GRANTED(锁被持有)或WAITING(锁正在等待)。
LOCK_DATA
:与锁相关联的数据(如果有)。对于InnoDB,如果LOCK_TYPE是RECORD,会显示被锁记录的主键值或次级索引值。
E.查看锁等待对应关系
select * from performance_schema.data_lock_waits;
F.死锁记录默认只记录最近一个死锁信息,若要将每个死锁信息都保存到错误日志,启用以下参数:
G.查看行锁信息:
show status like ‘innodb_row_lock_%’;
FLUSH TABLES WITH READ LOCK
query cache lock
对QC中的数据有更新时,都会引发query cache lock
② MDL锁
meta data lock,事务内对库、表、procedure、function、triggers、event、tablespace等多种对象上加的锁。
事务开启后,会锁定表的meta data lock,其他会话对表有DDL操作时,均需等待MDL释放后执行。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。