INDEX--创建索引和删除索引时的SCH_M锁

最近有一个困惑,生产服务器上有一表索引建得乱七八糟,经过整理后需要新建几个索引,再删除几个索引,建立索引时使用联机(ONLINE=ON)创建,查看下服务器负载(磁盘和CPU压力均比较低的情况)后就选择业务时间创建,但是到删除索引时却遇到问题:阻塞,删除索引需要架构修改锁(SCH_M),有阻塞很正常,虽然查询使用NOLOCK提示降低了对其他会话的影响,但还是会在页或表上生成一些意向共享锁(IS),这些意向共享锁与SCH_M无法兼容,因此阻塞无可避免,悲催的是在该表上多个会话重复执行查询且该查询执行时间超过100秒,根本无法找到一个完美的时间空挡来执行删除操作。想着白天业务高峰不成,我晚上来,为此好几晚半夜爬起来做尝试删除操作,最后还是跟业务确认后使用KILL干掉所有长时间阻塞会话才得以删除成功。

啰啰嗦嗦一堆,问题来了:在联机创建索引时,同样需要架构修改锁(SCH_M),为什么这就不阻塞呢?

感谢群里大神“一川晴雨”提醒,联机创建索引和删除索引虽然都使用架构修改锁(SCH_M),但是作用的对象却是不同的,因此影响也不同,那就让我们来验证下吧

首先准备测试数据

--=============================
--创建测试数据库
CREATE DATABASE DB2
GO
USE db2
GO
--创建测试表
CREATE TABLE TB1004
(
    ID INT IDENTITY(1,1) PRIMARY KEY,
    C1 BIGINT
)

GO
--导入数据,本次测试导入100w数据
INSERT INTO TB1004()
SELECT OBJECT_ID FROM SYS.all_columns
go 2000

--查询导入的数据量
SELECT  COUNT(1) FROM TB1004

接下来就是准备抓起锁,我们使用XEVENT来完成

--创建扩展回话XE_LockMonitor
--增加监控事件sqlserver.lock_acquired和sqlserver.lock_released
--并按锁类型和数据库名来过滤数据
CREATE EVENT SESSION [XE_LockMonitor] ON SERVER
ADD EVENT sqlserver.lock_acquired(
    ACTION(sqlserver.database_id,sqlserver.database_name,sqlserver.sql_text)
    WHERE ([sqlserver].[equal_i_sql_unicode_string]([sqlserver].[database_name],N‘DB2‘) AND [mode]=(2))),
ADD EVENT sqlserver.lock_released(
    ACTION(sqlserver.database_id,sqlserver.database_name,sqlserver.sql_text)
    WHERE ([sqlserver].[equal_i_sql_unicode_string]([sqlserver].[database_name],N‘DB2‘) AND [mode]=(2)))
ADD TARGET package0.event_file(SET filename=N‘D:\DB\XE_LockMonitor.xel‘)
WITH (MAX_MEMORY=4096 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,MAX_DISPATCH_LATENCY=30 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=NONE,TRACK_CAUSALITY=OFF,STARTUP_STATE=OFF)
GO

建立完后查看该扩展事件属性

有了扩展事件会话,然后我们激活启用它

--=============================================================
--启动回话
ALTER EVENT SESSION [XE_LockMonitor] ON SERVER
STATE=START;

启动扩展会话后,选择“监视实时数据”,然后在弹出的窗口中,先配置要显示的数据,在标题栏右键选择“选择列”,选择以下我们关心的列,并保存。

准备好测试环境,是时候开测数据啦

--========================
--脱机创建索引
--耗时5秒
CREATE INDEX IDX_OBJECTID
ON TB1004(ID)
WITH(ONLINE=OFF,MAXDOP=1)
GO
--========================
--删除索引
DROP INDEX IDX_OBJECTID ON TB1004
--========================
--联机创建索引
--耗时53秒
CREATE INDEX IDX_OBJECTID
ON TB1004(ID)
WITH(ONLINE=ON,MAXDOP=1)
GO
--========================
--删除索引
DROP INDEX IDX_OBJECTID ON TB1004

扩展会话捕获到的数据:

使用SELECT OBJECT_NAME(1269579561)查看发现OBJECT对象为TB1004

由上面的数据我们不难发现这么几个结论:

1.无论联机还是脱机创建索引时,架构修改锁的对象为HOBT和METADATA

2.删除索引操作时,架构修改锁的对象为OBJECT:TB1004

3.联机索引创建耗时53秒,脱机索引创建耗时53秒(在没有外部数据操作情况下),脱机索引创建耗时远小于联机索引创建

--============================================================

是时候揭晓谜底啦

我们开启一个会话,执行下面SQL:

--使用NOLOCK访问表
SELECT * FROM TB1004 WITH(NOLOCK)

再另外开启一个会话,执行下面SQL:

--====================================================
--使用SP_LOCK来获取锁
--查找某个对象上的锁
DECLARE @T TABLE
(
 SPID BIGINT,
 DataBaseID INT,
 OBJECTID BIGINT,
 IndexID BIGINT,
 LockType VARCHAR(20),
 LockResource NVARCHAR(200),
 LockMode NVARCHAR(20),
 LockStats NVARCHAR(200)
)
INSERT INTO @T
EXEC SP_LOCK

