고유 색인을 추가 할 수없는 경우 중복을 피할 수있는 방법은 무엇입니까


10

동시성 문제가 발생했습니다.

사용자가 2 o 3 트랜잭션을 전송하여 DB에 복제해서는 안되는 일부 데이터를 유지하는 일반적인 문제입니다. 중복 레코드의 경우 오류를 반환해야합니다.

이 문제는 해시를 저장하는 열에 인덱스 (고유)를 추가 할 수있을 때 쉽습니다.

그러나이 경우 거대한 테이블 (아마도 수백만 개의 레코드)이 있으며 테이블을 수정할 수 없습니다.

실제로 중복되지 않아야하지만 고유 인덱스는 설정되지 않은 데이터 해시를 저장하는 열이 있습니다.

플러시 직전에 존재하는지 확인하기 위해 Java 코드를 사용하려고하지만 여전히 중복이 발생합니다.

이에 대한 가능한 해결책은 다음과 같습니다.

  • 삽입하려는 해시가 이미 테이블에 있는지 확인하는 트리거를 작성하십시오.
  • 이 테이블의 고유 인덱스를 저장할 다른 테이블을 작성하고 기본 테이블에 외래 키를 추가하십시오.
  • 태아 자세에 앉아 울다

해시 충돌이나 검사 버그로 인해 해시 검사가 실패합니까?
candied_orange

4
질문이 없습니다. 따라서 수백만 개의 레코드로 모든 거대한 테이블에 대해 한 번 인덱싱하는 대신 추가 할 다음 백만 개의 레코드 각각을 읽는 것을 선호합니다. 기존 수백만은 두 배를 찾고 있습니까? 또는 일부 정보를 복제하고 조인을 추가하여 확인하십시오.
Christophe

문제는이 변경을 수행하기 위해 서비스를 위해 2 시간 이상 다운 될 수없는 일부 요구 사항을 충족시키기 위해 서비스를위한 많은 공간과 긴 다운 타임이 필요하다는 경고를 받았습니다. 이 테이블에서 유지 관리를 수행하는 것이 가장 좋은 방법이라는 것을 알고 있지만 지금은 할 수없는 일이므로 해결 방법이 필요합니다.
rafuru

4
나는 그것을 얻지 못한다-왜 인덱스를 "모방"하기 위해 트리거를 추가하거나 다른 테이블을 추가하는 것이 기존 테이블에 인덱스를 추가하는 것보다 다운 타임이 덜 걸리는가?
Doc Brown

2
@rafuru : 누가 고유 인덱스를 만들어야한다고 말했습니까? 고유하지 않은 표준 인덱스는 아마도 동일한 해시 값을 가진 모든 행을 빠르게 찾는 데 필요한 모든 것입니다.
Doc Brown

답변:


3

해결하기 쉬운 몇 가지 가능한 시나리오와 그렇지 않은 위험한 시나리오가 있습니다.

값을 입력 한 사용자의 경우 INSERT가 문제점을 감지하기 전에 얼마 후에 동일한 SELECT를 간단한 SELECT를 입력하십시오. 이것은 한 사용자가 값을 제출하고 나중에 다른 사용자가 동일한 값을 제출하는 경우에 효과적입니다.

사용자가 코드를 한 번만 호출하여 중복 된 값 목록 (예 : {ABC, DEF, ABC})을 제출하면 응용 프로그램은 중복을 감지하고 필터링하여 오류를 발생시킬 수 있습니다. 또한 삽입 전에 DB에 고유 값이 포함되어 있지 않은지 확인해야합니다.

까다로운 시나리오는 한 사용자의 쓰기가 다른 사용자의 쓰기와 동시에 DBMS 내에 있고 동일한 값을 쓰는 경우입니다. 그렇다면 당신은 그들 사이에 경쟁 조건이 있습니다. DBMS는 선점 형 멀티 태스킹 시스템 (대부분 사용중인 시스템을 말하지는 않음)이므로 모든 작업이 실행 중 일시 중지 될 수 있습니다. 즉, user1의 작업은 기존 행이 없는지 확인한 다음 user2의 작업은 기존 행이 없는지 확인한 다음 user1의 작업은 해당 행을 삽입 한 다음 user2의 작업은 해당 행을 삽입 할 수 있습니다. 각 시점에서 작업은 개별적으로 만족하며 올바른 일을하고 있습니다. 그러나 전 세계적으로 오류가 발생합니다.

