기록 데이터를 저장하는 방법


162

일부 동료와 나는 과거 데이터를 저장하는 가장 좋은 방법에 대해 토론했습니다. 현재 일부 시스템의 경우 별도의 테이블을 사용하여 기록 데이터를 저장하고 현재 활성 레코드에 대한 원래 테이블을 유지합니다. 테이블 FOO가 있다고 가정 해 봅시다. 내 시스템에서 모든 활성 레코드는 FOO에 저장되고 모든 기록 레코드는 FOO_Hist에 저장됩니다. FOO의 많은 다른 필드는 사용자가 업데이트 할 수 있으므로 모든 항목에 대한 정확한 계정을 업데이트하고 싶습니다. FOO_Hist는 자동 증가 HIST_ID를 제외하고 FOO와 정확히 동일한 필드를 보유합니다. FOO가 업데이트 될 때마다 FOO_Hist에 다음과 유사한 삽입 문을 수행합니다 insert into FOO_HIST select * from FOO where id = @id.

동료는 역사적인 이유로 테이블의 정확한 사본을 가져서는 안되며 역사적인 목적을 나타내는 플래그가있는 활성 레코드에 다른 레코드를 삽입해야하기 때문에 이것이 나쁜 디자인이라고 말합니다.

히스토리 데이터 스토리지를 처리하기위한 표준이 있습니까? 백만 개가 넘는 레코드가있을 수 있다는 점을 고려할 때 내 활성 레코드를 동일한 테이블의 모든 내 레코드로 어지럽히고 싶지는 않습니다 (장기 생각하고 있습니다).

당신이나 당신 회사는 이것을 어떻게 처리합니까?

MS SQL Server 2008을 사용하고 있지만 모든 DBMS에 대한 일반적인 대답을 유지하고 싶습니다.

답변:


80

운영 체제 내에서 히스토리 데이터를 직접 지원하면 애플리케이션이 이전보다 훨씬 복잡해집니다. 일반적으로 시스템 내에서 레코드의 히스토리 버전을 조작하기 어려운 요구 사항이 없으면 수행하지 않는 것이 좋습니다.

자세히 살펴보면 내역 데이터에 대한 대부분의 요구 사항은 다음 두 가지 범주 중 하나에 속합니다.

  • 감사 로깅 : 감사 테이블을 사용하는 것이 좋습니다. 시스템 데이터 사전에서 메타 데이터를 읽어 감사 로그 테이블 및 트리거를 작성하는 스크립트를 생성하는 도구를 작성하는 것은 매우 쉽습니다. 이 유형의 도구는 대부분의 시스템에 감사 로깅을 개선하는 데 사용할 수 있습니다. 데이터웨어 하우스를 구현하려는 경우 변경된 서브 시스템에이 서브 시스템을 사용할 수도 있습니다 (아래 참조).

  • 내역보고 : 과거 상태, '현재 상태'위치 또는 시간에 따른 분석보고에 대한보고. 위에서 설명한 종류의 감사 로깅 테이블을 쿼리하여 간단한 기록보고 요구 사항을 충족 할 수 있습니다. 보다 복잡한 요구 사항이있는 경우 기록을 운영 체제에 직접 통합하려고 시도하는 것보다보고를 위해 데이터 마트를 구현하는 것이 더 경제적 일 수 있습니다.

    차원이 느리게 변경되는 것은 기록 상태를 추적하고 쿼리하는 가장 간단한 메커니즘이며, 기록 추적의 대부분을 자동화 할 수 있습니다. 일반 핸들러는 작성하기가 어렵지 않습니다. 일반적으로 기록보고에는 최신 데이터를 사용할 필요가 없으므로 일괄 새로 고침 메커니즘이 일반적으로 적합합니다. 이렇게하면 핵심 및보고 시스템 아키텍처가 비교적 단순 해집니다.

요구 사항이이 두 범주 중 하나에 해당되면 운영 체제에 기록 데이터를 저장하지 않는 것이 좋습니다. 히스토리 기능을 다른 서브 시스템으로 분리하면 전체적인 노력이 줄어들고 의도 한 목적에 훨씬 더 잘 작동하는 트랜잭션 및 감사 /보고 데이터베이스를 생성 할 수 있습니다.


