2010-02-26 4 views
1

저는 최근에 SQL 서버가 select * from table with (rowlock updlock) where key=value과 같은 명령문이 실행될 때 기본 키에 대해서만 잠금을 발행하는 것을 거부하는 다소 실망한 상황을 경험했습니다. 이제 나 잘못 이해하지 마라. 행을 잠그지 만 한 걸음 더 나아가서 테이블을 잠근다.SQL Server의 잠금 아키텍처를보다 개발자 친화적으로 만들기 위해 어떤 변경을해야합니까?

SQL 잠금 에스컬레이션에 대해 읽었으며 잠금 힌트의 특정 인덱스를 살펴 보았지만 수백만 개의 레코드가있는 테이블에 여러 개의 인덱스가 있고 동시적일 때 실용적이지 않습니다 해당 레코드에서 발생해야하는 업데이트 작은 테이블과 특정 쿼리의 경우 원하는 동작을 얻을 수 있지만 테이블의 폭이 넓고 (많은 열) 데이터를 사용하는 프로세스가 많으면이 메서드가 녹아내어 실제 충돌 지점이 될 수 있습니다. 내가 추가보고 싶은 무엇

은 행의 기본 키에 대한 잠금 언제 인덱스를하는 발행 할 것이다 (기본 키 잠금 방치 것이다) PKLock로 빨아 새로운 lockhint입니다 테이블 스캔 또는 다른 방법을 사용하여 행을 가져 오면이 잠금을 검사하여 전체 테이블을 잠그지 않고이를 수행합니다.

이와 같이 테이블 잠금을 실행할 필요가 없으므로 DB에 대한 코드의 병렬 실행 용량이 크게 증가합니다.

이 아이디어에 무게를두고 그것이 가지고있는 결함, 개선 할 수있는 방법 또는 내 딜레마를 해결하기 위해 추가해야하는 다른 요소를 지적하십시오.

편집

@Remus

나는이 쿼리

begin transaction 
select lockname from locks where lockname='A' 
update Locks Set locked=1 where lockname='A' 

다음이 쿼리를 실행하면 :

begin transaction 
select lockname from locks where lockname='A' 

행 A가 커밋하기 전에 두 예제에 반환됩니다 거래. 이것은 업데이트가 아니라 차단 뒤에 있습니다. 쿼리 1로

  1. 가 : 읽고을 잠 그려면, 업데이트 쿼리 2와
  2. : 읽기와 B, 업데이트 B를 잠금, 커밋

    은 성공적인 솔루션을 사용하여 인덱스를 지정하지 않고 다음을 수행해야합니다 쿼리 2와 쿼리 2

  3. : 2
  4. 쿼리 커밋, A,를 업데이트 읽기 및 잠금 : B를 읽고 검색어 1
  5. 을 발표됩니다에 고정 될 때까지 차단 : 커밋 검색어 1
  6. 를 쿼리 2를
+0

여기 대신 Microsoft에 제안 하시겠습니까? –

+0

나는 그것이 지역 사회에서 나온 것이라면 마이크로 소프트가 더 잘 알고 있다고 생각한다. 게다가 나쁜 생각 일 뿐이므로이 문제를 해결하기 위해 왜 다른 방법을 사용해야 하는지를 알고 싶습니다. – Middletone

+0

정확한 진술 :'select * from table (rowlock updlock) where key = value' 큰 테이블 전체를 잠근다. 또는 귀하의 쿼리가 더 복잡하고 전체 테이블이 잠겨 있는지 확인 하시겠습니까? –

답변

3

님 전에이 질문을하고 답을 받았다 : fix your schema and your code. 이 게시물에서 잠금 충돌은 IX 잠금이었고 의도 잠금에 대한 충돌은 테이블 세분화를 나타내는 높은 세분성 잠금을 나타냅니다.잠금 힌트가 필요하지 않습니다. 색인 및 괜찮은 쿼리가 필요합니다. 대답은 간단하다 예를 들어 가지고 다른 질문, Why does row level locking not appear to work correctly in SQL server? : 잠금 테이블 LockName에 클러스터 된 인덱스를 구성 할 필요가있다 : 다른 세션에

begin transaction 
update Locks 
    set Locked=1 
    output inserted.* 
    where LockName = 'A'; 

:

CREATE TABLE [dbo].[Locks]( 
    [LockName] [varchar](50) NOT NULL, 
    [Locked] [bit] NOT NULL, 
    CONSTRAINT [PK_Locks] PRIMARY KEY CLUSTERED ([LockName])); 
GO  

insert into Locks (LockName, Locked) values ('A', 0); 
insert into Locks (LockName, Locked) values ('B', 0); 
GO 

