全局锁
顾名思义,全局锁就是对整个数据库实例加锁。MySQL提供了一个加全局读锁的方法,命令是Flushtableswithreadlock(FTWRL)。当你需要让整个库处于只读状态的时候,可以使用这个命令,之后其他线程的以下语句会被阻塞:数据更新语句(数据的增删改)、数据定义语句(包括建表、修改表结构等)和更新类事务的提交语句。
表级锁
MySQL里面表级别的锁有两种:一种是表锁,一种是元数据锁(metadatalock,MDL)。
表锁
表锁的语法是locktablesread/write。与FTWRL类似,可以用unlocktables主动释放锁,也可以在客户端断开的时候自动释放。需要注意,locktables语法除了会限制别的线程的读写外,也限定了本线程接下来的 *** 作对象。
元数据锁
MDL不需要显式使用,在访问一个表的时候会被自动加上。MDL的作用是,保证读写的正确性。你可以想象一下,如果一个查询正在遍历一个表中的数据,而执行期间另一个线程对这个表结构做变更,删了一列,那么查询线程拿到的结果跟表结构对不上,肯定是不行的。
12 事务的ACID原则
13 锁是关系数据库很重要的一部分, 数据库必须有锁的机制来确保数据的完整和一致性 131 SQL Server中可以锁定的资源:
132 锁的粒度:
133 锁的升级: 锁的升级门限以及锁升级是由系统自动来确定的,不需要用户设置 134 锁的类型: (1) 共享锁: 共享锁用于所有的只读数据 *** 作 (2) 修改锁: 修改锁在修改 *** 作的初始化阶段用来锁定可能要被修改的资源,这样可以避免使用共享锁造成的死锁现象 (3) 独占锁: 独占锁是为修改数据而保留的。它所锁定的资源,其他事务不能读取也不能修改。独占锁不能和其他锁兼容。 (4) 架构锁 结构锁分为结构修改锁(Sch-M)和结构稳定锁(Sch-S)。执行表定义语言 *** 作时,SQL Server采用Sch-M锁,编译查询时,SQL Server采用Sch-S锁。 (5) 意向锁 意向锁说明SQL Server有在资源的低层获得共享锁或独占锁的意向。 (6) 批量修改锁 批量复制数据时使用批量修改锁 134 SQL Server锁类型 (1) HOLDLOCK: 在该表上保持共享锁,直到整个事务结束,而不是在语句执行完立即释放所添加的锁。 (2) NOLOCK:不添加共享锁和排它锁,当这个选项生效后,可能读到未提交读的数据或“脏数据”,这个选项仅仅应用于SELECT语句。 (3) PAGLOCK:指定添加页锁(否则通常可能添加表锁)。 (4) READCOMMITTED用与运行在提交读隔离级别的事务相同的锁语义执行扫描。默认情况下,SQL Server 2000 在此隔离级别上 *** 作。 (5) READPAST: 跳过已经加锁的数据行,这个选项将使事务读取数据时跳过那些已经被其他事务锁定的数据行,而不是阻塞直到其他事务释放锁, READPAST仅仅应用于READ COMMITTED隔离性级别下事务 *** 作中的SELECT语句 *** 作。 (6) READUNCOMMITTED:等同于NOLOCK。 (7) REPEATABLEREAD:设置事务为可重复读隔离性级别。 (8) ROWLOCK:使用行级锁,而不使用粒度更粗的页级锁和表级锁。
1、在mysql数据库中如何锁定一行数据,保证不被其他的 *** 作影响。
2、从对数据的 *** 作类型分为读锁和写锁。从对数据 *** 作的粒度来分:表锁和行锁。
3、现在我们建立一个表来演示数据库的行锁讲解。
4、行锁基本演示如下图所示。
5、如果两个会话 *** 作的是不同的行,就不会互相阻塞了。
对表的增删改查,都需要MDL锁,无所不在
MDL读锁之间不互斥,但MDL读写锁互斥
#举个栗子
假设t是一张大表
session1对t执行一个查询(SR)
session2对t执行一个DDL(SU,可能升级到X)
session3对t执行一个查询(SR)
可知session1持有t表的MDL读锁(SR),session1的查询还没有结束的时候,去执行session2的DDL(SU),此时session2需要MDL写锁(SU升级到X,需要X锁),由于MDL读写锁互斥,因此session2需要等待session1释放MDL读锁(SR阻塞X);同时session2对后面的所有MDL读锁互斥(X阻塞SR),因此session2又继续阻塞了session3
#注释:一开始的DDL能看到的状态是SU,但如果SU的某个阶段被阻塞,会被升级到X,从而引发SR阻塞X,达到实验的效果。但实际测试中,DDL是分阶段的,如果没有满足一定的要求,就不会引发阻塞,看到的结果就是SR和SU并没有互相阻塞。这个过程需要具体的去查看源码,此处不展开。
事务中的MDL锁在语句开始时申请,但并不会在语句结束后就马上释放,而是会等到事务结束时才进行释放
忙时对大表DDL会产生的灾难性的结果就是:如果后续对该表有查询 *** 作,而且web端又有重试机制的话,那么会有一个新的session再次发起读请求,反复如此,线程池就会在短时间内爆炸
在线执行DDL的时候,需要检查一下information_schemainnodb_trx表中有没有当前 *** 作表对应的事务,此外还可以使用ALTER TABLE tbl_name NOWAIT进行 *** 作(MySQL80新特性)
eg
session1
select from cpf where payid<>'xxx'
union
select from cpf where payid<>'xxx'
union (union重复50次,确保查询时间几十秒以上)
session2
alter table cpf modify payer_userid varchar(500);
session3
select from cpf where payer_userid='18051512003600300034';
#执行结果
session1执行了31秒,当session1完成的时候session2和session3相继完成
在session4中执行show processlist,结果如下
#变种1
如果session1在执行select之前,添加一句start transaction
会发现session1什么时候执行完commit,sesssion2和session3什么时候完成
也就是证实了在事务中的MDL锁,在语句查询完之后并不会释放,而是会随着事务的释放而释放
#变种2
session1和session3在执行select之前,添加一句start transaction,然后session1,2,3依次按顺序执行
会发现session1阻塞了session2,而session3在执行完start transaction之后就被阻塞,根本没有办法去执行后面的select
当session1执行commit释放之后,session2仍然处于阻塞状态,session3亦是如此
直到session2或者session3当中任意一个执行了停止(navicat客户端 *** 作,类似于rollback)后,另一个才能完成执行
单纯从变种2的结果来看,MDL锁并没有按照执行时间的先后来进行分配,当session1的锁释放之后,session3先获得了读锁
MySQL是server-engine结构,MDL锁是server层的锁
通过show processlist可以发现waiting for table metadata lock,但这还远远不够,需要在performance_schema库中进行设置(MySQL80默认开启)
57临时开启
UPDATE performance_schemasetup_instruments SET ENABLED='YES', TIMED='YES' WHERE NAME='wait/lock/metadata/sql/mdl';
57永久开启(修改cnf配置)
[mysqld]
performance-schema-instrument = 'wait/lock/metadata/sql/mdl=ON'
global:全局级(FTWRL)
schema:库级(drop database)
table:表级(lock table read/write)
commit:提交级
关于global对象,主要作用是防止DDL和写 *** 作的过程中,执行set golbal_read_only = on或flush tables with read lock。
关于commit对象锁,主要作用是执行flush tables with read lock后,防止已经开始在执行的写事务提交。insert/update/delete在提交时都会上(COMMIT,MDL_EXPLICIT,MDL_INTENTION_EXCLUSIVE)锁
DML和DDL在执行之前都会申请IX锁,DML会在global级别上加,而DDL会在global和schema这2个级别上都加IX(也就是2把锁)
IX与大部分锁都是兼容的,除了S,当然了X肯定是不兼容的;但IX与IX之间是兼容的,比如下图
flush table with read lock会持有这个锁(在global级别和commit级别)
FTWRL在全局级和事务级上分别加上了S锁
IX与S是不兼容的
所以DML和DDL都会与FTWRL产生阻塞
逻辑备份第一句:flush table with read lock(S锁)
大表DML(IX锁)
先执行的阻塞后执行的,逻辑备份之前需要检查是否有在线DDL(X锁)以及DML(IX锁),否则逻辑备份产生等待;尽量不要在忙时进行逻辑备份,否则阻碍忙时DML
如下图,前面2行是FTWRL持有的S锁,第3行是一个update语句,IX直接被阻塞,处于pending的锁等待状态;同时由于S锁的持有时间为EXPLICIT,表明FTWRL需要一个显示的释放(unlock tables)
DML并不是只有IX锁,DML和select for update在执行中持有的锁实际是SW锁(DML需要找一个大一点的表来验证,目前只验证了select for update),IX只是DML初期需要获得的锁
如下图是一个select for update语句,start transaction对应的是第2行的SR锁,而语句本身对应的是SW锁
如果在此时执行一个FTWRL,我们会发现2个会话并不会相互阻塞(因为S锁与SR和SW都是兼容的),如下图
但如果我们是先执行的FTWRL再执行的select for update,那么画风就不是像上图那样了
如下图所示,在先执行FTWRL的情况下,select for update压根没有获得SW锁,而是在获取IX锁的过程中就受挫了,一直处于pending状态。(如果这个S锁不释放,那么后面的IX会一直等待,直到超时)
S锁除了逻辑备份时的FTWRL以外,createa table as也会持有这个锁
目前已知的是desc *** 作会持有这个SH锁
SH锁与绝大部分锁都兼容,除开X锁
也就是说在做rename一类的 *** 作的时候,你是无法去执行desc的
前面提到的start transaction,以及所有的非当前读都需要持有这个锁
非当前读的意思就是快照读,也就是普通的select
与SR锁有冲突的有2个,一个是X,另一个是SNRW
研发有时候会很困惑的问我,“我这个表只有几十行数据,select查不出来???” 这时候就需要检查MDL锁了
当前读需要持有此锁,常见的DML和select for update都对应此锁,但不包括DDL
与SW锁有冲突的有4个,SU,SRO,SNRW,X
看到一种说法是这个锁仅对MyISAM引擎生效,冲突范围与SW锁类似
部分alter语句会持有该锁。该锁可能会升级成SNW,SNRW,X;而X锁也有可能逐步降级到SU锁
SU锁和SU,SNW,SNRW,X锁互斥
表面看起来DML的SW锁和SU锁不互斥(DML和DDL),但实际上因为SU锁存在升级的属性,SU锁会升级到SNW锁,从而和SW产生互斥
如下图,SU并没有被SW锁阻塞,但升级到SNW之后,SNW被SW阻塞,一直处于pending状态
SU锁的兼容性如下
查看改过源码的例子,在执行alter的时候,SU会升级到X,之后X降级到SU,然后SU再升级到X
先SU,再SW,SW被SU阻塞
先SW,再SU,SU并未被SW阻塞,但是SU向上升级的过程中产生的SNW被SW阻塞;于是将SW的会话commit,之后SNW向下降级成SU,并成功获得锁;
所以虽然看起来SW和SU不是一个双向阻塞,但实际效果就是双向阻塞,无论DML和DDL谁在前面,都必然会发生相互的阻塞
不兼容的有点多,先贴一个兼容性
SU升级X的过程中会升级成SNW
SU升级成X的过程中,有一个copy的过程,这个过程就是SNW,在这个copy的过程中,允许DML但是不允许select(SR)
copy是一个非常耗时的过程
lock tables read的语句会持有这个锁
SRO阻塞SW,SNRW,X
兼容性如图
lock tables write的语句会持有这个锁
阻塞的锁非常多,除开SH和S以外,其他的都阻塞,连SR都阻塞了
兼容性如下
换句话说flush tables with read lock; (S)会堵塞lock table write; (SNRW)
但是flush tables with read lock;(S)却不会堵塞lock table read (SRO)
阻塞一切
各种DDL均属于这个范畴
create,drop,rename (alter table add column也属于这个范畴)
SW锁阻塞X锁,(X锁是为了去执行一个drop)
X锁阻塞SH
thread104在做一个create table as的表复制 *** 作,在表里面并没有发现X锁的信息,在thread95上对新表做一个desc *** 作,可以看到SH锁处于等待状态,然而这里阻碍SH的并不是X锁
只有1行的select被堵住
thread95做一个start transaction之后不提交,thread107对95的表做出一个rename *** 作,X锁被前面的SR锁阻塞,这时候thread108对该表发起一个limit仅仅为1的查询,但被X锁阻塞。由于lock_wait_timeout这个参数通常是1年,所以一连串查询被堵死
alter开头的几个SQL,无论是modify还是add,查询出来都是SU锁,但DDL是一个过程,其中的有一部分如果发生了阻塞,可能会发现是X锁阻塞;拿SR阻塞X锁的实验来说,SR阻塞X的过程非常短暂,如果没有刚好卡到那个点,看到的结果可能就是SR和SU互不干涉,但如果卡到那个点,就会观测到X被SR所阻塞。具体的需要读源码,这里不展开
SELECT
locked_schema,
locked_table,
locked_type,
waiting_processlist_id,
waiting_age,
waiting_query,
waiting_state,
blocking_processlist_id,
blocking_age,
substring_index(sql_text,"transaction_begin;" ,-1)ASblocking_query,
sql_kill_blocking_connection
FROM
(
SELECT
bOWNER_THREAD_IDASgranted_thread_id,
aOBJECT_SCHEMAASlocked_schema,
aOBJECT_NAMEASlocked_table,
"Metadata Lock"ASlocked_type,
cPROCESSLIST_IDASwaiting_processlist_id,
cPROCESSLIST_TIMEASwaiting_age,
cPROCESSLIST_INFOASwaiting_query,
cPROCESSLIST_STATEASwaiting_state,
dPROCESSLIST_IDASblocking_processlist_id,
dPROCESSLIST_TIMEASblocking_age,
dPROCESSLIST_INFOASblocking_query,
concat('KILL', dPROCESSLIST_ID)ASsql_kill_blocking_connection
FROM
performance_schemametadata_locks a
JOINperformance_schemametadata_locks bONaOBJECT_SCHEMA=bOBJECT_SCHEMA
ANDaOBJECT_NAME=bOBJECT_NAME
ANDalock_status='PENDING'
ANDblock_status='GRANTED'
ANDaOWNER_THREAD_ID<>bOWNER_THREAD_ID
ANDalock_type='EXCLUSIVE'
JOINperformance_schemathreads cONaOWNER_THREAD_ID=cTHREAD_ID
JOINperformance_schemathreads dONbOWNER_THREAD_ID=dTHREAD_ID
) t1,
(
SELECT
thread_id,
group_concat(CASEWHENEVENT_NAME='statement/sql/begin'THEN"transaction_begin"ELSEsql_textENDORDERBYevent_id SEPARATOR ";" )ASsql_text
FROM
performance_schemaevents_statements_history
GROUPBYthread_id
) t2
WHERE
t1granted_thread_id=t2thread_id
MDL锁处理
MDL元数据锁
快速处理MDL锁
以上就是关于mysql中锁的类型有哪些(mysql的锁有几种)全部的内容,包括:mysql中锁的类型有哪些(mysql的锁有几种)、如何解除sql server数据库数据被锁定、mysql如何用事务和锁 锁住某一行数据,使得不允许两个用户同时读取一行数据!!等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)