SQL Server 테이블에서 변경 감지


13

내 응용 프로그램에서 SQL Server 2012에서 DB를 실행하면 비싼 쿼리를 주기적으로 실행하고 나중에 응용 프로그램에서 쿼리 할 수있는 테이블에 결과를 쓰는 작업 (예약 된 작업)이 있습니다.

이상적으로는 쿼리가 마지막으로 실행 된 이후에 변경된 것이있는 경우에만 비싼 쿼리를 실행하고 싶습니다. 소스 테이블이 매우 크기 때문에 모든 후보 열 또는 이와 유사한 항목에 대한 체크섬을 선택할 수는 없습니다.

다음과 같은 아이디어가 있습니다.

  • 소스 테이블에서 무언가를 변경할 때마다 마지막으로 변경된 타임 스탬프, "조회해야 함"플래그 또는 이와 유사한 것을 추적 테이블에 기록합니다.
  • 트리거를 사용하여 동일한 작업을 수행하십시오.

그러나 필자는 명시 적으로 쓰기를 추적하지 않고 테이블의 변경 사항을 감지하는 간단한 방법이 있는지 알고 싶습니다. 예를 들어, ROWVERSION테이블 의 "현재" 또는 이와 유사한 것을 얻을 수 있습니까?

답변:


14

아니요, 없습니다. 모든 트랜잭션의 모든 업데이트가 '마지막 업데이트 시간'을 추적하는 하나의 레코드를 업데이트하려고 할 때 모든 종류의 '마지막 업데이트 시간'추적은 심각한 성능 문제를 야기합니다. 즉, 한 트랜잭션만으로도 언제든지 테이블을 업데이트 할 수 있으며 다른 모든 트랜잭션은 첫 번째 트랜잭션이 커밋 될 때까지 기다려야합니다 . 완전한 직렬화. 마지막 업데이트가 발생한시기를 알기 위해 그러한 성능 저하를 감수 할 관리자 / 개발자 수는 적을 것입니다.

따라서 사용자 정의 코드를 통해 처리해야합니다. 대안 (로그 레코드에서 감지)이 트랜잭션 복제 (또는 CDC alter-ego) 전용으로 예약 된 특권이므로 트리거를 의미합니다 . '마지막 업데이트 시간'열을 통해 추적하려고하면 위에서 언급 한 직렬화 문제에 직면하게됩니다. 업데이트 동시성이 중요한 경우 큐 메커니즘을 사용해야합니다 (트리거는 INSERT를 사용한 다음 프로세스는 삽입 된 값을 집계하여 '최종 업데이트 시간'을 공식화합니다). 현재 ID를 몰래 숨기 거나 sys.dm_db_index_usage_stats를 찾는 것과 같은 '영리한'솔루션으로 속이지 마십시오 . 또한 Rails 타임 스탬프와 같이 레코드 당 'updated_at'열은

'경량'대안이 있습니까? 실제로 하나가 있지만 그것이 당신에게 효과가 있고 그것을 올바르게 얻는 것이 어렵다는 것을 말하기는 어렵습니다 : Query Notifications . 쿼리 알림 경우 알림을 설정 정확히 있다는 않는 모든 데이터 변경 사항을 가지고 있으며, 당신이 당신의 쿼리를 새로 고침 할 필요가있다. 대부분의 개발자는 SqlDependency와 같은 .Net 화신에만 익숙하지만 쿼리 알림 데이터 변경을 감지하는 오래 지속되는 지속적인 메커니즘으로 사용될 있습니다. 진정한 변경 추적과 비교하면 실제로 가벼워지고 의미는 사용자의 요구에 가깝습니다 (무언가, 무엇이든 , 변경되었으므로 쿼리를 다시 실행해야 함).

그러나 결국, 당신의 장소에서, 나는 실제로 내 가정을 재고하고 드로잉 보드로 돌아갑니다. 로그 전달 또는 복제를 사용하여 다른 서버에서보고 데이터베이스를 설정할 수 있습니다. 내가 줄 사이에서 읽은 것은 적절한 ETL 파이프 라인과 분석 데이터웨어 하우스가 필요하다는 것입니다 ...