무슨 말인지 알 것 같아 FOO_Hist 테이블로 수행 한 작업은 실제로 감사 테이블을 생성하는 것이 었습니다. 업데이트시 감사 테이블에 삽입하기 위해 트리거를 사용하는 대신 프로그램에서 명령문을 실행했습니다. 그 맞습니까?
Aaron Aaron

6
거의 요 그러나 트리거를 사용하여 이러한 종류의 감사 로깅을 수행하는 것이 좋습니다. 트리거는 모든 변경 사항 (수동 데이터 수정 사항 포함)이 감사 로그에 기록되도록합니다. 감사 할 테이블이 10-20 개가 넘으면 트리거 생성기 도구를 빌드하는 것이 훨씬 빠릅니다. 감사 로그의 디스크 트래픽에 문제가있는 경우 감사 로그 테이블을 별도의 디스크 세트에 넣을 수 있습니다.
ConcernedOfTunbridgeWells

네, 100 % 동의합니다. 감사합니다.
Aaron

40

나는 그것을하는 특별한 표준 방법이 없다고 생각하지만 가능한 방법으로 던질 것이라고 생각했습니다. Oracle과 애플리케이션 데이터 저장을 위해 XML을 사용하는 사내 웹 애플리케이션 프레임 워크에서 작업합니다.

가장 간단한 마스터-디테일 모델을 사용합니다.

예를 들어 마스터 테이블 에는 Widgets종종 ID가 포함되어 있습니다. 종종 시간이 지나도 변하지 않거나 기록이없는 데이터가 포함됩니다.

예를 들어 다음을 Widget_Details포함하는 상세 / 이력 테이블 :

  • ID-기본 키 상세 / 이력 ID
  • MASTER_ID-예를 들어 'WIDGET_ID'라는이 경우 마스터 레코드에 대한 FK입니다.
  • START_DATETIME-해당 데이터베이스 행의 시작을 나타내는 타임 스탬프
  • END_DATETIME-해당 데이터베이스 행의 끝을 나타내는 타임 스탬프
  • STATUS_CONTROL-단일 문자 열이 행의 상태를 나타냅니다. 'C'는 현재, NULL 또는 'A'는 기록 / 아카이브됨을 나타냅니다. END_DATETIME에 색인을 생성 할 수 없으므로 NULL 만 사용합니다.
  • CREATED_BY_WUA_ID-행을 생성 한 계정의 ID를 저장합니다
  • XMLDATA-실제 데이터를 저장

따라서 기본적으로 엔터티는 마스터에 1 개의 행과 세부 사항에 1 개의 행을 갖는 것으로 시작합니다. NULL 종료 날짜와 STATUS_CONTROL이 'C'인 세부 사항입니다. 업데이트가 발생하면 현재 행이 현재 시간의 END_DATETIME을 갖도록 업데이트되고 status_control이 NULL (또는 선호하는 경우 'A')로 설정됩니다. 세부 사항 테이블에 새 행이 작성되며, 여전히 동일한 마스터에 링크되어 있으며 status_control 'C', 업데이트하는 개인의 ID 및 XMLDATA 컬럼에 저장된 새 데이터가 있습니다.

이것이 우리의 역사적 모델의 기초입니다. Create / Update 로직은 Oracle PL / SQL 패키지에서 처리되므로 현재 ID, 사용자 ID 및 새 XML 데이터를 함수에 전달하고 내부적으로 행의 모든 ​​업데이트 / 삽입을 수행하여 히스토리 모델에서이를 표시합니다. . 시작 및 종료 시간은 테이블의 해당 행이 활성화 된시기를 나타냅니다.

스토리지는 저렴하며 일반적으로 데이터를 삭제하지 않으며 감사 추적을 유지하는 것을 선호합니다. 이를 통해 주어진 시간에 데이터가 어떻게 보이는지 확인할 수 있습니다. status_control = 'C'를 인덱싱하거나 View를 사용하면 혼란이 문제가되지 않습니다. 분명히 쿼리는 항상 현재 (NULL end_datetime 및 status_control = 'C') 버전의 레코드를 사용해야합니다.


