SQL Server 2005에서 교착 상태 진단


82

Stack Overflow SQL Server 2005 데이터베이스에서 악성이지만 드문 교착 상태가 발생했습니다.

프로파일 러를 연결하고 교착 상태 문제 해결에 대한이 훌륭한 기사를 사용하여 추적 프로파일을 설정 하고 여러 예제를 캡처했습니다. 이상한 점은 교착 상태 쓰기가 항상 동일 하다는 것입니다 .

UPDATE [dbo].[Posts]
SET [AnswerCount] = @p1, [LastActivityDate] = @p2, [LastActivityUserId] = @p3
WHERE [Id] = @p0

다른 교착 상태 문은 다양하지만 일반적으로 posts 테이블 을 간단하고 간단하게 읽습니다 . 이것은 항상 교착 상태로 죽습니다. 여기에 예가 있습니다.

SELECT
[t0].[Id], [t0].[PostTypeId], [t0].[Score], [t0].[Views], [t0].[AnswerCount], 
[t0].[AcceptedAnswerId], [t0].[IsLocked], [t0].[IsLockedEdit], [t0].[ParentId], 
[t0].[CurrentRevisionId], [t0].[FirstRevisionId], [t0].[LockedReason],
[t0].[LastActivityDate], [t0].[LastActivityUserId]
FROM [dbo].[Posts] AS [t0]
WHERE [t0].[ParentId] = @p0

명확하게 말하면, 쓰기 / 쓰기 교착 상태가 아니라 읽기 / 쓰기입니다.

현재 LINQ와 매개 변수화 된 SQL 쿼리가 혼합되어 있습니다. with (nolock)모든 SQL 쿼리에 추가 했습니다. 이것은 일부 도움이되었을 수 있습니다. 또한 어제 수정 한 (매우) 잘못 작성된 배지 쿼리가 하나있었습니다. 매번 실행하는 데 20 초 이상이 걸리고 그 위에 매분 실행되었습니다. 이것이 잠금 문제의 원인이되기를 바랬습니다!

불행히도 약 2 시간 전에 또 다른 교착 상태 오류가 발생했습니다. 똑같은 증상, 똑같은 범인이 쓴다.

정말 이상한 점은 위에서 본 잠금 쓰기 SQL 문이 매우 특정한 코드 경로의 일부라는 것입니다. 그것은 것 에만 새 응답이 질문에 추가 될 때 실행 - 그것은 새 응답 수와 마지막 날짜 / 사용자와 부모의 질문을 업데이트합니다. 이것은 분명히 우리가 수행하는 엄청난 수의 읽기에 비해 그렇게 일반적이지 않습니다! 내가 말할 수있는 한, 우리는 앱 어디에서나 엄청난 수의 쓰기를하고 있지 않습니다.

NOLOCK이 일종의 거대한 망치라는 것을 알고 있지만 여기서 실행하는 대부분의 쿼리는 정확할 필요가 없습니다. 사용자 프로필이 몇 초가 지난 경우에도 신경 쓰시겠습니까?

Linq와 함께 NOLOCK을 사용하는 것은 Scott Hanselman이 여기에서 설명 하는 것처럼 조금 더 어렵습니다 .

우리는 사용의 아이디어를 유혹하고 있습니다

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

모든 LINQ 쿼리가이 세트를 갖도록 기본 데이터베이스 컨텍스트에서 그것 없이는 우리가 만드는 모든 LINQ 호출 (대부분의 간단한 읽기 호출)을 3-4 줄 트랜잭션 코드 블록으로 래핑해야합니다.

SQL 2005의 사소한 읽기가 쓰기에 교착 상태가 될 수 있다는 사실에 약간 실망한 것 같습니다. 쓰기 / 쓰기 교착 상태가 큰 문제라는 것을 알 수 있지만 읽습니까? 우리는 여기서 은행 사이트를 운영하지 않으며 매번 완벽한 정확성이 필요하지 않습니다.

