문제

MySQL에서 Int vs. Varchar를 사용하는 것 사이에 측정 가능한 성능 차이가 있습니까? Varchar를 참조 목록 (미국 주, 국가 코드를 생각)의 주요 키로 사용하고 동료는 모든 테이블의 기본 키로 int auto_increment에 버리지 않을 것입니다.

내 주장은 자세히 설명합니다 여기, Int와 Varchar 간의 성능 차이는 무시할 수 있다는 것입니다. 모든 INT 외국 키 참조는 참조를 이해하기 위해 조인이 필요하기 때문에 Varchar 키는 정보를 직접 제시합니다.

그렇다면이 특정 사용 사례와 그와 관련된 성능 문제에 대한 경험이 있습니까?

도움이 되었습니까?

해결책

당신은 당신이 자연 키 대신 a 대리 키. 이 혜택이 신청서에서 중요한지 평가할 수 있습니다.

즉, 응용 프로그램의 쿼리를 대량의 데이터로 작업하거나 매우 자주 실행되기 때문에 빠른 속도가 가장 중요한 쿼리를 측정 할 수 있습니다. 이러한 쿼리가 조인을 제거함으로써 혜택을 받고 Varchar 기본 키를 사용하여 어려움을 겪지 않으면 그렇게하십시오.

데이터베이스의 모든 테이블에 대해 전략을 사용하지 마십시오. 경우에 따라 자연 키가 더 좋을 수도 있지만 다른 경우에는 대리 키가 더 좋습니다.

다른 사람들은 실제로 자연 키가 바뀌지 않거나 복제를하지 않는 것이 실제로 드물다는 좋은 지적을합니다. 따라서 대리 키는 일반적으로 가치가 있습니다.

다른 팁

성능에 관한 것이 아닙니다. 좋은 기본 키를 만드는 것에 관한 것입니다. 시간이 지남에 따라 독특하고 변하지 않습니다. 국가 코드와 같은 엔티티는 시간이 지남에 따라 변하지 않으며 기본 키의 좋은 후보라고 생각할 수 있습니다. 그러나 쓰라린 경험은 그것이 거의 그렇지 않습니다.

int auto_increment는 "시간이 지남에 따라 고유하고 변하지 않는"조건을 충족합니다. 따라서 선호도.

길이에 따라 다릅니다. varchar가 20자가되고 int가 4 인 경우 int를 사용하면 인덱스가 디스크의 색인 공간 페이지 당 5 배나 많은 노드를 갖습니다. 지수는 많은 물리적 및/또는 논리적 읽기만큼 5 분의 1이 필요합니다 ..

따라서 성능이 문제 인 경우 기회가 주어지면 항상 테이블 과이 테이블의 행을 참조하는 외래 키에는 항상 필수적인 키 (대리라고 함)를 사용하십시오 ...

동시에, 데이터 일관성을 보장하기 위해, 중요한 모든 테이블은 또한 중복 행을 삽입 할 수 없도록 의미있는 비 언어 대체 키 (또는 고유 인덱스)를 가지십시오 (의미있는 테이블 속성을 기반으로 한 중복).

주 조회와 같은 특정 용도에 대해 테이블의 크기가 너무 작기 때문에 실제로는 중요하지 않습니다. 일반적으로 수천 행 미만의 테이블에 대한 지수의 성능에 영향을 미치지 않습니다. ..

절대적으로하지.

나는 Int, Varchar, Char 사이의 몇 가지 ... 몇 가지 ... 성능 점검을했습니다.

기본 키 (고유 및 클러스터링)를 가진 1 천만 개의 레코드 테이블은 내가 사용한 세 가지 중 어느 쪽이든 동일한 속도와 성능 (및 하위 트리 비용)을 가졌습니다.

즉, 응용 프로그램에 가장 적합한 것을 사용하십시오. 성능에 대해 걱정하지 마십시오.

나는이 온라인에 대한 벤치 마크가 없기 때문에 약간 짜증이 났으므로 스스로 테스트를 실행했습니다.

