효율적인 트랜잭션, 레코드 잠금


5

나는 하나의 레코드를 다시 선택하는 저장 프로 시저가 있습니다. 저장 프로 시저가 다른 PC의 여러 응용 프로그램에서 호출 될 수 있습니다. 저장 프로 시저가 처리해야하는 다음 레코드를 다시 가져오고, 두 응용 프로그램이 동시에 저장된 프로 시저를 호출하는 경우 같은 레코드를 다시 가져 오지 않아야한다는 아이디어가 있습니다. 내 쿼리는 아래, 나는 가능한 한 효율적으로 쿼리를 작성하려고 노력하고있어 (SQL 2008). 이보다 더 효율적으로 처리 할 수 ​​있습니까?

CREATE PROCEDURE GetNextUnprocessedRecord 
AS 
BEGIN 
    SET NOCOUNT ON; 

    --ID of record we want to select back 
    DECLARE @iID BIGINT  

    -- Find the next processable record, and mark it as dispatched 
    -- Must be done in a transaction to ensure no other query can get 
    -- this record between the read and update 
    BEGIN TRAN 

     SELECT TOP 1 
      @iID = [ID] 
     FROM 
      --Don't read locked records, only lock the specific record 
      [MyRecords] WITH (READPAST, ROWLOCK) 
     WHERE 
      [Dispatched] is null 
     ORDER BY 
      [Received] 

     --Mark record as picked up for processing 
     UPDATE 
      [MyRecords] 
     SET 
      [Dispatched] = GETDATE() 
     WHERE 
      [ID] = @iID  

    COMMIT TRAN 

    --Select back the specific record 
    SELECT 
     [ID], 
     [Data] 
    FROM  
     [MyRecords] WITH (NOLOCK, READPAST) 
    WHERE 
     [ID] = @iID 

END 
  0

에 모든 것을 수행이 구문을 사용할 수 있습니다 트랜잭션 너무 인덱스를 필터링 safe ... 22 feb. 092009-02-22 08:07:45

  0

SELECT 후 UPDATE 후 WAITFOR DELAY를 0시 2 분 0 초에 넣고 SP를 실행하고 다른 연결에서 동일한 SP를 실행하십시오 ... 22 feb. 092009-02-22 08:17:24

  0

실제로 저는 틀 렸습니다! HOLDLOCK은 MyRecords 테이블의 REPEATABLEREAD와 같은 효과를냅니다. 22 feb. 092009-02-22 08:22:27

  0

기본적으로 더 안전합니다. 그렇지 않으면 느슨한 개념적 혼란이 널리 퍼져 있습니다. 22 feb. 092009-02-22 08:43:46

  0

나는 아직도 당신이 확실히 안전하지 않은 REPEATABLEREAD 22 feb. 092009-02-22 08:55:17

3

READPAST 잠금 힌트를 사용하면 SQL이 정상적으로 보입니다.

나는이 당신이 ID를 얻을 것을 의미

... 
[MyRecords] WITH (READPAST, ROWLOCK, XLOCK) 
... 

되는하지만 또한 SERIALIZABLE HOLDLOCK/인 사용 XLOCK를 추가하고, 당신이 수행하고이를 업데이트하는 동안 독점적으로 해당 행을 잠글 것입니다.

편집 : Dispatched 및 Received 열에 색인을 추가하면 더 빨리 처리 할 수 ​​있습니다. [ID] (PK 인 것으로 가정)이 클러스터되지 않은 경우 [ID]가 포함됩니다. 이 SQL 2008

때문에 그리고 또한 나는이 TSQL을 확신 아니에요 하나 개 XLOCK없이 이동 또는 HOLDLOCK

UPDATE 
    MyRecords 
SET 
    --record the row ID 
    @id = [ID], 
    --flag doing stuff 
    [Dispatched] = GETDATE() 
WHERE 
    [ID] = (SELECT TOP 1 [ID] FROM MyRecords WITH (ROWLOCK, READPAST) WHERE Dispatched IS NULL ORDER BY Received) 

UPDATE, assign, set in one

  0