아이디어? 생각?


모든 작업에 대해 새 LINQ to SQL DataContext 개체를 인스턴스화하고 있습니까? 아니면 모든 호출에 대해 동일한 정적 컨텍스트를 공유하고 있습니까?

Jeremy, 우리는 대부분의 경우 기본 컨트롤러에서 하나의 정적 데이터 컨텍스트를 공유합니다.

private DBContext _db;
/// <summary>
/// Gets the DataContext to be used by a Request's controllers.
/// </summary>
public DBContext DB
{
    get
    {
        if (_db == null)
        {
            _db = new DBContext() { SessionName = GetType().Name };
            //_db.ExecuteCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED");
        }
        return _db;
    }
}

모든 컨트롤러에 대해 또는 페이지별로 또는 .. 더 자주 새 컨텍스트를 만드는 것이 좋습니까?


2
사용중인 <a href=" en.wikipedia.org/wiki/… 모드</a>, "비관적"(잠금 기반) 또는 "낙관적"(<a href = " en.wikipedia.org/wiki/ … )?
John Siracusa

의 Guy의 응답에 동의 합니다. 증상을 해결하려고하기보다는 근본적인 원인을 해결하지 않는 이유는 무엇입니까? Posts 테이블에 AnswerCount의 누계를 추가하면 잠재적 차단 리소스가 생성되었습니다. Jeff가 StackOverflow에 대한 ERD를 게시하여 사람들이 비평 할 수 있도록 하시겠습니까?
andyp

2
와우-Scott과 함께 팟 캐스트에서이 소식을 들었습니다. 더 나은 구성으로 즉시 배송되지 않는다는 것도 믿을 수 없습니다. 나는 이것을 DBA에게 보여줄 것입니다. (그들이 'nolock'을 광범위하게 사용하기 때문에)
Dan Esparza

3
이것이 교착 상태 인 이유는 samsaffron.com/archive/2008/08/27/Deadlocked+ 를 참조하십시오 . 스냅 샷 격리를 켜는 것이이 문제에 대한 좋은 해결책입니다.
Sam Saffron

답변:


44

MSDN에 따르면 :

http://msdn.microsoft.com/en-us/library/ms191242.aspx

READ COMMITTED SNAPSHOT 또는 ALLOW SNAPSHOT ISOLATION 데이터베이스 옵션이 ON이면 데이터베이스에서 수행 된 모든 데이터 수정에 대해 논리적 복사본 (버전)이 유지됩니다. 특정 트랜잭션에 의해 행이 수정 될 때마다 데이터베이스 엔진 인스턴스는 이전에 커밋 된 행 이미지 버전을 tempdb에 저장합니다. 각 버전은 변경 한 트랜잭션의 트랜잭션 시퀀스 번호로 표시됩니다. 수정 된 행의 버전은 링크 목록을 사용하여 연결됩니다. 최신 행 값은 항상 현재 데이터베이스에 저장되고 tempdb에 저장된 버전이 지정된 행에 연결됩니다.

단기 실행 트랜잭션의 경우 수정 된 행의 버전이 tempdb 데이터베이스의 디스크 파일에 기록되지 않고 버퍼 풀에 캐시 될 수 있습니다. 버전이 지정된 행에 대한 필요성이 일시적인 경우 단순히 버퍼 풀에서 삭제되며 반드시 I / O 오버 헤드가 발생하는 것은 아닙니다.

추가 오버 헤드에 대해 약간의 성능 저하가있는 것처럼 보이지만 무시할 수 있습니다. 확인하기 위해 테스트해야합니다.

이 옵션을 설정하고 실제로 필요한 경우가 아니면 코드 쿼리에서 모든 NOLOCK을 제거하십시오. 데이터베이스 트랜잭션 격리 수준에 맞서기 위해 데이터베이스 컨텍스트 처리기에서 NOLOCK 또는 전역 메서드를 사용하는 것은 문제에 대한 반창고입니다. NOLOCKS는 데이터 레이어의 근본적인 문제를 가려서 자동 선택 / 업데이트 행 버전 관리가 해결책 인 것처럼 보이는 신뢰할 수없는 데이터를 선택할 수 있습니다.