정기적으로 기본적으로 수행하지 않으므로 의도하지 않게 결과에 영향을 줄 수있는 요인에 대한 설정 및 단계를 확인하고 의견에 우려 사항을 게시하십시오.

설정은 다음과 같습니다.

  • Intel® Core ™ i7-7500U CPU @ 2.70GHz × 4
  • 15.6 GIB RAM은 테스트 중에 약 8GB가 무료임을 확인했습니다.
  • 148.6 GB SSD 드라이브, 충분한 여유 공간.
  • 우분투 16.04 64 비트
  • Linux 용 MySQL VER 14.14 Distrib 5.7.20 (x86_64)

테이블 :

create table jan_int (data1 varchar(255), data2 int(10), myindex tinyint(4)) ENGINE=InnoDB;
create table jan_int_index (data1 varchar(255), data2 int(10), myindex tinyint(4), INDEX (myindex)) ENGINE=InnoDB;
create table jan_char (data1 varchar(255), data2 int(10), myindex char(6)) ENGINE=InnoDB;
create table jan_char_index (data1 varchar(255), data2 int(10), myindex char(6), INDEX (myindex)) ENGINE=InnoDB;
create table jan_varchar (data1 varchar(255), data2 int(10), myindex varchar(63)) ENGINE=InnoDB;
create table jan_varchar_index (data1 varchar(255), data2 int(10), myindex varchar(63), INDEX (myindex)) ENGINE=InnoDB;

그런 다음 각 테이블에서 1 천만 행을 Essence와 같은 PHP 스크립트로 채웠습니다.

$pdo = get_pdo();

$keys = [ 'alabam', 'massac', 'newyor', 'newham', 'delawa', 'califo', 'nevada', 'texas_', 'florid', 'ohio__' ];

for ($k = 0; $k < 10; $k++) {
    for ($j = 0; $j < 1000; $j++) {
        $val = '';
        for ($i = 0; $i < 1000; $i++) {
            $val .= '("' . generate_random_string() . '", ' . rand (0, 10000) . ', "' . ($keys[rand(0, 9)]) . '"),';
        }
        $val = rtrim($val, ',');
        $pdo->query('INSERT INTO jan_char VALUES ' . $val);
    }
    echo "\n" . ($k + 1) . ' millon(s) rows inserted.';
}

을 위한 int 테이블, 비트 ($keys[rand(0, 9)]) 그냥 대체되었습니다 rand(0, 9), 그리고 varchar 테이블, 나는 6 자로 자르거나 확장하지 않고 미국 상태 이름을 사용했습니다. generate_random_string() 10 자 임의 문자열을 생성합니다.

그런 다음 MySQL에서 달렸습니다.

  • SET SESSION query_cache_type=0;
  • 을 위한 jan_int 테이블:
    • SELECT count(*) FROM jan_int WHERE myindex = 5;
    • SELECT BENCHMARK(1000000000, (SELECT count(*) FROM jan_int WHERE myindex = 5));
  • 위와 동일한 다른 테이블의 경우 myindex = 'califo' ~을 위한 char 테이블과 myindex = 'california' ~을 위한 varchar 테이블.

시간의 시간 BENCHMARK 각 테이블의 쿼리 :

  • Jan_int : 21.30 초
  • Jan_int_index : 18.79 초
  • JAN_CHAR : 21.70 초
  • JAN_CHAR_INDEX : 18.85 초
  • JAN_VARCHAR : 21.76 SEC
  • JAN_VARCHAR_INDEX : 18.86 SEC

테이블 및 색인 크기와 관련하여 다음은 다음과 같습니다. show table status from janperformancetest; (몇 개의 열이 표시되지 않음) :

