기본 키로 MySQL int vs varchar (InnoDB 스토리지 엔진?


13

웹 응용 프로그램 (프로젝트 관리 시스템)을 구축 중이며 성능과 관련하여 궁금합니다.

내부에 Issues 테이블이 있으며 다양한 다른 테이블에 연결되는 12 개의 외래 키가 있습니다. 그중 8 개는 웹 응용 프로그램에서 레코드가 의미를 갖기 위해 다른 테이블에서 제목 필드를 가져 오기 위해 참여해야하지만 8 개의 조인을 수행하는 것을 의미합니다. 각 조인에 대해 1 개의 필드.

이제 영구 증가 이유로 샤딩이 GUID를 사용해야하는 경우가 아니라면 자동 증분 기본 키를 사용하라는 지시를 받았지만 varchar (최대 길이 32) 성능을 현명하게 사용하는 것은 얼마나 나쁩니 까? 나는이 테이블의 대부분이 아마도 많은 레코드를 가지고 있지 않을 것임을 의미합니다 (대부분 20 미만이어야 함). 또한 제목을 기본 키로 사용하면 95 %의 시간을 조인 할 필요가 없으므로 SQL의 95 %에서 성능 저하가 발생할 수 있습니다 (제 생각에). 내가 생각할 수있는 유일한 단점은 내가 가지고있는 디스크 공간 사용량이 높다는 것입니다 (그러나 하루는 실제로 큰 문제입니다).

열거 형 대신 많은 것들에 대해 조회 테이블을 사용하는 이유는 응용 프로그램 자체를 통해 최종 사용자가 구성 할 수있는 모든 값이 필요하기 때문입니다.

많은 레코드를 제외하고 varchar를 테이블의 기본 키로 사용하는 단점은 무엇입니까?

업데이트-일부 테스트

그래서 나는 이것에 대한 기본 테스트를하기로 결정했습니다. 나는 100000 개의 레코드를 가지고 있으며 이것이 기본 쿼리입니다.

기본 VARCHAR FK 쿼리

SELECT i.id, i.key, i.title, i.reporterUserUsername, i.assignedUserUsername, i.projectTitle, 
i.ProjectComponentTitle, i.affectedProjectVersionTitle, i.originalFixedProjectVersionTitle, 
i.fixedProjectVersionTitle, i.durationEstimate, i.storyPoints, i.dueDate, 
i.issueSecurityLevelId, i.creatorUserUsername, i.createdTimestamp, 
i.updatedTimestamp, i.issueTypeId, i.issueStatusId
FROM ProjectManagement.Issues i

기본 INT FK 쿼리

SELECT i.id, i.key, i.title, ru.username as reporterUserUsername, 
au.username as assignedUserUsername, p.title as projectTitle, 
pc.title as ProjectComponentTitle, pva.title as affectedProjectVersionTitle, 
pvo.title as originalFixedProjectVersionTitle, pvf.title as fixedProjectVersionTitle, 
i.durationEstimate, i.storyPoints, i.dueDate, isl.title as issueSecurityLevelId, 
cu.username as creatorUserUsername, i.createdTimestamp, i.updatedTimestamp, 
it.title as issueTypeId, is.title as issueStatusId
FROM ProjectManagement2.Issues i
INNER JOIN ProjectManagement2.IssueTypes `it` ON it.id = i.issueTypeId
INNER JOIN ProjectManagement2.IssueStatuses `is` ON is.id = i.issueStatusId
INNER JOIN ProjectManagement2.Users `ru` ON ru.id = i.reporterUserId
INNER JOIN ProjectManagement2.Users `au` ON au.id = i.assignedUserId
INNER JOIN ProjectManagement2.Users `cu` ON cu.id = i.creatorUserId
INNER JOIN ProjectManagement2.Projects `p` ON p.id = i.projectId
INNER JOIN ProjectManagement2.`ProjectComponents` `pc` ON pc.id = i.projectComponentId
INNER JOIN ProjectManagement2.ProjectVersions `pva` ON pva.id = i.affectedProjectVersionId
INNER JOIN ProjectManagement2.ProjectVersions `pvo` ON pvo.id = i.originalFixedProjectVersionId
INNER JOIN ProjectManagement2.ProjectVersions `pvf` ON pvf.id = i.fixedProjectVersionId
INNER JOIN ProjectManagement2.IssueSecurityLevels isl ON isl.id = i.issueSecurityLevelId

또한 다음 추가 사항으로 이러한 쿼리를 실행했습니다.

  • 특정 항목을 선택하십시오 (여기서 i.key = 43298).
  • i.id로 그룹화
  • 정렬 기준 (int FK의 경우 제목, varchar FK의 경우 i.issueTypeId)
  • 한도 (50000, 100)
  • 그룹화 및 제한
  • 그룹화, 주문 및 제한

이에 대한 결과는 다음과 같습니다.

쿼리 유형 : VARCHAR FK TIME / INT FK TIME


기본 쿼리 : ~ 4ms / ~ 52ms

특정 항목 선택 : ~ 140ms / ~ 250ms

i.id로 그룹화 : ~ 4ms / ~ 2.8sec

주문 : ~ 231ms / ~ 2sec

제한 : ~ 67ms / ~ 343ms

그룹화 및 제한 : ~ 504ms / ~ 2sec

그룹화, 주문 및 제한 : ~ 504ms /~2.3sec

이제 하나 또는 다른 (또는 둘 다) 더 빠르게 만들 수있는 구성을 모르지만 VARCHAR FK가 데이터 쿼리에서 더 빨리 보이는 것처럼 보입니다 (때로는 훨씬 더 빠릅니다).

속도 향상이 추가 데이터 / 인덱스 크기의 가치가 있는지 여부를 선택해야한다고 생각합니다.


당신의 테스트는 무언가를 나타냅니다. 기본 MySQL 설정이 실제로 InnoDB에 최적화되어 있지 않기 때문에 다양한 InnoDB 설정 (버퍼 풀 등)으로 테스트 할 것입니다.
ypercubeᵀᴹ

삽입 / 업데이트 / 삭제 성능도 인덱스 크기에 영향을받을 수 있으므로 테스트해야합니다. 모든 InnoDB 테이블의 하나의 클러스터 된 키는 일반적으로 PK이며이 (PK) 열은 다른 모든 인덱스에도 포함됩니다. 이것은 아마도 InnoDB의 큰 PK와 테이블의 많은 인덱스의 큰 단점 일 것입니다 (그러나 32 바이트는 중간 크기가 아니기 때문에 문제가되지 않을 수 있습니다).
ypercubeᵀᴹ

테이블이 100K보다 커질 것으로 예상되는 경우 (실제로는 크지 않은) 더 큰 테이블 (예 : 10 ~ 100M 행 이상)로 테스트해야합니다.
ypercubeᵀᴹ

@ypercube 따라서 데이터를 2 백만으로 늘리고 varchar 외래 키가 꽤 안정적으로 유지되는 경우 int FK에 대한 select 문이 기하 급수적으로 느려집니다. varchar는 select query (이 특정 테이블 및 기타 몇 가지 테이블에서 중요 할 것임)의 이득을 얻기 위해 디스크 / 메모리 요구 사항의 가격이 가치가 있다고 생각합니다.
ryanzec

결론을 내리기 전에 db (특히 InnoDB) 설정도 확인하십시오. 작은 참조 테이블로, 나는 기하 급수적으로 증가 기대하지 않을 것이다
ypercubeᵀᴹ

답변:


9

기본 키에 대해 다음 규칙을 따릅니다.

a) 비즈니스 의미가 없어야합니다-개발중인 응용 프로그램과 완전히 독립적이어야하므로 숫자 자동 생성 정수로 이동하십시오. 그러나 고유 한 추가 열이 필요한 경우이를 지원하기 위해 고유 한 인덱스를 작성하십시오.