ALTER Database [StackOverflow.Beta] SET READ_COMMITTED_SNAPSHOT ON

3
"NOLOCKS는 데이터 레이어의 근본적인 문제를 가릴 것입니다"... NOLOCK은 어떤 종류의 문제를 가릴까요? NOLOCK이 필요하다고 생각되면 어떤 문제를 찾아야합니까?
Matt Hamilton

3
이 답변이 "답변"이되는 것은 무엇입니까? 밀리 초 읽기가 왜 즉시 쓰기를 교착 상태로 만드는지 이해하지 못합니다. 나는 "user13484"에 의한 대답이 너무 나쁘다고 추측하고 있는데, 만약 그렇다면 그것에 대한 언급이 없다.
RichardTheKiwi

37

NOLOCKREAD UNCOMMITTED 는 미끄러운 경사입니다. 교착 상태가 먼저 발생하는 이유를 이해하지 않는 한 절대 사용해서는 안됩니다. "모든 SQL 쿼리에 (nolock)을 추가했습니다"라고 말하는 것이 걱정됩니다. 모든 곳에 WITH NOLOCK 을 추가 해야한다는 것은 데이터 영역에 문제가 있다는 확실한 신호입니다.

업데이트 문 자체는 약간 문제가있는 것처럼 보입니다. 트랜잭션의 초기에 카운트를 결정합니까, 아니면 객체에서 가져 옵니까? AnswerCount = AnswerCount+1질문이 추가되면이 문제를 처리하는 더 좋은 방법 일 것입니다. 그러면 정확한 카운트를 얻기 위해 트랜잭션이 필요하지 않으며 잠재적으로 노출되는 동시성 문제에 대해 걱정할 필요가 없습니다.

많은 작업없이 더티 읽기를 사용하지 않고 이러한 유형의 교착 상태 문제를 해결하는 쉬운 방법 중 하나는 "Snapshot Isolation Mode"수정되지 않은 마지막 데이터를 항상 깨끗하게 읽을 수있는 SQL 2005의 새로운 기능 을 사용 하는 것입니다. 교착 상태 문을 정상적으로 처리하려면 쉽게 잡아서 재 시도 할 수 있습니다.


4
저는 JEzell과 함께 있습니다. 가장 먼저 제로화 한 것은 'SET AnswerCount = <고정 값>'입니다. 그 가치는 어디에서 오는가? 그것은 트랜잭션의 다른 곳에서 잠금을 잡는 방식으로 검색했는지 궁금합니다. 그것부터 시작하겠습니다. 그리고 네, 글로벌 NOLOCK은 반창고입니다.
Cowan

25

OP 질문은이 문제가 발생한 이유를 묻는 것이 었습니다. 이 게시물은 다른 사람들이 해결할 수있는 가능한 해결책을 남기면서 그에 대한 대답을 희망합니다.

이것은 아마도 인덱스 관련 문제 일 것입니다. 예를 들어 Posts 테이블에 ParentID와 업데이트중인 필드 (AnswerCount, LastActivityDate, LastActivityUserId) 중 하나 이상을 포함하는 클러스터되지 않은 인덱스 X가 있다고 가정 해 보겠습니다.

SELECT cmd가 ParentId로 검색하기 위해 인덱스 X에서 공유 읽기 잠금을 수행 한 다음 UPDATE cmd가 쓰기 전용을 수행하는 동안 나머지 열을 가져 오기 위해 클러스터형 인덱스에 대해 공유 읽기 잠금을 수행해야하는 경우 교착 상태가 발생합니다. 클러스터형 인덱스를 잠그고이를 업데이트하려면 인덱스 X에 대한 쓰기 전용 잠금을 가져와야합니다.