|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Name              | Engine | Version | Row_format | Rows    | Avg_row_length | Data_length | Max_data_length | Index_length | Data_free | Auto_increment | Collation              |
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| jan_int           | InnoDB |      10 | Dynamic    | 9739094 |             43 |   422510592 |               0 |            0 |   4194304 |           NULL | utf8mb4_unicode_520_ci |  
| jan_int_index     | InnoDB |      10 | Dynamic    | 9740329 |             43 |   420413440 |               0 |    132857856 |   7340032 |           NULL | utf8mb4_unicode_520_ci |   
| jan_char          | InnoDB |      10 | Dynamic    | 9726613 |             51 |   500170752 |               0 |            0 |   5242880 |           NULL | utf8mb4_unicode_520_ci |  
| jan_char_index    | InnoDB |      10 | Dynamic    | 9719059 |             52 |   513802240 |               0 |    202342400 |   5242880 |           NULL | utf8mb4_unicode_520_ci |  
| jan_varchar       | InnoDB |      10 | Dynamic    | 9722049 |             53 |   521142272 |               0 |            0 |   7340032 |           NULL | utf8mb4_unicode_520_ci |   
| jan_varchar_index | InnoDB |      10 | Dynamic    | 9738381 |             49 |   486539264 |               0 |    202375168 |   7340032 |           NULL | utf8mb4_unicode_520_ci | 
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

내 결론은이 특정 사용 사례에 성능 차이가 없다는 것입니다.

짧은 코드의 경우 차이가 없을 것입니다. 이 코드를 보유한 테이블이 매우 작을 가능성이 높고 (최대 2 천 행) 자주 변경되지 않기 때문에 (새로운 미국 주를 마지막으로 추가 한 것은 언제입니까?) 특히 그렇습니다.

키가 더 넓은 더 큰 테이블의 경우 위험 할 수 있습니다. 예를 들어 사용자 테이블에서 이메일 주소/사용자 이름을 사용하는 방법을 생각해보십시오. 수백만 명의 사용자가 있고 해당 사용자 중 일부는 긴 이름이나 이메일 주소를 가지고있을 때 어떻게됩니다. 이제이 키를 사용 하여이 테이블에 가입해야 할 때마다 훨씬 비싸게됩니다.

기본 키의 경우 물리적으로 행을 고유하게 만드는 것은 기본 키로 결정되어야합니다.

외국 키로 언급하는 경우, 자동 증분 정수를 대리로 사용하는 것은 두 가지 주요 이유에 대한 좋은 아이디어입니다.
- 첫째, 보통 조인기에서 발생하는 오버 헤드가 적습니다.
- 둘째, 고유 한 Varchar가 포함 된 테이블을 업데이트 해야하는 경우 업데이트는 모든 어린이 테이블로 캐스케이드하고 모든 어린이 테이블과 인덱스를 업데이트 해야하는 반면 Int 대리를 사용하면 업데이트하면됩니다. 마스터 테이블과 색인입니다.

대리인을 사용하는 Drawaback은 대리의 의미를 변경할 수 있다는 것입니다.

ex.
id value
1 A
2 B
3 C

Update 3 to D
id value
1 A
2 B
3 D

Update 2 to C
id value
1 A
2 C
3 D

Update 3 to B
id value
1 A
2 C
3 B

그것은 모두 당신이 당신의 구조에서 실제로 걱정해야 할 것과 가장 의미하는 바에 달려 있습니다.

대리자가있는 일반적인 경우 AUTO_INCREMENT 아프다 :

일반적인 스키마 패턴은 a입니다 다수의 매핑:

CREATE TABLE map (
    id ... AUTO_INCREMENT,
    foo_id ...,
    bar_id ...,
    PRIMARY KEY(id),
    UNIQUE(foo_id, bar_id),
    INDEX(bar_id) );

이 패턴의 성능은 특히 innodb를 사용할 때 훨씬 좋습니다.

CREATE TABLE map (
    # No surrogate
    foo_id ...,
    bar_id ...,
    PRIMARY KEY(foo_id, bar_id),
    INDEX      (bar_id, foo_id) );