b) 조인에서 수행해야 함-기본 키의 길이가 길어질수록 varchars 대 정수에 대한 조인은 약 2 배에서 3 배 느리므로 키를 정수로 사용하려고합니다. 모든 컴퓨터 시스템이 이진이기 때문에 문자열이 이진으로 변경된 다음 다른 시스템과 비교할 때 매우 느립니다.

c) 가능한 가장 작은 데이터 유형을 사용하십시오-테이블에 52 미국 주와 같은 열이 거의 없을 것으로 예상되면 가능한 가장 작은 유형을 사용하여 2 자리 코드에 CHAR (2)를 사용할 수 있지만 여전히 tinyint를 사용합니다. (128) 열의 경우 최대 20 억까지 올라갈 수있는 큰 정수

또한 프로젝트 이름이 변경되는 경우 (예 : 드물지 않은 경우) 기본 키에서 다른 테이블로 변경 사항을 계단식으로 연결하는 데 어려움이 있습니다.

기본 키의 순차 자동 증분 정수를 수행하고 데이터베이스 시스템이 향후 변경을 지원하는 기본 제공 효율성을 얻습니다.


1
문자열은 이진으로 변경되지 않습니다. 그들은 처음부터 바이너리로 저장됩니다. 그것들은 어떻게 저장됩니까? 대소 문자를 구분하지 않는 비교를 허용하는 작업을 생각하고 있습니까?
모든 거래의 존

