감사 로깅을위한 데이터베이스 디자인


151

새 데이터베이스를 디자인해야 할 때마다 변경 사항에 대한 감사 로그를 유지하기 위해 데이터베이스 스키마를 설정하는 방법에 대해 꽤 많은 시간을 할애합니다.

여기에 대해 이미 몇 가지 질문이 있었지만 모든 시나리오에 대해 최선의 단일 접근법이 있다는 것에 동의하지 않습니다.

또한 각 접근 방식의 장단점을 나열하는 데이터베이스 변경 로그 유지 관리에 대한흥미로운 기사를 우연히 발견했습니다 . 잘 작성되어 있고 흥미로운 정보가 있지만 내 결정을 더 어렵게 만들었습니다.

내 질문은 : 내가 사용할 수있는 참조가 있습니까, 책이나 의사 결정 트리와 같은 일부 입력 변수를 기반으로 어떤 방법으로 결정 해야하는지 결정할 수 있습니까?

  • 데이터베이스 스키마의 성숙
  • 로그를 쿼리하는 방법
  • 레코드를 다시 작성해야 할 확률
  • 더 중요한 것 : 쓰기 또는 읽기 성능
  • 기록되는 값의 특성 (문자열, 숫자, 얼룩)
  • 사용 가능한 저장 공간

내가 아는 접근법은 다음과 같습니다.

1. 생성 및 수정 된 날짜 및 사용자에 대한 열 추가

테이블 예 :

  • 신분증
  • value_1
  • value_2
  • value_3
  • created_date
  • modified_date
  • created_by
  • modified_by

주요 단점 : 우리는 수정의 역사를 잃습니다. 커밋 후 롤백 할 수 없습니다.

2. 테이블 만 삽입

테이블 예 :

  • 신분증
  • value_1
  • value_2
  • value_3
  • ...에서
  • 삭제됨 (부울)
  • 사용자

주요 단점 : 외래 키를 최신 상태로 유지하는 방법은 무엇입니까? 필요한 큰 공간

3. 각 테이블에 대해 별도의 히스토리 테이블 작성

히스토리 테이블 예 :

  • 신분증
  • value_1
  • value_2
  • value_3
  • value_4
  • 사용자
  • 삭제됨 (부울)
  • 타임 스탬프

주요 단점 : 모든 감사 된 테이블을 복제해야합니다. 스키마가 변경되면 모든 로그도 마이그레이션해야합니다.

4. 모든 테이블에 대한 통합 히스토리 테이블 작성

히스토리 테이블 예 :

  • table_name
  • 사용자
  • new_value
  • 삭제됨 (부울)
  • 타임 스탬프

주요 단점 : 필요한 경우 레코드 (롤백)를 쉽게 다시 만들 수 있습니까? new_value 열은 거대한 문자열이어야하므로 다른 모든 열 유형을 지원할 수 있습니다.



1
그리고 테이블 대신 히스토리 데이터베이스를 사용하는 것은 어떻습니까?
Jowen

어쩌면 당신의 디자인을 확인할 수 있습니다 github.com/airblade/paper_trail
zx1986

실행 된 모든 (필수) 쿼리를 그대로 기록하는 것은 나쁜 생각입니까?
Dinushan

답변:


87

몇몇 위키 플랫폼에서 사용하는 한 가지 방법은 식별하는 데이터와 감사하는 컨텐츠를 분리하는 것입니다. 복잡성을 추가하지만 편집 한 필드 목록뿐만 아니라 사용자에게 이전 레코드의 모양에 대한 정보를 제공하기 위해 매쉬업해야하는 완전한 레코드의 감사 추적으로 끝납니다.

예를 들어 영업 거래를 추적 할 수있는 기회 라는 테이블이있는 경우 실제로 두 개의 별도 테이블을 작성합니다.

기회
기회 _ 콘텐츠 (또는 이와 유사한 것)