왜요?

  • Innodb Secondary 키에는 추가 조회가 필요합니다. 쌍을 PK로 옮기면 한 방향으로는 피합니다.
  • 보조 지수는 "커버링"이므로 추가 조회가 필요하지 않습니다.
  • 이 테이블은 제거하기 때문에 더 작습니다 id 그리고 하나의 색인.

다른 케이스 (국가):

country_id INT ...
-- versus
country_code CHAR(2) CHARACTER SET ascii

너무 자주 초보자는 Country_code를 4 바이트로 정규화합니다. INT '천연'2 바이트를 사용하는 대신 거의 2 바이트 문자열을 사용하는 대신. 더 빠르고, 작고, 더 적은 조인, 더 읽기 쉬운.

Hautelook에서는 자연 키를 사용하기 위해 많은 테이블을 변경했습니다. 우리는 실제 성능 증가를 경험했습니다. 당신이 언급했듯이, 우리의 많은 쿼리는 이제 더 적은 결합을 사용하여 쿼리를 더 성능으로 만듭니다. 합리적이면 복합 기본 키를 사용합니다. 즉, 일부 테이블은 대리 키가 있으면 작업하기가 더 쉽습니다.

또한 사람들이 데이터베이스에 인터페이스를 작성하도록하는 경우 대리 키가 도움이 될 수 있습니다. 제 3자는 대리 키가 매우 드문 상황에서만 변경 될 것이라는 사실에 의존 할 수 있습니다.

문제는 MySQL에 관한 것이므로 큰 차이가 있다고 말합니다. 그것이 Oracle에 관한 것이라면 (숫자를 문자열로 저장합니다 - 예, 처음에는 믿을 수 없었습니다) 큰 차이는 없습니다.

테이블의 스토리지는 문제가되지 않지만 인덱스를 업데이트하고 참조합니다. 기본 키를 기반으로 레코드를 찾는 것과 관련된 쿼리는 빈번합니다. 자주 발생하기 때문에 가능한 한 빨리 발생하기를 원합니다.

문제는 CPU가 4 바이트와 8 바이트 정수를 자연스럽게 다루는 것입니다. 규소. 두 개의 정수를 비교하는 것이 정말 빠릅니다. 하나 또는 두 개의 클럭 사이클에서 발생합니다.

이제 문자열을보십시오. 많은 캐릭터로 구성되어 있습니다 (요즘 문자 당 하나 이상의 바이트). 우선 순위를 위해 두 줄을 비교하는 것은 한두 주기로 수행 할 수 없습니다. 대신에 문자열의 문자는 차이가 발견 될 때까지 반복해야합니다. 일부 데이터베이스에서는 더 빠르게 만들기위한 트릭이 있다고 확신하지만, INT 비교가 자연스럽게 수행되고 CPU에 의해 실리콘에서 빠르게 번개되기 때문에 여기서는 관련이 없습니다.

내 일반적인 규칙 - 모든 기본 키는 특히 ORM (Hibernate, Datanucleus 등)을 사용하여 OO 앱에서 자동화 된 INT 여야합니다. DB를 빠르게 해결하려면 앱의 응답성에 중요합니다.

나는 같은 딜레마에 직면했다. 나는 3 개의 사실 테이블, 도로 사고, 사고의 차량 및 사고로 DW (Constellation Schema)를 만들었습니다. 데이터에는 1979 년부터 2012 년까지 영국에서 기록 된 모든 사고와 60 차원 테이블이 포함됩니다. 모두 약 2 천만 개의 기록.

사실 테이블 관계 :

+----------+          +---------+
| Accident |>--------<| Vehicle |
+-----v----+ 1      * +----v----+
     1|                    |1
      |    +----------+    |
      +---<| Casualty |>---+
         * +----------+ *

RDMS : MySQL 5.6