이제 A가 X를 잠그고 Y를 얻으려고하는 반면 B는 Y를 잠그고 X를 얻으려고하는 상황이 있습니다.

물론 이것이 실제로 원인인지 확인하기 위해 어떤 인덱스가 사용 중인지에 대한 추가 정보로 그의 게시물을 업데이트하는 OP가 필요합니다.


이 분석에 동의합니다. SELECT 및 UPDATE는 행을 다른 순서로 처리하므로 각각 다른 행 잠금을 얻으려고합니다.
Mike Dimmick

이것은 전체 스레드에 대한 최상의 응답이며 교착 상태가 실제로 발생하는 이유에 대한 유일한 설명입니다. 여기에서 가장 좋은 답변이기 때문에 # 1 답변이 아닙니다.
Jonathan Kehayias

일부 답변에는 두 개의 원자 간단한 문 사이에 잠금에 대한 요점이 누락되었습니다. 이것은 그것을 설명하려는 유일한 게시물입니다. 명령문은 간단하지만 테이블 업데이트에는 여러 작업으로 확장되는 여러 CIX 및 NCIX 업데이트가 포함될 수 있습니다. NCIX 순회, CIX 북마크 검색과 관련된 READ에 대해서도 동일합니다. 이 (사람들이 ?? 질문을 읽습니까) 같은 순서 등으로 테이블을 조인과는 아무 상관이 없다
RichardTheKiwi

18

나는이 질문에 대해 매우 불편하고 참석자가 대답합니다. "이 마법 가루를 사용해보세요! 그 마법 가루가 아닙니다!"가 많이 있습니다.

나는 당신이 취한 자물쇠를 분석하고 어떤 정확한 유형의 자물쇠가 교착 상태인지 결정한 곳을 볼 수 없습니다.

당신이 지적한 것은 교착 상태가 아닌 일부 잠금이 발생한다는 것입니다.

SQL 2005에서는 다음을 사용하여 어떤 잠금이 해제되는지에 대한 자세한 정보를 얻을 수 있습니다.

DBCC TRACEON (1222, -1)

교착 상태가 발생하면 더 나은 진단을받을 수 있습니다.


13
교착 상태는 SQL Server의 교착 상태 모니터에 의해 즉시 처리됩니다. DMV는 교착 상태를 해결하는 데 쓸모가 없습니다. 피해자가 발생하는 것을 발견하기 전에 선택되어 죽이기 때문입니다.
Jonathan Kehayias

14

모든 작업에 대해 새 LINQ to SQL DataContext 개체를 인스턴스화하고 있습니까? 아니면 모든 호출에 대해 동일한 정적 컨텍스트를 공유하고 있습니까? 나는 원래 후자의 접근 방식을 시도했고 내가 기억하는 바에 따르면 DB에서 원치 않는 잠금이 발생했습니다. 이제 모든 원자 작업에 대한 새로운 컨텍스트를 만듭니다.


10

NOLOCK으로 파리를 잡기 위해 집을 불 태우기 전에 Profiler로 캡처 한 데드락 그래프를 살펴볼 수 있습니다.

교착 상태에는 (적어도) 2 개의 잠금이 필요합니다. 연결 1에는 잠금 A가 있고 잠금 B를 원하며 연결 ​​2에는 그 반대의 경우도 마찬가지입니다. 이것은 해결할 수없는 상황이며 누군가 제공해야합니다.

지금까지 보여준 것은 Sql Server가 하루 종일 기꺼이 수행하는 간단한 잠금으로 해결됩니다.

나는 당신 (또는 LINQ)이 그 UPDATE 문으로 트랜잭션을 시작하고 다른 정보를 미리 선택하고 있다고 생각합니다. 그러나 각 스레드가 보유한 잠금을 찾기 위해 교착 상태 그래프를 역 추적 한 다음 프로파일 러를 통해 역 추적하여 해당 잠금이 부여 된 명령문을 찾아야합니다.