그렇다면 제공하는 정보를 신뢰할 수없는 경우 왜 Microsoft가 sys.dm_db_index_usage_stats를 생성하지 않아도됩니까?
Craig Efrein

변경 추적을 위해 설계된 DMV가 아닙니다 . 성능 조정 인 의도 된 목적에 매우 안정적입니다.
Remus Rusanu

8

나는 게임에 2 년 늦었다 고 생각하지만, 실제로 당신이 요구하는 것을하는 매우 가벼운 방법이 있습니다.

도움이 될 수있는 두 가지 SQL Server 메커니즘이 있습니다. 궁극적 인 솔루션은이 둘의 하이브리드 일 수 있습니다.

추적 변경 . SQL Server에는 특정 테이블을 감시하는 기능이 있으며 기본 키 값에 따라 변경된 행과 변경 유형 (삽입, 업데이트 또는 삭제) 만 기록합니다. 테이블 집합에 대한 변경 감지를 설정하면 간단한 쿼리를 통해 마지막으로 확인한 이후에 테이블이 변경되었는지 여부를 알 수 있습니다. 오버 헤드는 추가 단순 인덱스 유지와 거의 같습니다.

Rowversion / 타임 스탬프 . 이것은 8 바이트 varbinary 열 유형 (BigInt로 캐스트 가능)으로, 행을 포함하는 행이 삽입 또는 갱신 될 때마다 (데이터베이스 전체에서 증가) 삭제에 도움이되지 않습니다. 이러한 열을 색인화 한 경우 마지막으로 평가 된 이후 MAX (timestamp)를 해당 값과 비교하여 행 데이터가 변경되었는지 쉽게 알 수 있습니다. 값이 단조 증가하므로 새 값이 마지막으로 확인한 값보다 큰 경우 데이터가 변경되었음을 신뢰할 수 있습니다.


7

소스가 삽입 전용 인 경우 IDENTITY열을 제공하십시오 . 데이터 전송을 수행하면 가장 높은 값을 기록합니다. 다음 전송 중에는 이전 전송 중에 기록 된 값보다 큰 값만 쿼리하면됩니다. 로그 레코드를 데이터웨어 하우스로 전송하기 위해이 작업을 수행합니다.

업데이트 가능한 행의 경우 "더티"플래그를 추가하십시오. 깨끗하고 더러 우며 삭제 된 세 가지 값이 있습니다. 일상적인 쿼리는 플래그가 "삭제됨"으로 설정된 행을 생략해야합니다. 유지 관리, 테스트 및 런타임에 비용이 많이 듭니다. 큰 쿼리 후에 삭제 표시된 모든 행을 제거하고 다른 모든 플래그는 플래그를 재설정해야합니다. 이것은 잘 확장되지 않습니다.

변경 데이터 캡처에 대한 더 가벼운 대안은 변경 추적 입니다. 어떤 값이 변경 되었는지 알려주지 않고 마지막으로 쿼리 된 이후에 행이 변경되었음을 알려줍니다 . 내장 된 기능으로 변경된 값을 검색하고 추적을 관리 할 수 ​​있습니다. CT를 사용하여 100,000,000 개의 행 테이블에서 하루에 약 100,000 건의 변경 사항을 처리했습니다.

쿼리 알림은 결과 집합 수준에서 여전히 높은 레버리지로 작동합니다. 개념적으로 뷰를 정의하는 것과 같습니다. SQL Server는 해당 뷰를 통해 반환 된 행이 변경되었음을 감지하면 응용 프로그램에 메시지를 보냅니다. 변경된 행 수 또는 열이 표시되지 않습니다. "무언가 발생했습니다"라는 간단한 메시지 만 있습니다. 문의하고 반응하는 것은 응용 프로그램에 달려 있습니다. 실제로는 상상할 수 있듯이 그보다 훨씬 복잡합니다. 쿼리를 정의하는 방법에 대한 제한이 있으며 변경된 데이터 이외의 조건에 대해 알림이 발생할 수 있습니다. 알림이 발생하면 제거됩니다. 이후에 관심있는 활동이 더 발생하면 더 이상 메시지가 전송되지 않습니다.