기본적으로 사고 지수는 15 자리의 바르 차 (숫자와 문자)입니다. 사고 지수가 변하지 않으면 대리 키를 갖지 않으려 고 노력했습니다. i7 (8 코어) 컴퓨터에서 DW는 치수에 따라 1,200 만 건의로드 레코드 후에 쿼리하기에는 너무 느 렸습니다. 많은 재 작업 및 큰 대리 키를 추가 한 후 평균 20% 속도 성능 향상을 얻었습니다. 그러나 성능이 낮지 만 유효한 시도. MySQL 튜닝 및 클러스터링에서 일하고 있습니다.

성능의 영향에 대해서는 확실하지 않지만 적어도 개발 중에는 타협이 가능한 것으로 보인다. 자동 증가 된 정수 "대리"키와 의도 된 독특한 "자연적인"키를 모두 포함시키는 것입니다. 이를 통해 성능을 평가할 수있는 기회와 자연 키의 변화성을 포함하여 가능한 다른 문제가 있습니다.

평소와 같이 담요 답변이 없습니다. '때에 따라 다르지!' 그리고 나는 얼굴이 좋지 않습니다. 원래 질문에 대한 나의 이해는 주소/연락 테이블과 같은 잠재적으로 거대한 테이블의 외국 키 인 Country (Integer ID 또는 Char/Varchar Code)와 같은 작은 테이블의 키에 대한 것이 었습니다.

DB에서 데이터를 다시 원할 때 두 가지 시나리오가 있습니다. 첫 번째는 상태 및 국가 코드 또는 이름으로 모든 연락처를 나열하려는 목록/검색 종류의 쿼리입니다 (ID는 도움이되지 않으므로 조회가 필요합니다). 다른 하나는 기본 키의 GET 시나리오로, 국가의 이름이 표시되어야하는 단일 연락처 레코드를 보여줍니다.

후자의 경우, 단일 레코드 또는 몇 가지 레코드 및 키 읽기에 대한 테이블을 모으기 때문에 FK가 무엇을 기반으로하는지는 중요하지 않습니다. 전자 (검색 또는 목록) 시나리오는 우리의 선택에 의해 영향을받을 수 있습니다. 국가 (적어도 인식 가능한 코드와 검색 자체에 국가 코드가 포함되어 있음)를 보여 주어야하므로 대리 키를 통해 다른 테이블에 가입 할 필요가 없기 때문에 잠재적으로 잠재적으로 할 수 있습니다 (실제로 테스트하지 않았기 때문에 여기서 신중합니다. 이것은 매우 가능성이 있지만) 성능을 향상시킵니다. 그것이 확실히 검색에 도움이된다는 사실에도 불구하고.

코드는 크기가 작기 때문에 일반적으로 국가와 상태에 대해 3 숯을 넘지 않기 때문에이 시나리오에서 자연 키를 외래 키로 사용해도 괜찮을 수 있습니다.

키가 더 긴 바르 르 차 값과 아마도 더 큰 테이블에 의존하는 다른 시나리오; 대리 키는 아마도 이점이있을 것입니다.

성능의 범위 (상자 정의에서)를 고려하여 분명히 차이가 있습니다.

1- 대리인 int를 사용하면 Toupper (), Tolower (), toupperInvarient () 또는 TolowerInvarient ()를 코드 또는 쿼리에서 사용할 필요가 없기 때문에 응용 프로그램이 빠릅니다.이 4 가지 기능에는 성능 벤치 마크가 다릅니다. 이에 대한 Microsoft 성능 규칙을 참조하십시오. (응용 프로그램의 성능)

2- 대리 INT 사용은 시간이 지남에 따라 키를 변경하지 않음을 보장합니다. 국가 코드조차도 변경 될 수 있습니다. Wikipedia는 시간이 지남에 따라 ISO 코드가 어떻게 변경되는지 참조하십시오. 하위 트리의 기본 키를 변경하는 데 많은 시간이 걸립니다. (데이터 유지 보수 성능)

3- PK/FK가 int가 아닌 경우 NHibernate와 같은 ORM 솔루션에 문제가있는 것 같습니다. (개발자 성능)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top