最近有一个困惑,生产服务器上有一表索引建得乱七八糟,经过整理后需要新建几个索引,再删除几个索引,建立索引时使用联机(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锁的更多相关文章

  1. mysql 创建索引和删除索引

    索引的创建可以在CREATE TABLE语句中进行,也可以单独用CREATE INDEX或ALTER TABLE来给表增加索引.删除索引可以利用ALTER TABLE或DROP INDEX语句来实现. ...

  2. SQL优化 MySQL版 - 索引分类、创建方式、删除索引、查看索引、SQL性能问题

    SQL优化 MySQL版  - 索引分类.创建方式.删除索引.查看索引.SQL性能问题 作者 Stanley 罗昊 [转载请注明出处和署名,谢谢!] 索引分类 单值索引 单的意思就是单列的值,比如说有 ...

  3. mysql索引 ->创建索引、修改索引、删除索引的命令语句

    查看表中已经存在 index:show index from table_name; 创建和删除索引索引的创建可以在CREATE TABLE语句中进行,也可以单独用CREATE INDEX或ALTER ...

  4. mysql——创建索引、修改索引、删除索引的命令语句

    查看表中已经存在 index:show index from table_name; 创建和删除索引索引的创建可以在CREATE TABLE语句中进行,也可以单独用CREATE INDEX或ALTER ...

  5. MongoDB性能篇之创建索引,组合索引,唯一索引,删除索引和explain执行计划

    这篇文章主要介绍了MongoDB性能篇之创建索引,组合索引,唯一索引,删除索引和explain执行计划的相关资料,需要的朋友可以参考下 一.索引 MongoDB 提供了多样性的索引支持,索引信息被保存 ...

  6. mysql索引学习----2----创建索引、修改索引、删除索引的命令语句

    查看表中已经存在 index:show index from table_name; 创建和删除索引索引的创建可以在CREATE TABLE语句中进行,也可以单独用CREATE INDEX或ALTER ...

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

    --------------------------------------------------------- -- ALTER TABLE 创建索引 ---------------------- ...

  8. SQL创建索引和删除索引

    使用CREATE 语句创建索引 CREATE INDEX index_name ON table_name(column_name,column_name) include(score) 普通索引 C ...

  9. Oracle如何创建索引、删除索引、查询索引

    1.创建单一索引 create index 索引名称 on 表名(列名); 2.创建复合索引 create index 索引名称 on 表名(列名1,列名2); 3.删除索引 drop index 索 ...

随机推荐

  1. 使用python识别验证码

    公司的登录注册等操作有验证码,测试环境可以让开发屏蔽掉验证码,但是如果到线上的话就要想办法识别验证码或必过验证码了. 识别验证码主要分为三部分,一.对验证码进行二值化.二.将二值化后的图片分割.三.进 ...

  2. 等待时间,time.sleep()和implicitly_wait()

    在运行一个以前执行的过的功能时,报错了,不能执行了. 功能描述:通过导航,选择下拉项(发布职位功能),下面是审查的元素: 获取元素的代码: 尝试了用xpath去获取:driver.find_eleme ...

  3. Only POT texture can be compressed to PVRTC format

    在图片压缩格式 报这个Warning的时候,意思是该图片必须要采用2的幂次方大小才能使用该格式. POT: Power of Two. 采取方式是在advanced里边,把Non Power of 2 ...

  4. 【Python基础教程第2版】——第二讲:列表和元组

    引言: 什么是数据结构? 数据结果是通过某种方式(例如对元素进行编号)组织在一起的数据元素的集合.Python中最常用的数据结构是序列. Python包含6种内建的序列:列表和元组(最常用:列表可以修 ...

  5. CentOS下zabbix监控mysql5.6版本主从

    目录 CentOS下zabbix监控mysql5.6版本主从 1. Zabbix添加自定义监控流程 2. 具体步骤 1. 编写监控mysql主从脚本 2. mysql赋权 3. 查看脚本执行效果 4. ...

  6. 6-Linux 上mysql的常用命令 以及 tomcat的相关指定

    mysql -u root -p 进入Mysql //注意一下有逗号!!! show databases; //显示所有的数据库 drop database mydb; // 删除mydb这个数据库 ...

  7. struts2框架之OGNL表达式概述(在代码中使用OGNL表达式)

    1. OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写 * 所谓对象图,即以任意一个对象为根,通过OGNL可以访问与这个对象关联的其它对象 * 通 ...

  8. 对于Android开发,啥是高级工程师?

    最近一直在思考自己的技术方向.新的技术永远都是层出不穷,kotlin,flutter,小程序,轻应用等等,但是作为一个老鸟,新的东西,永远都是学不完的,想在新的技术上迭代学习出一个新高度,而增加自己的 ...

  9. _GET_

    _GET_:读取属性,没有自动创建

  10. [BAT] 执行xcopy命令后出现Invalid num of parameters错误的解决办法

    如果是Windows下的命令行,对于有空格的文件路径要加引号,对于xcopy命令就是源路径和目标路径都要加引号 xcopy "C:\ppt" "D:\Program do ...