원더풀 팁! XLOCK 힌트를 추가하고 Received와 Dispatched에 클러스터되지 않은 인덱스를 만들고 "Dispatched IS NULL"필터가있는 ID를 포함 시켰습니다. 이것은 ID의 클러스터 된 인덱스에 추가됩니다. 나는 여전히 업데이트 할당 집합이 더 빠르지 않은지 알아 내려고하고있다. 23 feb. 092009-02-23 07:07:14

+1

감사합니다. 명백한 성능보다는 트랜잭션 무결성에 대해 더 걱정할 것입니다. 이 단일 성명서는 XLOCK의 필요성을 제거합니다. 23 feb. 092009-02-23 07:48:33


0

각 피커 프로세스에 고유 한 ID를 할당하고 pickerproc 및 pickstate 열을 레코드에 추가 할 수 있습니다. 그런 다음

업데이트 MyRecords
SET의 pickerproc = MYPROC,
pickstate = 'I'-에 'I'n 과정
WHERE 식 (SELECT MyRecords FROM MAX (ID) WHERE pickstate ='A ') - 'A'vailable

이렇게하면 한 걸음 더 나아가 기록을 남길 수 있으며 여가 시간에 나머지 처리 작업을 수행 할 수 있습니다. 그런 다음 pickstate를 'C'omplete', 'E'rror'등으로 설정할 수 있습니다.

나는 Mitch가 메시지 큐 테이블을 만들고 거기에 ID를 삽입하는 또 다른 좋은 기술을 언급하고 있다고 생각한다. 몇 가지 SO 스레드가 있습니다. '메시지 큐 테이블'을 검색하십시오.

  0

보다는 SERIALIZABLE 트랜잭션을 만들어야한다고 생각합니다! 22 feb. 092009-02-22 08:51:33

  0

그리고 그 이유는 무엇입니까? 그것은 당신이 그것을 바꿀 때까지 pickstate 'A'가있는 같은 MAX (Id)를 넘기지 않을 것입니다. 적어도 10^6 + 인스턴스를 기반으로 한 경험이 없습니다. 22 feb. 092009-02-22 08:55:38

  0

.. 그리고 그것은 어느 쪽도 효율적이지 않을 것이다 22 feb. 092009-02-22 08:57:49

  0

나는 듣고있다. .. 당신의 논증은 무엇 이냐? 나는 항상 SQL에 대해 더 많이 배울 수있다 ... 22 feb. 092009-02-22 09:00:29

  0

@le dorfier : 트랜잭션 일관성을 유지하려면 SERIALIZABLE 트랜잭션으로 래핑해야합니다. 그렇지 않으면 내가 잘못하지 않는 한 더티 읽기/반복 불가능 읽기/팬텀 읽기/ 22 feb. 092009-02-22 09:01:27

  0

의 위험이 있습니다. MAX 작업은 행을 스캔 할 것입니다. 이것이 큰 테이블이라면, 비교적 오랜 시간 동안 열리는 tranasction을 유지할 것입니다. 22 feb. 092009-02-22 09:03:12

  0

나는 그 과장된 잠금 문서를 다시 알아 내야 만하는 것을 싫어하지만 ACID가 기본적으로 적용된다는 가정하에 시작합니다. 당신이 동의하지 않습니까? 22 feb. 092009-02-22 09:03:40

  0

(리플렉션시) 나는 귀하의 요점 (즉, 픽 스테이트 열의 하위 카디널리티)을 보았습니다. 하지만 SQL 2008에서 사용할 수있는 "필터링 된 색인"으로 해결됩니다. http://blog.sqlauthority.com/2008/09/01/sql-server-2008-introduction-to-filtered-index-improve-performance-with를 참조하십시오. -filtered-index/ 22 feb. 092009-02-22 09:18:22


0

빠른 처리를 위해 "메모리"테이블에 MyRecords를 보관할 수 있습니다.

  0

테이블이 상당히 커질 수 있으며, 서버가 충돌하면 어떻게됩니까? 22 feb. 092009-02-22 19:27:06