복잡한 기준으로 인덱싱 된 읽기 최소화


12

Firebird 2.5 작업 티켓 데이터베이스를 최적화하고 있습니다. 그들은 다음과 같이 선언 된 테이블에 저장됩니다.

CREATE TABLE TICKETS (
  TICKET_ID id PRIMARY KEY,
  JOB_ID id,
  ACTION_ID id,
  STATUS str256 DEFAULT 'Pending'
);

일반적으로 처리되지 않고 Pending상태 가있는 첫 번째 티켓을 찾고 싶습니다 .

내 처리 루프는 다음과 같습니다.

  1. 첫 번째 티켓 검색 Pending
  2. 티켓으로 작업하십시오.
  3. 티켓 상태 업데이트 => Complete
  4. 반복.

너무 멋진 것은 없습니다. 이 루프가 실행되는 동안 데이터베이스를보고 있으면 각 반복마다 인덱스 된 읽기 수가 증가하는 것을 볼 수 있습니다. 내가 말할 수있는 성능은 크게 저하되지는 않지만 테스트중인 컴퓨터는 매우 빠릅니다. 그러나 일부 사용자로부터 시간이 지남에 따라 성능이 저하되었다는보고를 받았습니다.

에 대한 색인이 Status있지만 여전히 Ticket_Id각 반복마다 열을 스캔하는 것처럼 보입니다 . 내가 뭔가를 간과하고있는 것 같지만 확실하지 않습니다. 이와 같은 수치의 색인 된 읽기 수가 증가합니까, 아니면 색인이 어떤 방식으로 잘못 작동합니까?

-의견 편집-

Firebird에서는 다음과 같이 행 검색을 제한합니다.

Select First 1
  Job_ID, Ticket_Id
From
  Tickets
Where
  Status = 'Pending'

"first"라고 말하면 where라는 제한된 레코드 세트를 요구합니다 Status = 'Pending'.


"첫 번째 티켓을 어디에서"보류 중 "으로 검색 " 에서 "first" 란 무엇입니까 ?
ypercubeᵀᴹ

"최초의"작은 의미하는 경우 ticket_id, 당신은 probbaly 온 인덱스 필요(status, ticket_id)
ypercubeᵀᴹ

그리고 다른 쿼리 / 문이 아니라이 절차로 인해 성능 저하가 얼마나 확실합니까?
ypercubeᵀᴹ

@ypercube-아니요, 성능 저하가 어디인지 확실하지 않습니다. 그렇기 때문에 제 질문은 "이 문제에 관심을 가져야합니까, 아니면 정상적인 색인 동작입니까?"였습니다. 데이터베이스를 모니터링하는 동안 발견 한 것으로 예상치 못한 것으로 간주했습니다. 인덱스 열에 대해 where 절을 제공 할 때 이전 행을 계속 스캔하지 않을 것입니다. FWIW : ticket_id상태를 색인화하는 것보다 실제로 성능이 저하 되도록 색인을 수정했습니다 .
gddc

id(데이터 유형)를 사용하면 정의 된 도메인?
a_horse_with_no_name

답변:


1

"완료"상태에있는 항목 수가 증가하여 시간이 지남에 따라 성능이 저하됩니다. 이것에 대해 잠시 생각해보십시오. 상태가 "Complete"인 적은 수의 행이있을 수 있으므로 테스트 할 때 성능이 저하되지 않습니다. 그러나 프로덕션 환경에서는 "완료"상태의 수백만 행이있을 수 있으며이 수는 시간이 지남에 따라 증가합니다. 이것은 본질적으로 Status에 대한 색인을 시간이 지남에 따라 유용하게 만듭니다. 따라서 데이터베이스는 Status가 거의 항상 'Complete'값을 가지므로 인덱스를 사용하는 대신 테이블을 스캔하기 만한다고 결정합니다.