이 퍼즐을 완성하는 데 최소한 4 개의 문이있을 것으로 예상합니다 (또는 여러 잠금을 사용하는 문-아마도 Posts 테이블에 트리거가 있습니까?).


7

사용자 프로필이 몇 초가 지난 경우에도 신경 쓰시겠습니까?

아니요-그것은 완벽하게 받아 들여집니다. 기본 트랜잭션 격리 수준을 설정하는 것이 아마도 가장 좋고 깨끗한 방법 일 것입니다.


5

일반적인 읽기 / 쓰기 교착 상태는 인덱스 순서 액세스에서 발생합니다. 읽기 (T1)는 인덱스 A에서 행을 찾은 다음 인덱스 B (일반적으로 클러스터링 됨)에서 프로젝션 된 열을 찾습니다. 쓰기 (T2)는 인덱스 B (클러스터)를 변경 한 다음 인덱스 A를 업데이트해야합니다. T1은 A에서 S-Lck를, B에서 S-Lck를 원하고, T2는 B에서 X-Lck를, A에서 U-Lck를 원합니다. 교착 상태 , 퍼프. T1이 죽었습니다. 이것은 OLTP 트래픽이 많고 인덱스가 너무 많은 환경에서 일반적입니다. :). 해결책은 읽기가 A에서 B로 이동할 필요가 없도록하는 것입니다 (예 : A에 포함 된 열 또는 프로젝트 된 목록에서 열을 제거) 또는 T2가 B에서 A로 이동할 필요가 없도록하는 것입니다 (인덱싱 된 열을 업데이트하지 않음). 불행히도 linq는 당신의 친구가 아닙니다 ...


BTW A와 B는 동일한 테이블의 인덱스입니다
Remus Rusanu

3

@Jeff-나는 확실히 이것에 대한 전문가는 아니지만 거의 모든 호출에서 새로운 컨텍스트를 인스턴스화하여 좋은 결과를 얻었습니다. ADO로 호출 할 때마다 새 연결 개체를 만드는 것과 비슷하다고 생각합니다. 어쨌든 연결 풀링이 계속 사용되기 때문에 오버 헤드는 생각만큼 나쁘지 않습니다.

다음과 같은 전역 정적 도우미를 사용합니다.

public static class AppData
{
    /// <summary>
    /// Gets a new database context
    /// </summary>
    public static CoreDataContext DB
    {
        get
        {
            var dataContext = new CoreDataContext
            {
                DeferredLoadingEnabled = true
            };
            return dataContext;
        }
    }
}

그런 다음 다음과 같이합니다.

var db = AppData.DB;

var results = from p in db.Posts where p.ID = id select p;

그리고 나는 업데이트를 위해 똑같은 일을 할 것입니다. 어쨌든, 나는 당신만큼 트래픽이 많지는 않지만, 소수의 사용자와 함께 초기에 공유 DataContext를 사용했을 때 확실히 약간의 잠금을 얻었습니다. 보장 할 수는 없지만 시도해 볼 가치가 있습니다.

업데이트 : 그런 다음 다시 코드를 살펴보면 특정 컨트롤러 인스턴스의 수명 동안 데이터 컨텍스트 만 공유합니다. 컨트롤러 내에서 여러 호출에 의해 동시에 사용되지 않는 한 기본적으로 괜찮아 보입니다. 주제에 대한 스레드에서 ScottGu는 다음과 같이 말했습니다.

컨트롤러는 단일 요청에 대해서만 존재하므로 요청 처리가 끝나면 가비지 수집됩니다 (즉, DataContext가 수집됨을 의미합니다).

어쨌든, 그것이 아닐 수도 있지만, 아마도 일부 부하 테스트와 함께 시도해 볼 가치가있을 것입니다.


3

Q. 왜 당신은 저장되어 AnswerCount에서 Posts처음에 테이블?