SELECT SPID
,DataBaseID
,DB_NAME(DataBaseID) AS DataBaseName
,OBJECTID
,OBJECT_Name(OBJECTID,DataBaseID) ObjectName
,IndexID
,LockType
,LockResource
,LockMode
,LockStats
FROM @T
WHERE OBJECTID=OBJECT_ID(‘TB1004‘)

我们发现,即使使用NOLOCK提示,仍需要SCH_S锁,这就是为什么DROP INDEX时被阻塞的原因,因为在同一个资源(object_ID:1269579561)上有互斥的SCH_M锁和SCH_S锁。

而对于联机索引创建,索引创建会话使用的SCH_M锁的对象与NOLOCK查询的使用的SCH_S锁的对象不是同一个,因此不会阻塞。

相信诸位看官到此应该深深地明白WHY了吧。

--=====================================================================

再次感谢群友”一川晴雨“,妹子为你而上

INDEX--创建索引和删除索引时的SCH_M锁

时间: 2024-10-29 07:59:26

INDEX--创建索引和删除索引时的SCH_M锁的相关文章

MySQL创建索引、删除索引和查看查索引

1.索引作用 在索引列上,除了上面提到的有序查找之外,数据库利用各种各样的快速定位技术,能够大大提高查询效率.特别是当数据量非常大,查询涉及多个表时,使用索引往往能使查询速度加快成千上万倍. 例如,有3个未索引的表t1.t2.t3,分别只包含列c1.c2.c3,每个表分别含有1000行数据组成,指为1-1000的数值,查找对应值相等行的查询如下所示. SELECT c1,c2,c3 FROM t1,t2,t3 WHERE c1=c2 AND c1=c3 此查询结果应该为1000行,每行包含3个相

mysql 创建索引和删除索引

索引的创建可以在CREATE TABLE语句中进行,也可以单独用CREATE INDEX或ALTER TABLE来给表增加索引.删除索引可以利用ALTER TABLE或DROP INDEX语句来实现.(1)使用ALTER TABLE语句创建索引.语法如下:alter table table_name add index index_name (column_list) ;alter table table_name add unique (column_list) ;alter table ta

mysql 创建索引、重建索引、查询索引、删除索引 转自:http://www.phpernote.com/mysql/942.html

本篇文章主要是对MySQL索引操作方法做了一下总结,包括创建索引.重建索引.查询索引.删除索引的操作.以下所列示例中中 `table_name` 表示数据表名,`index_name` 表示索引名,column list 表示字段列表(如:`id`,`order_id`). 1.创建索引 索引的创建可以在CREATE TABLE语句中进行,也可以单独用CREATE INDEX或ALTER TABLE来给表增加索引.以下命令语句分别展示了如何创建主键索引(PRIMARY KEY),联合索引(UNI

MySql创建索引、删除索引、新增字段、删除字段、修改字段语句

--------------------------------------------------------- -- ALTER TABLE 创建索引 --------------------------------------------------------- -- 创建主键 ALTER TABLE `table_name` ADD CONSTRAINT PRIMARY KEY( `column` ); -- 创建主键 ALTER TABLE `table_name` ADD PRIM

索引分类、创建索引、删除索引

1.普通索引没有索引类型: 2.唯一性索引: 索引起别名: 3.多列索引: 在已有表上创建索引: 表4 CREATE INDEX index_userName ON t_user4(userName); 创建唯一性索引:(有别名) CREATE UNIQUE INDEX index_password ON t_user4(PASSWORD); 多列索引: 4.用ALTER TABLE创建索引: 唯一性索引: 多列索引: 删除索引: DROP INDEX index_password ON t_u

MongoDB索引管理——创建索引,查看索引,删除索引,重建索引

先给users集合插入两条记录,然后用users集合来进行索引管理的演示: > user1={"name":"liming","age":20,"gender":"F"} { "name" : "liming", "age" : 20, "gender" : "F" } > db.users.in

es手动创建索引,修改索引,删除索引

1.创建索引 创建索引的语法PUT /my_index{ "settings": { ... any settings ... }, "mappings": { "type_one": { ... any mappings ... }, "type_two": { ... any mappings ... }, ... }} 创建索引的示例PUT /my_index{ "settings": { "

MySQL 添加索引,删除索引及其用法

一.索引的作用 一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,遇到最多的,也是最容易出问题的,还是一些复杂的查询操作,所以查询语句的优化显然是重中之重. 在数据量和访问量不大的情况下,mysql访问是非常快的,是否加索引对访问影响不大.但是当数据量和访问量剧增的时候,就会发现mysql变慢,甚至down掉,这就必须要考虑优化sql了,给数据库建立正确合理的索引,是mysql优化的一个重要手段. 索引的目的在于提高查询效率,可以类比字典,如果要查“mysql

对于mysql加索引,删除索引,添加列,删除列,修改列顺序的最佳办法测试

1.首先进行数据训的XltraBackup备份,有备无患,切记切记! 2.mysql -uroot -pD******** -- 导出csv文件 use dsideal_db; MariaDB [dsideal_db]> SELECT * from t_resource_info INTO OUTFILE "/usr/local/mysql/t_resource_info.txt" FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '