시나리오는 확장되는 사용자 집합을 가지고 있으며 시간이 지남에 따라 사용자는 현재 동일한 표에서 ‘삭제 된'(플래그 포함)으로 표시된 계정을 취소합니다.
이메일 주소가 동일한 (사용자가 로그인하는 방식) 사용자가 새 계정을 만들려면 다시 가입 할 수 있지만 새 계정이 만들어집니다. (모든 계정에 고유 한 ID가 있으므로 이메일 주소를 실제 주소와 삭제 된 주소간에 복제 할 수 있습니다).
내가 알았던 것은 우리 시스템 전체에서 정상적인 과정 에서 사용자 테이블을 지속적으로 쿼리하여 사용자를 검사하는 것이 삭제되지 않는 반면, 내가 생각하는 것은 우리가 전혀 그렇게 할 필요가 없다는 것입니다. ! [Clarification1 : ‘constantly querying’으로, ‘… FROM users WHERE isdeleted = “0”AND …’와 같은 쿼리가 있음을 의미했습니다. 예를 들어, 우리는 그래서 쿼리에서 특정 날짜에 모든 회의에 등록 된 모든 사용자를 가져 오기 위해해야 할 수도 있습니다, 우리는 또한 에 isDeleted = “0”사용자의이 -이 내 지점을 명확하게 않습니다]?
(1) continue keeping deleted users in the 'main' users table
(2) keep deleted users in a separate table (mostly required for historical
book-keeping)
두 가지 접근법의 장단점은 무엇입니까?
답변
(1) 삭제 된 사용자를 ‘기본’사용자 테이블에 계속 유지
- 장점 : 모든 경우에 더 간단한 쿼리
- 단점 : 사용자 수가 많은 경우 시간이 지남에 따라 성능이 저하 될 수 있습니다
(2) 삭제 된 사용자를 별도의 테이블에 보관하십시오 (대부분의 역사적 부기 유지에 필요)
예를 들어 트리거를 사용하여 삭제 된 사용자를 내역 테이블로 자동 이동시킬 수 있습니다.
- 장점 : 활성 사용자 테이블에 대한 간단한 유지 관리, 안정적인 성능
- 단점 : 히스토리 테이블에 대해 다른 쿼리가 필요합니다. 그러나 대부분의 응용 프로그램에 관심이 없어야하기 때문에이 부정적인 영향은 아마도 제한적입니다.
답변
같은 테이블을 사용하는 것이 좋습니다. 주된 이유는 데이터 무결성입니다. 사용자에 따라 관계가있는 많은 테이블이있을 가능성이 높습니다. 사용자가 삭제되면 해당 레코드를 분리 된 상태로 두지 않습니다.
레코드가 고아가되면 제약 조건을 적용하기가 어려워지고 기록 정보를 찾기가 더 어려워집니다. 이전 레코드를 모두 복구하려는 경우 사용자가 사용한 이메일을 제공 할 때 고려해야 할 다른 동작입니다. 이것은 소프트 삭제를 사용하여 자동으로 작동합니다. 코딩하는 한, 예를 들어 현재 c # linq 응용 프로그램에서 where deleted = 0 절이 모든 쿼리 끝에 자동으로 추가됩니다.
답변
“내가 주목 한 것은 우리 시스템 전체에서 정상적인 상황에서 사용자 테이블을 지속적으로 쿼리하여 사용자를 검사하지 않는다는 것입니다.”
이것은 나에게 나쁜 디자인 냄새를 준다. 그런 종류의 논리를 숨겨야합니다. 예를 들어, 다음과 같은 작업을 수행하는 대신 “시스템 간”사용 UserService
방법 isValidUser(userId)
을 제공 해야합니다 .
“사용자 레코드를 받고 사용자가 삭제 된 것으로 플래그가 지정되어 있는지 확인하십시오”.
삭제 된 사용자를 저장하는 방법은 비즈니스 로직에 영향을 미치지 않아야합니다.
이러한 종류의 캡슐화를 사용하면 위의 주장이 더 이상 지속성의 접근에 영향을 미치지 않아야합니다. 그런 다음 지속성 자체와 관련된 장단점에 더 집중할 수 있습니다.
고려해야 할 사항은 다음과 같습니다.
- 삭제 된 레코드를 실제로 얼마나 오래 제거해야합니까?
- 삭제 된 레코드의 비율은 얼마입니까?
- 실제로 테이블에서 제거하면 참조 무결성 (예 : 사용자가 다른 테이블에서 참조 됨)에 문제가 있습니까?
- 사용자를 다시여시겠습니까?
일반적으로 나는 결합 된 방법을 취할 것입니다 :
- ac를 다시 열거 나 최근에 닫힌 ac를 확인하는 등의 기능 요구 사항을 유지하기 위해 레코드를 삭제 된 것으로 플래그를 지정하십시오.
- 미리 정의 된 기간이 지나면 삭제 된 레코드를 보관 테이블로 이동합니다 (부기 목적으로).
- 사전 정의 된 아카이브 기간 후에 제거하십시오.
답변
이 질문에 올바르게 대답하려면 먼저 다음을 결정해야합니다. 이 시스템 / 응용 프로그램에서 “삭제”는 무엇을 의미합니까?
이 질문에 대답하려면 또 다른 질문에 대답해야합니다. 왜 레코드가 삭제됩니까?
사용자가 데이터를 삭제해야하는 여러 가지 이유가 있습니다. 일반적으로 삭제가 필요한 이유 는 정확히 하나의 이유 (테이블 당)입니다. 몇 가지 예는 다음과 같습니다.
- 디스크 공간을 회수합니다.
- 보존 / 개인 정보 보호 정책에 따라 강제 삭제가 필요합니다.
- 부정확하거나 부정확하게 잘못된 데이터는 복구하는 것보다 삭제 및 재생이 더 쉽습니다.
- 대부분 의 행은, 예를 들어, 로그 테이블 X 레코드 제한 / 일 삭제됩니다.
하드 삭제에 대한 몇 가지 매우 나쁜 이유 도 있습니다 (나중에 이러한 이유에 대한 자세한 내용 참조).
- 사소한 오류를 수정합니다. 이것은 보통 개발자 게으름과 적대적인 UI를 강조합니다.
- 거래 (예 : 청구 된 적이없는 송장)를 “공제”합니다.
- 당신이 할 수 있기 때문에 .
왜 그렇게 큰 문제입니까? 좋은 올레의 문제는 무엇입니까 DELETE
?
- 돈에 먼 거리에있는 시스템에서도 하드 삭제는 아카이브 / 비석 테이블로 이동하더라도 모든 종류의 회계 기대치를 위반합니다. 이를 처리하는 올바른 방법은 소급 이벤트 입니다.
- 아카이브 테이블은 라이브 스키마와 다른 경향이 있습니다. 새로 추가 된 열이나 계단식을 잊어 버린 경우 해당 데이터를 영구적으로 잃어버린 것입니다.
- 하드 삭제는 특히 계단식 작업에서 매우 비싼 작업 일 수 있습니다 . 많은 사람들이 인식하지 않는 하나 개 이상의 수준을 계단식 (또는 경우에 따라 어떤 계단식, DBMS에 따라) 레코드 수준의 작업을 대신 설정 작업에서 발생합니다.
- 반복적이고 빈번한 하드 삭제는 인덱스 조각화 프로세스 속도를 높입니다.
따라서 소프트 삭제가 더 낫습니다. 아니 정말:
- 캐스케이드 설정은 매우 어려워집니다. 거의 항상 클라이언트에 고아 행으로 표시됩니다.
- 하나의 삭제 만 추적 할 수 있습니다. 행이 여러 번 삭제 및 삭제 취소 된 경우 어떻게합니까?
- 분할, 뷰 및 / 또는 필터링 된 인덱스를 사용하면 다소 완화 될 수 있지만 읽기 성능이 저하됩니다.
- 앞에서 언급했듯이 일부 시나리오 / 관할 구역에서는 실제로 불법 일 수 있습니다.
진실은이 두 가지 접근 방식이 모두 틀렸다는 것입니다. 삭제가 잘못되었습니다. 실제로이 질문을하는 경우 거래 대신 현재 상태를 모델링하고 있음 을 의미 합니다. 이것은 데이터베이스 랜드에서 나쁜 습관입니다.
Udi Dahan은 이에 대해 Do n’t Delete-Just Do n’t 에서 썼습니다 . 이 항상 어떤 종류의 작업, 거래, 활동 , 또는 (내 추천 용어) 이벤트 실제로 “삭제”를 나타냅니다. 나중에 성능을 위해 “현재 상태”테이블로 비정규 화하려는 경우에도 괜찮지 만 이전이 아닌 트랜잭션 모델을 정리 한 후에 수행 하십시오.
이 경우 “사용자”가 있습니다. 사용자는 본질적으로 고객입니다. 고객은 귀하와 비즈니스 관계를 맺고 있습니다. 그 관계는 계정을 취소했기 때문에 단순히 허풍으로 사라지지 않습니다. 실제로 일어나고있는 일은 :
- 고객이 계정을 만듭니다
- 고객이 계정을 취소 함
- 고객이 계정을 갱신
- 고객이 계정을 취소 함
- …
모든 경우에 동일한 고객 이며 가능하면 동일한 계정입니다 (즉, 각 계정 갱신은 새로운 서비스 계약입니다). 그렇다면 왜 행을 삭제합니까? 이것은 모델링하기가 매우 쉽습니다.
+-----------+ +-------------+ +-----------------+
| Account | --->* | Agreement | --->* | AgreementStatus |
+-----------+ +-------------+ +----------------+
| Id | | Id | | AgreementId |
| Name | | AccountId | | EffectiveDate |
| Email | | ... | | StatusCode |
+-----------+ +-------------+ +-----------------+
그게 다야. 그것이 전부입니다. 아무것도 삭제할 필요가 없습니다. 위의 방법은 유연성이 뛰어나지 만 약간 단순화 할 수있는 상당히 일반적인 디자인입니다. “계약”레벨이 필요하지 않고 “계정”이 “계정 상태”테이블로 이동하도록 결정할 수 있습니다.
응용 프로그램에서 자주 필요의 목록을 얻을 경우 활성 계약 / 그것 (약간) 까다로운 쿼리의 다음 계정,하지만의는 무엇을보기위한 것입니다 :
CREATE VIEW ActiveAgreements AS
SELECT agg.Id, agg.AccountId, acc.Name, acc.Email, s.EffectiveDate, ...
FROM AgreementStatus s
INNER JOIN Agreement agg
ON agg.Id = s.AgreementId
INNER JOIN Account acc
ON acc.Id = agg.AccountId
WHERE s.StatusCode = 'ACTIVE'
AND NOT EXISTS
(
SELECT 1
FROM AgreementStatus so
WHERE so.AgreementId = s.AgreementId
AND so.EffectiveDate > s.EffectiveDate
)
그리고 당신은 끝났습니다. 이제 소프트 삭제의 모든 이점이 있지만 단점은 없습니다.
- 모든 레코드가 항상 표시되므로 고아 레코드는 문제가 아닙니다. 필요할 때마다 다른보기에서 선택하면됩니다.
- “삭제”는 일반적으로 매우 저렴한 작업입니다. 이벤트 테이블에 한 행만 삽입하면됩니다.
- 어떤 역사를 잃을 기회가 결코 지금까지 , 아니 당신이 나사 방식 심하게 문제는.
- 당신은 여전히 계정을 하드 삭제할 수 있다면 당신은 (개인 정보 보호를 위해 예) 필요, 및 삭제가 깨끗하게되지 일어날 응용 프로그램 / 데이터베이스의 다른 부분을 방해하는 지식 편안하게.
해결해야 할 유일한 문제는 성능 문제입니다. 많은 경우 실제로 클러스터 된 인덱스로 인해 실제로 문제가 아닌 것으로 밝혀졌습니다. AgreementStatus (AgreementId, EffectiveDate)
I / O 탐색은 거의 없습니다. 그러나 문제가 발생하면 트리거, 인덱스 / 구체화 된 뷰, 응용 프로그램 수준 이벤트 등을 사용하여이를 해결할 수있는 방법이 있습니다.
너무 일찍 성능에 대해 걱정하지 마십시오. 디자인을 올바르게하는 것이 더 중요합니다.이 경우 “올바른”은 데이터베이스를 트랜잭션 시스템 으로 사용하는 방식으로 데이터베이스를 사용한다는 의미 입니다.
답변
현재 모든 테이블에 소프트 삭제에 대한 삭제 플래그가있는 시스템을 사용하고 있습니다. 그것은 모든 존재의 허무입니다. 사용자가 한 테이블에서 레코드를 “삭제”할 수있을 때 관계 무결성을 완전히 훼손하지만 해당 테이블로 다시 돌아가는 FK 하위 레코드는 연속 삭제되지 않습니다. 시간이 지나면 실제로 쓰레기 데이터를 만듭니다.
따라서 별도의 기록 테이블을 권장합니다.
답변
테이블을 두 개로 나누는 것은 상상할 수있는 가장 작은 것입니다.
다음은 내가 권장하는 두 가지 매우 간단한 단계입니다.
- ‘users’테이블의 이름을 ‘allusers’로 바꾸십시오.
- ‘users’라는보기를 ‘delete * false’인 모든 사용자에서 ‘select *’로 작성하십시오.
추신 답변이 몇 달 동안 지연되어 죄송합니다.
답변
누군가 같은 이메일 주소로 돌아 왔을 때 삭제 된 계정을 복구했다면 모든 사용자를 같은 테이블에 보관하는 것이 좋을 것입니다. 이렇게하면 계정 복구 프로세스가 간단 해집니다.
그러나 새 계정을 만들면 삭제 된 계정을 별도의 테이블로 이동하는 것이 더 간단 할 것입니다. 라이브 시스템에는이 정보가 필요하지 않으므로 노출하지 마십시오. 당신이 말했듯이 더 큰 데이터 세트에서 쿼리를 더 간단하고 가능하게 만듭니다. 간단한 코드도 유지 관리가 더 쉽습니다.