6

테스트에서 varchar 대 int 키 성능 차이를 비교하지 않고 여러 조인 비용을 비교합니다. 1 개의 테이블을 쿼리하는 것이 많은 테이블을 조인하는 것보다 빠르다는 것은 놀라운 일이 아닙니다.
varchar 기본 키의 한 가지 단점은 atxdba가 지적한 대로 인덱스 크기를 늘리는 것입니다. 룩업 테이블에 PK (아마도 가능하지는 않지만)를 제외한 다른 인덱스가 없더라도 룩업을 참조하는 각 테이블에는이 열에 대한 인덱스가 있습니다.
자연스러운 기본 키의 또 다른 나쁜 점은 값이 변경되어 많은 계단식 업데이트가 발생한다는 것입니다. 모든 RDMS, 예를 들어 Oracle이 아니라on update cascade. 일반적으로 기본 키 값을 변경하는 것은 매우 나쁜 습관입니다. 자연스러운 기본 키가 항상 악하다고 말하고 싶지 않습니다. 조회 값이 작고 변경되지 않으면 허용 될 수 있다고 생각합니다.

고려해야 할 옵션 중 하나는 구체화 된 뷰를 구현하는 것입니다. MySQL은 직접 지원하지 않지만 기본 테이블에서 트리거를 사용하여 원하는 기능을 수행 할 수 있습니다. 따라서 표시해야 할 모든 것이있는 하나의 테이블이 있습니다. 또한 성능이 만족 스러우면 현재 존재하지 않는 문제로 어려움을 겪지 마십시오.


3

가장 큰 단점은 PK의 반복입니다. 디스크 공간 사용량이 증가했음을 지적했지만 증가 된 인덱스 크기를 확인하는 것이 더 큰 관심사입니다. innodb는 클러스터형 인덱스이므로 모든 보조 인덱스는 내부적으로 일치하는 레코드를 찾는 데 사용되는 PK의 사본을 내부적으로 저장합니다.

당신은 테이블이 "작은"것으로 예상된다고 말합니다 (실제로 20 행은 매우 작습니다). innodb_buffer_pool_size를 다음과 같이 설정하기에 충분한 RAM이있는 경우

select sum(data_length+index_length) from information_schema.tables where engine='innodb';

그런 다음 그렇게하면 아마 꽤 앉아있을 것입니다. 일반적으로 다른 mysql 오버 헤드 및 디스크 캐시를 위해 총 시스템 메모리의 30 %-40 % 이상을 남겨두고 싶습니다. 그리고 그것이 전용 DB 서버라고 가정합니다. 시스템에서 다른 작업을 수행하는 경우 요구 사항도 고려해야합니다.


1

@atxdba answer 외에도 디스크 공간에 숫자를 사용하는 것이 더 나은 이유를 설명했습니다 .2 점을 추가하고 싶습니다.

  1. 이슈 테이블이 VARCHAR FK 기반이고 20 개의 작은 VARCHAR (32) FK가 있다고 가정하면 레코드가 20x32 바이트 길이가 될 수 있지만 다른 테이블은 룩업 테이블이므로 INT FK는 TINYINT FK가 될 수 있습니다. 20 개 필드의 경우 20 바이트 레코드. 나는 수백 개의 레코드가 많이 변하지 않을 것이라는 것을 알고 있지만 수백만에 도달하면 공간 절약에 감사 할 것입니다.

  2. 속도 문제의 경우 커버링 인덱스 사용을 고려할 것입니다.이 쿼리의 경우 인덱스를 커버하기 위해 검색 테이블에서 많은 양의 데이터를 검색하지 않고 VARCHAR FK / W / COVERING과 함께 제공된 테스트를 다시 한 번 수행합니다. 색인 및 일반 INT FK.

그것이 도움이되기를 바랍니다.

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