세션 하나에 이렇게 다음을 수행하십시오 :

begin transaction 
update Locks 
    set Locked=1 
    output inserted.* 
    where LockName = 'B'; 

업데이트 충돌, 차단 없음, (잘못된) 힌트 필요 없음, 아무 것도 없습니다. 올바른 스키마 및 쿼리 설계.

여기에서 설명하는 잠금 장치는 이미 존재하며 키 잠금이라고도합니다. SQL Server는 기본적으로 암시 적 모드로 작동합니다. SQL Server가 초당 16000TPC 트랜잭션의 TPC-C 벤치 마크 번호를 게시 할 수 있다고 상상하십니까? 서버에 필요한 모든 병렬 처리 기능이 있으므로 사용 방법을 이해하려면 책 한 권 또는 두권 만 읽으면됩니다. 주제에 관한 많은 문헌이 있습니다. Transaction Processing: Concepts and Techniques으로 시작할 수 있습니다.

업데이트

begin transaction 
select lockname from locks where lockname='A' 
update Locks Set locked=1 where lockname='A' 

이 상관없이 당신이 시도 얼마나 많은/다양한 잠금 힌트 작동하지 않습니다. 출력 구문이 업데이트 된 이유는 다음과 같습니다.

begin transaction 
update Locks 
Set locked=1 
output inserted.* 
where lockname='A' 

이렇게하면 처음 업데이트 한 다음 업데이트 한 내용을 반환합니다. 이 기술은 당신이 추구하는 의미와 정확히 일치하는 데이터베이스에서 상당히 일반적입니다 : 자원 획득. 사실이 기술은 자원 획득 포스터 하위 요소의 핵심입니다 : 대기열 처리. 단락 OUTPUT Clause을 참조하십시오. 큐에서 당신이 자원의 테이블을 처리 할 수 ​​있고, 각 스레드 처리, 하나를 잡고 그것을 잠그고 시작 : 별도의 세션에서 지금

create table Resources (
    id int identity(1,1) not null, 
    enqueue_time datetime not null default getutcdate(), 
    is_processing bit not null default 0, 
    payload xml); 

create clustered index cdxResources on Resources 
    (is_processing, enqueue_time); 
go 

-- enqueue: 
insert into Resources (payload) values ('<do>This</do>'); 
insert into Resources (payload) values ('<do>That</do>'); 
insert into Resources (payload) values ('<do>Something</do>'); 
insert into Resources (payload) values ('<do>Anything</do>'); 

이 실행 :

--dequeue 
begin transaction; 
with cte as (
    select top(1) * 
    from Resources with(readpast) 
    where is_processing = 0 
    order by enqueue_time) 
update cte 
    set is_processing = 1 
    output inserted.*; 

당신은 볼 수 있습니다 각 세션은 자신의 리소스를 잡아서 잠그고 모든 사람들이 잠근 모든 것을 건너 뜁니다. 이처럼 정확하게 실행되는 시스템에서는 생산중인 시스템에서이 테이블 (웹 서비스 지불 처리 요청) 인 5 백만 개가 넘는 리소스와 100 개의 동시 처리 프로세서 (약 2 초가 소요됨)에서 초당 약 50 개를 대기열에서 제외하고 처리합니다. 처리 할 호출 당). 정크 하드웨어에. 그래서 절대적으로 가능합니다.

+0

내 locks 테이블이 테이블 스캔을 수행하는 동안 원래 lockname 필드에 인덱스를 추가하여이를 해결하고 쿼리의 인덱스를 포함했습니다. 70 만 개의 레코드가있는 테이블 스캔을 수행하지 않는 다른 테이블이 있습니다. 이러한 경우에는 테이블 스캔이없고 잠금이 중요합니다. 위의 예는 select 문 (원래 예제에서)에 추가하지 못합니다. 트랜잭션을 실행할 때 쿼리 2가 커밋되기 전에 쿼리 1의 데이터에서 반복 가능한 읽기를 수행 할 수 있음을 보여줍니다. – Middletone

+0

두 개의 업데이트 질의 전에 lockname = 'A ''에서'select * locks'를 추가하면 불가능할 때 데이터를 읽을 수 있습니다. 내 게시물의 전체 내용은 커밋 될 때까지 데이터를 읽을 수 없기 때문에 읽지 않은 행을 읽지 못하게해야합니다. – Middletone

+0

'출력이 삽입되었습니다. *'**는 선택입니다 **. 게다가,'lockname = 'A' '가해야 할 일을 정확히 수행하라. 업데이트 뒤에 숨어있다. 나는 이전에 조언을하지 못하는 사람들을 보았지만, 당신은 막대를 정말로 높게 설정하고 있습니다 ... –