다른 방법은 Posts테이블에를 저장하지 않고 AnswerCount필요에 따라 게시물에 대한 답변 수를 동적으로 계산하여 테이블에 대한 "다시 쓰기"를 제거하는 것 입니다.

예, 이것은 추가 쿼리를 실행하고 있음을 의미합니다.

SELECT COUNT(*) FROM Answers WHERE post_id = @id

또는 더 일반적으로 (홈 페이지에 이것을 표시하는 경우) :

SELECT p.post_id, 
     p.<additional post fields>,
     a.AnswerCount
FROM Posts p
    INNER JOIN AnswersCount_view a
    ON <join criteria>
WHERE <home page criteria>

그러나 이것은 일반적으로를 INDEX SCAN사용하는 것보다 리소스를 사용하는 데 더 효율적일 수 있습니다 READ ISOLATION.

고양이 피부를 만드는 방법은 여러 가지가 있습니다. 데이터베이스 스키마의 조기 비정규 화는 확장 성 문제를 일으킬 수 있습니다.


3

READ_COMMITTED_SNAPSHOT이 기본적으로 설정되어 있지 않은 설정을 원합니다. 그것은 당신에게 MVCC 의미론을 제공합니다. Oracle이 기본적으로 사용하는 것과 동일합니다. MVCC 데이터베이스를 갖는 것은 매우 유용하며 하나를 사용하지 않는 것은 제정신이 아닙니다. 이를 통해 트랜잭션 내에서 다음을 실행할 수 있습니다.

사용자 업데이트 Set FirstName = 'foobar'; // 1 년 동안 자기로 결정합니다.

한편 위의 내용을 적용하지 않고도 모든 사람이 해당 테이블에서 계속 선택할 수 있습니다. MVCC에 익숙하지 않다면 MVCC없이 살 수 있었다는 사실에 충격을받을 것입니다. 진지하게.


3

기본값을 커밋되지 않은 읽기로 설정하는 것은 좋은 생각이 아닙니다. 당신의 의지는 의심 할 여지없이 불일치를 야기하고 당신이 현재 가지고있는 것보다 더 나쁜 문제에 직면하게됩니다. 스냅 샷 격리는 잘 작동 할 수 있지만 Sql Server가 작동하는 방식이 크게 변경되어 tempdb에 막대한 부하를가합니다.

수행해야 할 작업은 다음과 같습니다. T-SQL에서 try-catch를 사용하여 교착 상태를 감지합니다. 이 경우 쿼리를 다시 실행하십시오. 이것은 표준 데이터베이스 프로그래밍 관행입니다.

Paul Nielson의 Sql Server 2005 Bible 에이 기술의 좋은 예가 있습니다 .

내가 사용하는 간단한 템플릿은 다음과 같습니다.

-- Deadlock retry template

declare @lastError int;
declare @numErrors int;

set @numErrors = 0;

LockTimeoutRetry:

begin try;

-- The query goes here

return; -- this is the normal end of the procedure

end try begin catch
    set @lastError=@@error
    if @lastError = 1222 or @lastError = 1205 -- Lock timeout or deadlock
    begin;
        if @numErrors >= 3 -- We hit the retry limit
        begin;
            raiserror('Could not get a lock after 3 attempts', 16, 1);
            return -100;
        end;

        -- Wait and then try the transaction again
        waitfor delay '00:00:00.25';
        set @numErrors = @numErrors + 1;
        goto LockTimeoutRetry;

    end;

    -- Some other error occurred
    declare @errorMessage nvarchar(4000), @errorSeverity int
    select    @errorMessage = error_message(),
            @errorSeverity = error_severity()

    raiserror(@errorMessage, @errorSeverity, 1)

    return -100
end catch;    

2
이 솔루션은 왜 나를 움찔하게 만들까요? !! 난 왜 교착 상태가되는지 .. 문제에 대해 정말 가난한 사람의 반창고가 아닌지 살펴볼 것입니다.
Pure.Krome