OP의 질문과 관련하여 QN은 설치 비용이 적고 런타임 비용이 적다는 이점이 있습니다. 엄격한 가입 메시지 반응 체제를 확립하고 유지하는 것이 상당한 노력 일 수 있습니다. 데이터 테이블이 크기 때문에 데이터 테이블이 자주 변경 될 가능성이 높으므로 대부분의 처리주기에서 알림이 실행될 수 있습니다. CT 또는 CDC에서와 같이 델타의 증분 처리가 변경된 사항에 대한 표시가 없으므로 불가능합니다. 잘못된 트리거로 인한 오버 헤드는 번거롭지 만 최악의 경우에도 비싼 쿼리를 현재보다 더 자주 실행할 필요는 없습니다.


3

SqlTableDependency

SqlTableDependency는 SQL Server 데이터베이스의 테이블 레코드 값이 포함 된 알림에 액세스하기위한 고급 구현 구성 요소입니다.

SqlTableDependency는 지정된 데이터베이스 테이블의 내용이 변경 될 때 알림을받는 데 사용되는 일반 C # 구성 요소입니다.

.NET SqlDepenency와의 차이점은 무엇입니까?

기본적으로 주요 차이점은 SqlTableDependency는 테이블에서 실행 된 DML 작업 (삽입 / 삭제 / 업데이트)뿐만 아니라 삽입, 변경 또는 삭제 된 레코드의 값을 포함하는 이벤트를 전송한다는 것입니다. SqlDepenency는 데이터베이스 테이블, 그들은 단지 무언가가 바뀌 었다고 말합니다.

GITHUB 프로젝트를 살펴보십시오 .


1

예상되는 업데이트가 인덱스에 영향을 미치는 경우 ( 만 해당되는 경우) 시스템 테이블 sys.dm_db_index_usage_stats을 사용하여 해당 테이블의 인덱스에 대한 마지막 업데이트를 감지 할 수 있습니다 . last_user_update필드를 사용합니다 .

예를 들어 가장 최근에 업데이트 된 테이블을 가져 오려면

select
    object_name(object_id) as OBJ_NAME, *
from
    sys.dm_db_index_usage_stats
where
    database_id = db_id(db_name())
order by
    dm_db_index_usage_stats.last_user_update desc

또는 특정 날짜 이후 특정 테이블이 변경되었는지 확인하려면 다음을 수행하십시오.

select
    case when count(distinct object_id) > 0 then 1 else 0 end as IS_CHANGED
from
    sys.dm_db_index_usage_stats
where
    database_id = db_id(db_name())
    and object_id = object_id('MY_TABLE_NAME')
    and last_user_update > '2016-02-18'

위의 Remus 의견에 대해 어떻게 생각하십니까? "현재 ID를 몰래 숨기거나 sys.dm_db_index_usage_stats를 찾는 것과 같은 '영리한'솔루션으로 속이려고하지 마십시오." (그의 답변 아래 그의 의견도 참조하십시오.)
Fabian Schmied

1
@FabianSchmied Interesting-답변을 추가했을 때이 사용 사례에서 신뢰할 수 없음을 나타내는 다른 Remus의 답변 과는 별개의 것을 찾을 수 없다는 것을 보지 못했습니다 . 에 대한 MS의 페이지 dm_db_index_operational_stats쇼 문제 (메타 데이터 캐시 지 웁니다으로 삭제),하지만하지 않는 dm_db_index_usage_stats. 내가 찾은 유일한 문제는 인덱스 다시 작성, 서버 다시 시작 및 데이터베이스 분리로 사용 통계를 지우는 것인데 여기에 적용된 것처럼 보이지 않았습니다. 이것에 대한 입증 된 정보를보고 싶을 것입니다.
Geoff
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.