이 답변은 원래 질문에 도움이 될 수 있지만 주로 다른 게시물의 부정확 한 정보를 해결하기위한 것입니다. 또한 BOL에서 넌센스 섹션을 강조 표시합니다.
그리고 INSERT 문서에 명시된 바와 같이 테이블에 대한 독점 잠금을 획득합니다. 테이블에 대해 SELECT를 수행 할 수있는 유일한 방법은 NOLOCK을 사용하거나 트랜잭션의 격리 수준을 설정하는 것입니다.
BOL의 링크 된 섹션은 다음과 같습니다.
INSERT 문은 항상 수정하는 테이블에서 배타적 (X) 잠금을 획득하고 트랜잭션이 완료 될 때까지 해당 잠금을 보유합니다. 배타적 (X) 잠금을 사용하면 다른 트랜잭션은 데이터를 수정할 수 없습니다. 읽기 작업은 NOLOCK 힌트 또는 읽기 커밋되지 않은 격리 수준을 사용해야 만 수행 할 수 있습니다. 자세한 정보 는 데이터베이스 엔진 잠금을 참조하십시오 .
주의 : 2014-8-27 현재, 위에서 인용 한 잘못된 진술을 제거하기 위해 BOL이 업데이트되었습니다.
고맙게도 이것은 사실이 아닙니다. 이 경우 테이블에 대한 삽입이 연속적으로 발생하고 삽입 트랜잭션이 완료 될 때까지 모든 판독기가 전체 테이블에서 차단됩니다. 그러면 SQL Server가 NTFS만큼 효율적인 데이터베이스 서버가됩니다. 하지 매우.
상식은 그렇게 할 수 없다고 제안하지만 Paul Randall이 지적한 것처럼 " 자신에게 호의를 베풀지 말아라 " BOL을 포함한 다른 사람을 신뢰할 수 없다면 우리는 그것을 증명해야한다고 생각합니다.
데이터베이스를 작성하고 리턴 된 DatabaseId를 기록하여 더미 테이블을 여러 행으로 채 웁니다.
SET STATISTICS IO OFF;
SET STATISTICS TIME OFF;
USE [master]
GO
IF EXISTS (SELECT name FROM sys.databases WHERE name = N'LockDemo')
DROP DATABASE [LockDemo]
GO
DECLARE @DataFilePath NVARCHAR(4000)
SELECT
@DataFilePath = SUBSTRING(physical_name, 1, CHARINDEX(N'master.mdf', LOWER(physical_name)) - 1)
FROM
master.sys.master_files
WHERE
database_id = 1 AND file_id = 1
EXEC ('
CREATE DATABASE [LockDemo] ON PRIMARY
( NAME = N''LockDemo'', FILENAME = N''' + @DataFilePath + N'LockDemo.mdf' + ''', SIZE = 2MB , MAXSIZE = UNLIMITED, FILEGROWTH = 2MB )
LOG ON
( NAME = N''LockDemo_log'', FILENAME = N''' + @DataFilePath + N'LockDemo_log.ldf' + ''', SIZE = 1MB , MAXSIZE = UNLIMITED , FILEGROWTH = 1MB )
')
GO
USE [LockDemo]
GO
SELECT DB_ID() AS DatabaseId
CREATE TABLE [dbo].[MyTable]
(
[id] [int] IDENTITY(1,1) PRIMARY KEY CLUSTERED
, [filler] CHAR(4030) NOT NULL DEFAULT REPLICATE('A', 4030)
)
GO
INSERT MyTable DEFAULT VALUES;
GO 100
lock : acquired 및 lock : released 이벤트를 추적하고, 이전 스크립트에서 DatabaseId를 필터링하고, 파일의 경로를 설정하고 리턴 된 TraceId를 기록하는 프로파일 러 추적을 설정하십시오.
declare @rc int
declare @TraceID int
declare @maxfilesize BIGINT
declare @databaseid INT
DECLARE @tracefile NVARCHAR(4000)
set @maxfilesize = 5
SET @tracefile = N'D:\Temp\LockTrace'
SET @databaseid = 9
exec @rc = sp_trace_create @TraceID output, 0, @tracefile, @maxfilesize, NULL
if (@rc != 0) goto error
declare @on bit
set @on = 1
exec sp_trace_setevent @TraceID, 24, 32, @on
exec sp_trace_setevent @TraceID, 24, 1, @on
exec sp_trace_setevent @TraceID, 24, 57, @on
exec sp_trace_setevent @TraceID, 24, 3, @on
exec sp_trace_setevent @TraceID, 24, 51, @on
exec sp_trace_setevent @TraceID, 24, 12, @on
exec sp_trace_setevent @TraceID, 60, 32, @on
exec sp_trace_setevent @TraceID, 60, 57, @on
exec sp_trace_setevent @TraceID, 60, 3, @on
exec sp_trace_setevent @TraceID, 60, 51, @on
exec sp_trace_setevent @TraceID, 60, 12, @on
exec sp_trace_setevent @TraceID, 23, 32, @on
exec sp_trace_setevent @TraceID, 23, 1, @on
exec sp_trace_setevent @TraceID, 23, 57, @on
exec sp_trace_setevent @TraceID, 23, 3, @on
exec sp_trace_setevent @TraceID, 23, 51, @on
exec sp_trace_setevent @TraceID, 23, 12, @on
-- DatabaseId filter
exec sp_trace_setfilter @TraceID, 3, 0, 0, @databaseid
-- Set the trace status to start
exec sp_trace_setstatus @TraceID, 1
-- display trace id for future references
select TraceID=@TraceID
goto finish
error:
select ErrorCode=@rc
finish:
go
행을 삽입하고 추적을 중지하십시오.
USE LockDemo
GO
INSERT MyTable DEFAULT VALUES
GO
EXEC sp_trace_setstatus 3, 0
EXEC sp_trace_setstatus 3, 2
GO
추적 파일을 열고 다음을 찾으십시오.
잠금 순서는 다음과 같습니다.
- MyTable에 대한 의도 전용 잠금
- 페이지 1 : 211의 의도 전용 잠금
- 삽입되는 값에 대한 클러스터형 인덱스 항목의 RangeInsert-NullResource
- 키 전용 잠금
그런 다음 잠금은 역순으로 해제됩니다. 테이블에서 독점 잠금을 획득 한 시점은 없습니다.
그러나 이것은 단지 하나의 배치 삽입입니다! 2, 3 또는 수십 개의 병렬 실행과 동일하지 않습니다.
네 그렇습니다. SQL Server (및 관계형 데이터베이스 엔진)는 명령문 및 / 또는 배치를 처리 할 때 다른 배치가 실행될 수 있는지에 대해 예측할 수 없으므로 잠금 획득 순서는 변하지 않습니다.
Serializable과 같은 더 높은 격리 수준은 어떻습니까?
이 특정 예에서는 정확히 동일한 잠금이 사용됩니다. 날 믿지마, 시도 해봐!