안녕하세요 크리스, 당신이 그렇게하면 ID (기본 키)를 올바르게 변경해야합니까? 다른 테이블에서 다른 테이블을 사용하는 경우 다른 테이블과의 관계는 어떻습니까?
projo

@projo 마스터 테이블의 ID는 PK이며 개념적으로 다루는 개념에 대한 "PK"입니다. 세부 사항 테이블의 ID는 마스터의 히스토리 버전 (세부 사항의 다른 열)을 식별하기위한 PK입니다. 관계를 형성 할 때 개념의 실제 PK (예 : 마스터 테이블의 ID 또는 세부 사항의 MASTER_ID 열)를 참조하고 STATUS_CONTROL = 'C'를 사용하여 현재 버전을 얻는 지 확인하십시오. 또는 세부 사항 ID를 참조하여 특정 시점과 무언가를 연관시킬 수 있습니다.
Chris Cameron-Mills

+1 여러 대규모 프로젝트에서이 패턴을 성공적으로 구현했습니다.
Three Value Logic

우리는 궁금하고 지금 같은 aproach.But를 사용하면 더 나은에만 START_DATETIME 저장하고 END_DATETIME 저장하지 않는 것입니다
bat_ventzi

내 경험에 몇 가지 변형. 엔터티가 "종료"(즉, 보관 또는 삭제) 된 경우 실제로 'C'상태 제어에 대한 세부 레코드가 없을 수 있습니다 (예 : 현재 행이 없음). 언제 발생했는지 알 수는 없습니다. 또는 마지막 행에서 end_datetime을 설정하고 'ended' 'C'행이 있으면 엔티티가 삭제 / 아카이브되었음을 나타낼 수 있습니다. 마지막으로, 이미 가지고있을 수도있는 STATUS 열을 통해이를 나타낼 수 있습니다.
Chris Cameron-Mills

15

나는 당신이 접근하는 것이 맞다고 생각합니다. 히스토리 테이블은 인덱스가없는 기본 테이블의 사본이어야합니다. 테이블에 업데이트 시간 소인도 있는지 확인하십시오.

다른 방법을 빨리 시도하면 문제가 발생합니다.

  • 유지 관리 오버 헤드
  • 선택에 더 많은 플래그
  • 쿼리 속도 저하
  • 테이블, 인덱스의 성장

7

에서 SQL 서버 2016 이상 ,라는 새로운 기능이 임시 테이블 의 목표는이 문제 해결하는 것을 개발자로부터 최소한의 노력은 . 임시 테이블의 개념은 CDC (Change Data Capture)와 유사하지만 임시 테이블이 CDC를 사용하는 경우 수동으로 수행해야하는 대부분의 작업을 추상화했다는 차이점이 있습니다.




1

Azure SQL을 사용하기 때문에 사용하기 시작한 옵션을 추가하고 싶었고 여러 테이블이 너무 번거 롭습니다. 내 테이블에 삽입 / 업데이트 / 삭제 트리거를 추가 한 다음 "FOR JSON AUTO"기능을 사용하여 변경 전 / 후 변경을 json으로 변환했습니다.

 SET @beforeJson = (SELECT * FROM DELETED FOR JSON AUTO)
SET @afterJson = (SELECT * FROM INSERTED FOR JSON AUTO)

변경 전후의 레코드에 대한 JSON 표현을 반환합니다. 그런 다음 변경이 발생한 시간의 타임 스탬프와 함께 해당 값을 기록 테이블에 저장합니다 (현재 관심있는 레코드의 ID도 저장함). 직렬화 프로세스를 사용하여 스키마 변경시 데이터가 다시 채워지는 방식을 제어 할 수 있습니다.

나는이 링크에서 이것에 대해 배운 여기


0

테이블을 분할 할 수 있습니까?

"SQL Server 2008을 사용하여 분할 된 테이블 및 인덱스 전략 데이터베이스 테이블의 크기가 수백 기가 바이트 이상으로 커지면 새 데이터를로드하고 오래된 데이터를 제거하며 인덱스를 유지 관리하기가 더 어려워 질 수 있습니다. 로드하거나 제거해야하는 데이터의 크기도 매우 커져서 테이블에 대한 INSERT 및 DELETE 작업이 실용적이지 않게 될 수 있습니다. Microsoft SQL Server 2008 데이터베이스 소프트웨어는 이러한 작업을보다 쉽게 ​​관리 할 수 ​​있도록 테이블 파티셔닝을 제공합니다. "