기회 표는 고유 레코드를 식별하는 데 사용하는 것입니다 당신이 당신의 외래 키 관계를 참조하려는 기본 키를 수용 할 정보가있을 것입니다. Opportunities_Content의 표는 사용자가 변경할 수있는 모든 필드를 개최 것이며하는 당신은 감사 추적을 계속하고 싶습니다. Content 테이블의 각 레코드 에는 자체 PK와 수정 날짜 및 수정 날짜 데이터가 포함됩니다. 기회 표는 주요 레코드가 원래 누구에 의해 생성 될 때의 현재 버전을 참조뿐만 아니라 정보를 포함한다.

다음은 간단한 예입니다.

CREATE TABLE dbo.Page(  
    ID int PRIMARY KEY,  
    Name nvarchar(200) NOT NULL,  
    CreatedByName nvarchar(100) NOT NULL, 
    CurrentRevision int NOT NULL, 
    CreatedDateTime datetime NOT NULL

그리고 내용 :

CREATE TABLE dbo.PageContent(
    PageID int NOT NULL,
    Revision int NOT NULL,
    Title nvarchar(200) NOT NULL,
    User nvarchar(100) NOT NULL,
    LastModified datetime NOT NULL,
    Comment nvarchar(300) NULL,
    Content nvarchar(max) NOT NULL,
    Description nvarchar(200) NULL

내용 테이블의 PK를 PageID 및 개정의 다중 열 키로 만들고 개정이 ID 유형 인 경우 수정합니다. 개정 열을 FK로 사용합니다. 그런 다음 다음과 같이 JOINing하여 통합 레코드를 가져옵니다.

SELECT * FROM Page
JOIN PageContent ON CurrentRevision = Revision AND ID = PageID

거기에 약간의 오류가있을 수 있습니다 ... 이것은 내 머리 꼭대기에 있습니다. 그러나 대체 패턴에 대한 아이디어를 제공해야합니다.


10
좋은 감사 접근 방식이지만 프로덕션 환경에서는 데이터베이스의 각 테이블에 대해 별도의 감사 테이블을 개발하고 각 테이블에 대한 트리거를 작성하여 변경 사항을 캡처하고이를 감사 테이블에 쓰는 데 많은 시간이 걸립니다. 또한 각 감사 테이블의 구조가 다르기 때문에 모든 테이블에 대해 단일 감사 보고서를 개발하는 데 큰 어려움이 있습니다.
asim-ishaq

11
감사 된 데이터베이스를 관리하려는 조직이 각 테이블에 대해 스크립트를 작성하고 유지 관리해야하는 경우에는 감사 한 데이터베이스를 작성하는 데 충분한 경험이있는 숙련 된 DBA 또는 매우 유연하고 경험이 풍부한 소프트웨어 엔지니어를 고용하는 것이 좋습니다. .
Hardryv

1
그것은 그가 올바른지 PageContent.PageID에 FK입니다 Page.IDPage.CurrentRevisionFK가있다 PageContent.Revision? 이 의존성이 정말로 원형인가?

2
나는 언급 된 대안들을 다루지 않기 때문에 투표를 중단했습니다. 매우 구체적인 사용 사례에 대한 매우 구체적인 솔루션 인 다른 옵션을 제공합니다. 그러나 저는 제안 된 디자인의 장점을 보았습니다
acteon

1
확신을 가지고 말할 수있는 필드가 거의 없다고 생각할 수 있으므로 각 엔터티에 대한 모든 "기본"테이블이 그대로 유지됩니다 id, revision_id. 더 많은 접합 테이블. 이것은 나에게 약간 냄새가 난다. OP의 접근 방식 3 (감사 된 테이블 당 기록 테이블)에 비해 어떤 이점이 있습니까?
Kenmore

14

SQL Server 2008을 사용하는 경우 변경 데이터 캡처를 고려해야합니다. 이것은 2008 년에 새로 도입되었으며 상당한 양의 작업을 절약 할 수 있습니다.


다음은 SQL 2012 변경 내용 추적 정보에 대한 링크입니다. msdn.microsoft.com/ko-kr/library/bb933994.aspx +1 내장 기능을 사용하여 바퀴를 다시 만들 필요가 없습니다.
Chris

4
@Chris 직접 사용해 본 적이 있습니까? 실제로 모든 것을 추적하지만 유용한 정보를 얻을 수있는 것은 완전히 다른 이야기입니다. 자전거에 트랙터 바퀴를 사용할 수 없습니다.
Jowen

이것은 정말 대단했을 입니다. 그러나 나와 같은 표준 SQL Server 버전 만 가지고 있다면 운이 나쁘다. "데이터 캡처 변경은 Enterprise , DeveloperEnterprise Evaluation 에디션 에서만 사용할 수 있습니다 ."
Brad Turek

6

나는 어떤 참조도 모르지만 누군가 무언가를 썼을 것이라고 확신한다.

그러나 목적이 단순히 감사 로그를 가장 일반적으로 사용하는 상황을 기록하는 것만으로 모든 것을 유지하지 않는 이유는 무엇입니까?

timestamp
username
ip_address
procedureName (if called from a stored procedure)
database
table
field
accesstype (insert, delete, modify)
oldvalue
newvalue

아마도 이것은 트리거에 의해 유지됩니다.


나는 데이터베이스 서버 내에서 그것을 얻는 방법을 모른다. 물론 외부에서 쉽게 할 수있다.
wallyk

5
이것은 원래 질문에 표시된 네 번째 옵션과 동일한 디자인 패턴 인 것 같습니다.
givanse 2014

3

블로깅 애플리케이션을위한 작은 예제 데이터베이스를 작성합니다. 두 개의 테이블이 필요합니다.

blog: 고유 한 게시물 ID, 제목, 콘텐츠 및 삭제 된 플래그를 저장합니다. audit: 레코드 ID, 블로그 게시물 ID, 변경 유형 (NEW, EDIT 또는 DELETE) 및 해당 변경 날짜 / 시간과 함께 기본 히스토리 변경 세트를 저장합니다. 다음 SQL은 blog삭제 된 열을 작성 하고 색인화합니다.

CREATE TABLE `blog` (
    `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
    `title` text,
    `content` text,
    `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0',
    PRIMARY KEY (`id`),
    KEY `ix_deleted` (`deleted`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='Blog posts';

다음 SQL은 audit테이블을 작성 합니다. 모든 열이 색인화되고 blog.id를 참조하는 audit.blog_id에 대한 외래 키가 정의됩니다. 따라서 블로그 항목을 실제로 삭제하면 전체 감사 기록도 제거됩니다.

CREATE TABLE `audit` (
    `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
    `blog_id` mediumint(8) unsigned NOT NULL,
    `changetype` enum('NEW','EDIT','DELETE') NOT NULL,
    `changetime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `ix_blog_id` (`blog_id`),
    KEY `ix_changetype` (`changetype`),
    KEY `ix_changetime` (`changetime`),
    CONSTRAINT `FK_audit_blog_id` FOREIGN KEY (`blog_id`) REFERENCES `blog` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

2

의사 결정 트리와 같은 것은 없다고 생각합니다. 장단점 (또는 요구 사항) 중 일부는 실제로 계산할 수 없기 때문에. 예를 들어 성숙도를 어떻게 측정합니까?

감사 로깅에 대한 비즈니스 요구 사항을 정렬하십시오. 이러한 요구 사항이 향후 어떻게 변경 될지 예측하고 기술 요구 사항을 생성하십시오. 이제 장단점과 비교하여 올바른 / 최상의 옵션을 선택할 수 있습니다.

그리고 당신이 어떻게 결정하든 상관없이, 당신이 항상 잘못 결정했다고 생각하는 사람이있을 것입니다. 그러나, 당신은 숙제를하고 결정을 정당화합니다.

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