2

과거에 저에게 도움이 된 한 가지는 모든 쿼리와 업데이트가 동일한 순서로 리소스 (테이블)에 액세스하는지 확인하는 것입니다.

즉, 하나의 쿼리가 Table1, Table2의 순서로 업데이트되고 다른 쿼리가 Table2, Table1의 순서로 업데이트하면 교착 상태가 발생할 수 있습니다.

LINQ를 사용하고 있기 때문에 업데이트 순서를 변경할 수 있는지 확실하지 않습니다. 그러나 그것은 봐야 할 것입니다.


1

사용자 프로필이 몇 초가 지난 경우에도 신경 쓰시겠습니까?

몇 초면 확실히 허용됩니다. 많은 사람들이 동시에 답변을 제출하지 않는 한 어쨌든 그렇게 길지 않은 것 같습니다.


1

나는 이것에 대해 Jeremy와 동의합니다. 각 컨트롤러 또는 페이지 당 새 데이터 컨텍스트를 만들어야하는지 묻습니다. 저는 모든 독립적 인 쿼리에 대해 새 데이터 컨텍스트를 만드는 경향이 있습니다.

나는 현재 당신처럼 정적 컨텍스트를 구현하는 데 사용되는 솔루션을 구축하고 있으며 스트레스 테스트 중에 서버 (백만 이상)의 짐승에 수많은 요청을 던졌을 때 무작위로 읽기 / 쓰기 잠금을 얻었습니다.

쿼리마다 LINQ 수준에서 다른 데이터 컨텍스트를 사용하도록 전략을 변경하고 SQL 서버가 연결 풀링 마법을 사용할 수 있다고 믿 자마자 잠금이 사라진 것처럼 보였습니다.

물론 저는 시간 압박을 받고 있었기 때문에 거의 같은 시간에 여러 가지 작업을 시도했기 때문에 그것이 문제를 해결했는지 100 % 확신 할 수는 없지만 높은 수준의 자신감을 가지고 있습니다. .


1

더티 읽기를 구현해야합니다.

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

쿼리에서 완벽한 트랜잭션 무결성이 절대적으로 필요하지 않은 경우 동시성이 높은 테이블에 액세스 할 때 더티 읽기를 사용해야합니다. Posts 테이블이 그중 하나라고 가정합니다.

이것은 커밋되지 않은 트랜잭션의 데이터에 대해 쿼리가 작동 할 때 소위 "팬텀 읽기"를 제공 할 수 있습니다.

우리는 여기서 은행 사이트를 운영하지 않습니다. 매번 완벽한 정확성이 필요하지 않습니다.

더티 읽기를 사용하십시오. 완벽한 정확도를 제공하지는 않지만 데드락 문제를 해결해야한다는 점에서 당신 말이 맞습니다.

그것 없이는 우리가 만드는 모든 LINQ 호출 (그 중 대부분인 간단한 읽기 호출)을 3-4 줄 트랜잭션 코드 블록에 래핑해야합니다.

"기본 데이터베이스 컨텍스트"에서 더티 읽기를 구현하는 경우 트랜잭션 무결성이 필요한 경우 항상 더 높은 격리 수준을 사용하여 개별 호출을 래핑 할 수 있습니다.


1

그렇다면 재시도 메커니즘 구현의 문제점은 무엇입니까? 항상 교착 상태가 발생할 가능성이 있으므로이를 식별하고 다시 시도 할 논리가없는 이유는 무엇입니까?

적어도 다른 옵션 중 일부는 재시도 시스템이 거의 시작되지 않는 경우 항상 수행되는 성능 패널티를 도입하지 않습니까?

또한 재 시도가 발생할 때 일종의 로깅을 잊지 않도록하여 드물게 자주 발생하는 상황에 빠지지 않도록하십시오.


1