SQL Server (및 다른 RDBMS?)에서는 필터링 된 인덱스를 사용하여 해결할 수 있습니다. SQL Server에서는 인덱스 정의의 끝에 WHERE 조건을 추가하여 "이 인덱스를 상태 <> '완료' '인 레코드에만 적용하십시오"라고 말하십시오. 그런 다음이 술어를 사용하는 모든 쿼리는 '완료'로 설정되지 않은 소량의 레코드에서 인덱스를 사용합니다. 그러나 http://www.firebirdsql.org/refdocs/langrefupd25-ddl-index.html 문서에 따르면 Firebird가 필터링 된 인덱스를 지원하는 것처럼 보이지 않습니다.

해결 방법은 ArchiveTickets 테이블에 'Complete'레코드를 넣는 것입니다. Tickets 테이블과 정확히 동일한 정의 (자동 생성 된 ID는 없지만)를 가진 테이블을 만들고 'Complete'레코드를 ArchiveTickets 테이블로 푸시하여 테이블 사이에 행을 유지하십시오. 티켓 표의 색인은 훨씬 적은 수의 레코드를 넘어 훨씬 더 높은 성능을 발휘합니다. 이는 'Complete'티켓을 참조하는 모든 보고서 등을 변경하여 Archive 테이블을 가리 키거나 Tickets와 ArchiveTicket 모두에서 UNION을 수행해야 함을 의미합니다. 이렇게하면 속도가 빠를뿐만 아니라 ArchiveTickets 테이블에 대해 특정 쿼리를 생성하여 다른 쿼리에서 더 나은 성능을 발휘할 수 있다는 이점도 있습니다.

프로덕션이 수천 개의 행으로 들어가려면이 점에 대해 걱정해야합니다. 시간이지나면서 성능이 저하되고 사용자 환경에 부정적인 영향을 미칩니다.


0

성능이 영향을 받는지 여부는 데이터 볼륨 및 시스템 용량의 기능입니다. 최신 하드웨어의 용량을 고려할 때 설명하는 디자인으로는 처리 할 수없는 티켓 판매량을 상상하기 어렵습니다. 그러나 정확성을 위해 권장하는 변경 사항이 있으며 보조 혜택으로 성능을 향상시킬 수 있습니다.

귀하의 첫 번째 진행 중이다 쿼리는 비 결정적이다. 먼저 어떤 순서에 따라? SQL 테이블에는 본질적인 순서가 없습니다. First 1해킹은 당신에게주고 일부 임의의 첫 번째입니다. 결정 론적으로 처리하기 위해 보류중인 작업을 Job_ID 순서로 처리하지 않는 이유는 무엇입니까?

두 개의 인덱스 {Job_ID}와 {Status, Job_ID}가있는 경우이 쿼리는 예측 가능하고 효율적인 한 행을 반환합니다.

Select Job_ID, Ticket_Id
From   Tickets
Where Job_ID = ( 
  select min(Job_ID) from Tickets 
  where Status = 'Pending'
);

나는 Firebird 사용자가 아니기 때문에 쿼리 계획을 확인 해야 하지만 하위 쿼리는 두 번째 인덱스 만 참조하고 첫 번째 인덱스는 값을 생성하므로 효율적 이어야 합니다. (예를 들어, 다른 효율성 트릭이있을 수 있습니다. 실제 테이블을 B + 트리로 구성하거나 숨겨진 row_id에 액세스 할 수 있습니다.)

정확성을 높이기위한 또 다른 변경 사항은 Status제한된 단일 바이트를 만들고 응용 프로그램에서 "Pending"문자열을 제공하도록하는 것입니다. 그것은 잘못된 Status값 으로부터 보호 하고 아마 거래에서 색인을 더 작게 만들 것입니다. 다음과 같은 것 :

CREATE TABLE TICKETS (
  TICKET_ID id PRIMARY KEY,
  JOB_ID id,
  ACTION_ID id,
  STATUS char(1) not NULL 
     DEFAULT 'P'
     CHECK( STATUS in ('P', 'C', 'X') ) -- whatever the domain is
);

물론 뷰 (또는 파생 열)를 사용하여 상태에 대한 표준 문자열을 제공 할 수 있습니다.

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