예, 테이블을 분할 할 수 있지만 기록 데이터를 처리 할 때의 표준입니까? 히스토리 데이터가 활성 데이터와 동일한 테이블에 포함되어야합니까? 이것들은 내가 논의하고 싶은 질문입니다. 또한 SQL Server 2008과 관련하여 임의적이지 않습니다.
Aaron

0

실제 문제는보고를 위해 내역 데이터와 활성 데이터를 함께 사용해야합니까? 그렇다면 하나의 테이블에 보관하고 활성 쿼리에서 사용할 활성 레코드에 대한보기를 분할하고 작성하십시오. 가끔씩 (리갈 문제 나 그와 같은 것을 조사하기 위해) 조사해야하는 경우 별도의 테이블에 두십시오.


2
그것은 더 어려운 JOIN몇 내역 보고서에서 두 개의 테이블 또는 모든 단일 테이블 삽입 / 업데이트를 수정하는 것이 더 어렵다 / 역사 문제 인식을 삭제 하시겠습니까? 실제로 감사 로그에는 기록 테이블에 현재 데이터도 포함되므로 보고서에 현재 테이블이 필요하지 않습니다.

0

다른 옵션은 [매일 | 시간별] 운영 데이터를 아카이브하는 것입니다. 대부분의 데이터베이스 엔진 은 데이터를 아카이브로 추출하는 것을 지원합니다 .

기본적으로 아이디어는 예약 된 Windows 또는 CRON 작업을 만드는 것입니다.

  1. 운영 데이터베이스에서 현재 테이블을 결정합니다.
  2. 모든 테이블의 모든 데이터를 CSV 또는 XML 파일로 선택
  3. 아카이빙을 쉽게하기 위해 파일 이름으로 생성 된 타임 스탬프를 사용하여 내 보낸 데이터를 ZIP 파일로 압축합니다.

많은 SQL 데이터베이스 엔진에는이 용도로 사용할 수있는 도구가 제공됩니다. 예를 들어 Linux에서 MySQL을 사용할 때 CRON 작업에서 다음 명령을 사용하여 추출을 예약 할 수 있습니다.

mysqldump --all-databases --xml --lock-tables=false -ppassword | gzip -c | cat > /media/bak/servername-$(date +%Y-%m-%d)-mysql.xml.gz

2
누군가가 값을 변경하고 아카이브주기 내에서 다시 변경하면 업데이트가 손실되므로 이력 데이터에는 전혀 적합하지 않습니다. 시간이 지남에 따라 하나의 엔티티에 대한 변경 사항을 보거나 엔티티를 부분적으로 복원하는 쉬운 방법도 없습니다.
Sgoettschkes

0

이 오래된 게시물을 알고 있지만 몇 가지 요점을 추가하고 싶었습니다. 이러한 문제에 대한 표준은 상황에 가장 적합한 것입니다. 이러한 스토리지의 필요성을 이해하고 기록 / 감사 / 변경 추적 데이터의 잠재적 사용은 매우 중요합니다.

감사 (보안 목적) : 모든 감사 가능 테이블에 공통 테이블을 사용하십시오. 값 앞과 값 필드 뒤에 열 name을 저장할 구조를 정의하십시오.

보관 / 기록 : 이전 주소 추적, 전화 번호 등 별도의 테이블 생성 FOO_HIST가 미래에 활성 트랜잭션 테이블 스키마가 크게 변경되지 않는 경우 (기록 테이블이 동일한 구조를 가져야하는 경우) 더 좋습니다. 테이블 정규화, 데이터 유형 변경 열 추가 / 제거를 예상하는 경우 히스토리 데이터를 xml 형식으로 저장하십시오. 다음 열 (ID, 날짜, 스키마 버전, XMLData)로 테이블을 정의하십시오. 이것은 스키마 변경을 쉽게 처리 할 수 ​​있습니다. 그러나 당신은 xml을 다루어야하고 그것은 데이터 검색을위한 수준의 복잡성을 야기 할 수있다.



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