일반적으로 DBMS는 해당 값을 고정하여이를 처리합니다. 이 문제에서는 새 행을 작성하므로 아직 잠글 항목이 없습니다. 대답은 범위 잠금입니다. 제안했듯이 현재 존재 여부에 관계없이 다양한 값을 잠급니다. 일단 잠금이 해제되면 잠금이 해제 될 때까지 다른 작업에서 해당 범위에 액세스 할 수 없습니다. 범위 잠금을 얻으려면 SERIALIZABLE의 격리 수준을 지정해야합니다 . 작업을 확인한 후 다른 작업이 연속으로 몰래 발생하는 현상을 팬텀 레코드라고 합니다.

전체 응용 프로그램에서 격리 수준을 직렬화 가능으로 설정하면 의미가 있습니다. 처리량 줄어 듭니다. 과거에 충분히 효과가 있었던 다른 경쟁 조건은 이제 오류를 표시하기 시작할 수 있습니다. 중복 유도 코드를 실행하고 나머지 응용 프로그램은 그대로 두는 연결에서 설정하는 것이 좋습니다.

코드 기반 대안은 이전보다는 쓰기 후에 확인 하는 것입니다. INSERT를 수행 한 다음 해시 값이있는 행 수를 계산하십시오. 중복이 있으면 조치를 롤백하십시오. 이것은 약간의 잘못된 결과를 초래할 수 있습니다. 작업 1이 작업 2를 쓴 다음 작업 1이 중복을 확인하고 찾습니다. 처음이더라도 롤백됩니다. 마찬가지로 두 작업 모두 중복 및 롤백을 모두 감지 할 수 있습니다. 그러나 최소한 재시도 메커니즘을 사용하고 새로운 복제본을 사용하지 않는 메시지가 표시됩니다. 롤백은 예외를 사용하여 프로그램 흐름을 제어하는 ​​것과 매우 유사합니다. 참고 아니라 그 모든트랜잭션의 작업은 중복 유도 쓰기뿐만 아니라 롤백됩니다. 그리고 동시성을 줄일 수있는 명시적인 트랜잭션이 있어야합니다. 해시에 대한 색인이 없으면 중복 검사가 엄청나게 느려집니다. 당신이 할 경우뿐만 아니라 독특한 하나를 만들 수 있습니다!

당신이 언급 한 것처럼 실제 솔루션은 고유 색인입니다. 이것이 유지 관리 기간에 맞는 것처럼 보입니다 (물론 시스템을 가장 잘 알고 있음). 해시가 8 바이트라고 가정하십시오. 약 1GB 인 1 억 행의 경우 경험에 따르면 합리적인 수준의 하드웨어가 이러한 많은 행을 1 ~ 2 분 안에 처리 할 수 ​​있습니다. 중복 검사 및 제거가 여기에 추가되지만 미리 스크립팅 할 수 있습니다. 그러나 이것은 제쳐두고 있습니다.


2

실제로 중복되지 않아야하지만 고유 인덱스는 설정되지 않은 데이터 해시를 저장하는 열이 있습니다.

해시 충돌 확인은 좋은 첫 번째 단계이지만 동일한 프로그램이 다시 시작되면 동일한 데이터가 동일한 데이터에서 동일한 해시를 생성한다고 보장 할 수 없습니다 . 많은 "빠른"해시 함수는 프로그램 시작시 시드 된 내장 prng을 사용합니다. 이 응용 프로그램에서와 마찬가지로 해시가 항상 동일해야하는 경우 암호화 해시를 사용하십시오. 우수하거나 안전한 암호화 해시가 필요하지 않습니다.

두 번째 단계는 실제로 데이터의 엔트로피를 줄이기 때문에 최상의 해시 함수로 인해 충돌이 발생하기 때문에 실제로 데이터의 동등성을 확인하는 것입니다.