이제 Jeremy의 답변을 보았으므로 모범 사례는 각 데이터 작업에 대해 새로운 DataContext를 사용하는 것임을 들었습니다. Rob Conery는 DataContext에 대한 여러 게시물을 작성했으며 싱글 톤을 사용하는 대신 항상 소식을 전합니다.

다음은 Video.Show에 사용한 패턴입니다 ( CodePlex의 소스보기에 대한 링크 ).

using System.Configuration;
namespace VideoShow.Data
{
  public class DataContextFactory
  {
    public static VideoShowDataContext DataContext()
    {
        return new VideoShowDataContext(ConfigurationManager.ConnectionStrings["VideoShowConnectionString"].ConnectionString);
    }
    public static VideoShowDataContext DataContext(string connectionString)
    {
        return new VideoShowDataContext(connectionString);
    }
  }
}

그런 다음 서비스 수준에서 (또는 업데이트의 경우 더 세분화 됨) :

private VideoShowDataContext dataContext = DataContextFactory.DataContext();

public VideoSearchResult GetVideos(int pageSize, int pageNumber, string sortType)
{
  var videos =
  from video in DataContext.Videos
  where video.StatusId == (int)VideoServices.VideoStatus.Complete
  orderby video.DatePublished descending
  select video;
  return GetSearchResult(videos, pageSize, pageNumber);
}

0

커밋되지 않은 읽기로 격리 수준을 설정하는 것이 다른 쿼리에 악영향을 미치지 않는 한 Greg와 동의해야합니다.

Jeff, 데이터베이스 수준에서 설정하면 다음과 같은 쿼리에 어떤 영향을 미치는지 알고 싶습니다.

Begin Tran
Insert into Table (Columns) Values (Values)
Select Max(ID) From Table
Commit Tran

0

내 프로필이 몇 분이라도 오래 되어도 괜찮습니다.

읽기가 실패한 후 다시 시도하고 있습니까? 많은 무작위 읽기를 실행할 때 몇 가지가 읽을 수 없을 때 히트 할 가능성이 있습니다. 내가 작업하는 대부분의 응용 프로그램은 읽기 수에 비해 쓰기가 거의 없으며 읽기가 당신이 얻는 수에 가깝지 않을 것이라고 확신합니다.

"READ UNCOMMITTED"를 구현해도 문제가 해결되지 않으면 처리에 대해 더 많이 알지 못하고 도움을주기가 어렵습니다. 이 동작에 도움이되는 다른 조정 옵션이있을 수 있습니다. 일부 MSSQL 전문가가 구출하지 않는 한 공급 업체에 문제를 제출하는 것이 좋습니다.


0

나는 모든 것을 계속 조정할 것입니다. 디스크 하위 시스템은 어떻게 작동합니까? 평균 디스크 대기열 길이는 얼마입니까? I / O가 백업되는 경우 실제 문제는 교착 상태 인 두 쿼리가 아니라 시스템 병목 현상을 일으키는 다른 쿼리 일 수 있습니다. 조정 된 20 초가 걸리는 쿼리를 언급하셨습니다. 다른 질문이 있습니까?

장기 실행 쿼리를 줄이는 데 초점을 맞추면 교착 상태 문제가 사라질 것입니다.


0

동일한 문제가 발생했으며 서버에서 DTS를 사용하도록 설정 (!)하지 않았기 때문에 TransactionScope에서 "IsolationLevel = IsolationLevel.ReadUncommitted"를 사용할 수 없습니다.

그것이 내가 확장 방법으로 한 일입니다.

public static void SetNoLock(this MyDataContext myDS)
{
    myDS.ExecuteCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED");
}

따라서 중요한 동시성 테이블을 사용하는 선택을 위해 다음과 같이 "nolock"을 활성화합니다.

using (MyDataContext myDS = new MyDataContext())
{
   myDS.SetNoLock();

   //  var query = from ...my dirty querys here...
}

Sugestions는 환영합니다!

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.