그래서:

1 단계 : 암호화 해시에서 충돌이 발생하는지 확인

2 단계 : 해시가 일치하면 실제 데이터가 동일한 지 확인


이것이 어떻게 질문에 대답하는지 알 수 없습니다. 사용 가능한 해시 열이 결정적 해시 함수로 채워져 있다고 가정 해 봅시다 (그렇지 않으면이를 사용하려는 시도는 의미가 없습니다). 내 이해에 문제는 데이터베이스의 해시 열에 인덱스가 없으므로 응답의 첫 번째 단계조차도 충돌이 있는지 확인하는 것입니다. 테이블의 새 레코드마다 테이블 전체를 스캔해야합니다. 수백만 건의 레코드가 너무 느려질 것입니다.
Doc Brown

인덱스를 만들지 않고 할 수있는 최선의 방법은 질문입니다. 해시 스캔은 적어도 하나의 열만 확인하면된다는 것을 의미합니다. 그렇지 않으면 확인해야하는 많은 열을 확인하는 것보다 훨씬 빠릅니다.
터크 사라 마

인덱스를 만들 수없는 경우에도 (이 경우에는 가능할 수 있음) OPs 원래 제안에서 " 이 테이블에 대한 고유 인덱스를 저장하기 위해 다른 테이블을 만들고 기본 테이블에 외래 키를 추가 "하는 것이 좋습니다. 더 의미가 있습니다.
Doc Brown

결정 론적 해시와 암호화 해시는 두 가지 직교 개념이 아닌가? 암호화 해시는 결정 론적이지 않을 수 있으며, 반대로 결정론 해시는 암호화 강도가 좋지 않을 수 있습니다.
Newtopian

그것들은 같은 것이 아니지만 직교하지도 않습니다. 암호화 해시는 결정 론적 해시의 하위 집합이지만, 어떤 이유로 든 뒤집을 수 있기를 원하지 않는 한 암호화되지 않은 결정 론적 해시를 만드는 귀찮은 사람은 없습니다.
Turksarama

2

고유 한 기본 키로 새 테이블을 만듭니다.

클라이언트 측에서 각 레코드에 대한 GUID 생성을 시작하여 간단한 재전송을 감지 할 수 있습니다.

새 테이블에 새 레코드를 넣으면 최소한 새로운 데이터가 들어올 수 있습니다.

새 테이블 "CheckedAgainstOldData"에 열이 있습니다.

현재 느린 해시 확인을 수행하는 모든 작업을 수행하는 백엔드 작업을 통해 이전 데이터에서 중복을 찾고 플래그를 적절하게 설정 하고이 시점에서 중복을 거부하고 알림을 클라이언트에 다시 보냅니다.

한편 데이터를 이전 테이블에서 새 테이블로 이동하여 해시 확인으로 중복을 확인하고 GUID를 생성하는 또 다른 백엔드 작업이 있습니다.

이 작업을 며칠 동안 (필요한 경우) 실행하면서 다운 타임없이 데이터를 전송할 수 있습니다.

전송이 완료되면 느린 "CheckedAgainstOldData"프로세스를 끌 수 있습니다. 모든 데이터를 단일 테이블로 전송하십시오.

솔직히 말해서 문제가 설명한대로 나쁘고 소프트웨어가 오래 되었다면 수천 개의 복제본을 갖게 될 것입니다.


1

"사용자"로부터 오는 데이터가 키보드에 앉아있는 누군가를 의미하고 두 명의 사용자가 같은 데이터를 동시에 입력하면 발생하는 것으로 가정합니다. 트리거 시작시 임의 지연을 발생시키는 기능을 추가하십시오. 테이블에 새로운 레코드를 작성하는 데 걸리는 시간을 최소한으로 줄이십시오. 그렇게하면 속임수 요청을받을 때 첫 번째 요청을 수행해야하며 존재 트리거가 올바른 결과를 반동해야합니다. (설명 : 각 통화마다 ALOHA 프로토콜 과 동일한 보안 주체를 따라 고유 한 임의 지연 시간이 있어야 합니다. )

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