MySQL에 MyISAM 또는 InnoDB에 해시 인덱스가없는 이유는 무엇입니까?


35

평등에서만 선택하는 응용 프로그램이 있으며 btree 인덱스 위에 해시 인덱스를 사용해야한다고 생각합니다. 당황스럽게도 MyISAM 또는 InnoDB에서는 해시 인덱스가 지원되지 않습니다. 무슨 일이야?


2
Mysql은 또한 함수 기반 인덱스, 비트 맵 인덱스 등을 지원하지 않습니다. 단지 mysql이기 때문에 ;-)

1
방금 해시 인덱스가 너무 기본적인 것이라고 생각했습니다.

1
@ 알렉스 : 그 이유는 "게으름"과 "관료주의"이지만 내 대답을 기다립니다)))


고성능 MySQL Book에서 답변 끝까지 멋진 HASH 알고리즘을 추가했습니다.
RolandoMySQLDBA

답변:


16

많은 데이터베이스는 해시 기반 인덱스 를 전혀 지원하지 않습니다 .

해시 테이블을 효율적으로 사용하려면 존재할 가능성이있는 행 수를 알아야합니다. 그렇지 않으면 기본 해시 테이블이 너무 크거나 (많은 빈 항목, 공간 낭비 및 잠재적으로 디스크 IO) 또는 너무 작은 의미가됩니다. 간접적으로 사용되는 경우가 종종 있습니다 (여러 수준의 간접적 가능성, 또는 해시 구현이 단일 수준 인 경우에는 더 나빠질 수 있습니다).이 시점에서 트리 기반보다 더 효율적이지 않은 지점에서 선형 검색을 수행 할 수 있습니다. 어쨌든 색인.

따라서 일반적으로 유용하기 때문에 (일반적으로 대안보다 낫습니다.) 데이터가 커지거나 줄어듦에 따라 인덱스를 가끔씩 다시 작성해야하므로 상당한 간헐적 인 오버 헤드가 발생할 수 있습니다. 일반적으로 메모리 기반 테이블의 경우 재 구축이 꽤 빠르기 때문에 (데이터가 항상 RAM에 있고 어떤 경우에도 방대하지 않기 때문에) 디스크에 큰 인덱스를 다시 작성하는 것이 좋습니다. 매우 무거운 작업 (그리고 IIRC mySQL은 라이브 인덱스 재 구축을 지원하지 않으므로 작업 중에 테이블 잠금을 유지합니다).

따라서 해시 인덱스는 일반적으로 성능이 좋을수록 메모리 테이블에 사용되지만 디스크 기반 테이블은 보너스가 아닌 성능을 저하시킬 수 있으므로 테이블을 지원하지 않습니다. 물론 디스크 기반 테이블에 대해 해시 인덱스를 사용할 수있게하는 것은 아무것도 없습니다. 의심 할 여지없이 일부 데이터베이스 이 기능을 지원하지만, 관리자는 추가 기능을 고려하지 않으므로 ISAM / InnoDB 테이블에서는 구현되지 않을 것입니다. 작성하고 유지 관리하는 추가 코드는 몇 가지 상황에서 큰 차이를 만드는 이점이 없습니다). 아마도 당신이 강하게 동의하지 않는다면 그들과 이야기하고 그 기능을 구현하기에 좋은 사례를 만들 수 있습니다.

큰 문자열을 인덱싱하는 경우 값의 해시를 저장하고 실제 값을 갖는 인덱싱하여 자체 의사 해시 인덱스를 구현하면 작동 할 수 있지만 큰 문자열 (확실히있는 곳)에 대해서만 더 효율적입니다 해시 값을 계산 하고이 값으로 트리 인덱스를 검색하는 것이 항상 더 빠를 가능성이 높으며 비교를 위해 더 큰 값을 사용하여 트리 인덱스를 검색하는 것만으로 사용되는 추가 스토리지가 중요하지 않습니다.) 구현하기 전에 성능 분석을 수행하십시오. 이것은 생산에 있습니다.


전체 테이블을 잠그지 않고 다시 해싱 (재 빌드)을 나란히 수행 할 수있는 방법이 있습니까?
Pacerier

@Pacerier : MySQL에 대해 아는 것이 아닙니다 (마지막으로 사용한 이후 기능을 추가 할 수 있었지만 설명서를 확인하십시오). DBMS가 온라인 인덱스 생성 / 재 구축을 지원하는 경우에도 기본 옵션은 아닙니다. 잠긴 내용은 다를 수 있습니다. 일부는 테이블에 대한 쓰기 잠금을 유지하고 일부 트랜잭션은 읽기 전용 인 경우 지연되지 않으며 일부 DMBS는 전체 테이블 잠금을 수행합니다. 온라인 재 구축 이 필요한 경우 사용할 DBMS를 선택하기 전에 각 DBMS 설명서를 확인하십시오.
David Spillett

일반적으로 재 구축은 데이터 길이가 두 배인 경우에만 필요합니다. 그들은 매분마다 데이터 길이가 두 배로 늘어날 까봐 걱정해야합니까? (일반적으로 데이터베이스가 커질 때 데이터베이스가 커질 때 매우 드물게 발생합니다)
SOFe

6

관련 메모에서 PostgreSQL 문서의 인덱스 유형에 대한 토론이 흥미로울 수 있습니다. 최신 버전의 문서에는 더 이상 존재하지 않지만 (다음 최적화로 인해), 테이크 아웃은 MySQL과 비슷할 수 있습니다 (해시 인덱스가 힙 테이블에만 사용되는 이유).

http://www.postgresql.org/docs/8.1/static/indexes-types.html

참고 : 테스트에 따르면 PostgreSQL의 해시 인덱스가 B- 트리 인덱스보다 더 나은 성능을 발휘하지 못했으며 해시 인덱스의 인덱스 크기와 빌드 시간이 훨씬 나쁩니다. 또한 해시 인덱스 작업은 현재 WAL로 기록되지 않으므로 데이터베이스 충돌 후 REINDEX를 사용하여 해시 인덱스를 다시 작성해야 할 수도 있습니다. 이러한 이유로 해시 인덱스 사용은 현재 권장되지 않습니다. 마찬가지로, R- 트리 인덱스는 GiST 인덱스의 동등한 작업과 비교할 때 성능상의 이점이없는 것 같습니다. 해시 인덱스와 마찬가지로 WAL 로그가 아니며 데이터베이스 충돌 후 다시 인덱싱해야 할 수도 있습니다. 해시 인덱스의 문제는 결국 수정 될 수 있지만 향후 릴리스에서는 R- 트리 인덱스 유형이 폐기 될 수 있습니다. R- 트리 인덱스를 사용하는 응용 프로그램을 GiST 인덱스로 마이그레이션하는 것이 좋습니다.

다시 말하지만, PostgreSQL 전용 (구식 버전)이지만 "자연스러운"인덱스 유형이 반드시 최적의 성능을 제공하지는 않는다는 것을 암시해야합니다.


5

흥미로운 점이 있습니다.

MySQL 5.0 Certification Study Guide , 페이지 433, 섹션 29.5.1에 ​​따르면

MEMORY 엔진은 기본 색인 알고리즘으로 HASH를 사용합니다.

웃음을 위해 MySQL 5.5.12에서 HASH를 사용하여 기본 키로 InnoDB 테이블과 MyISAM 테이블을 만들려고했습니다.

mysql> use test
Database changed
mysql> create table rolando (num int not null, primary key (num) using hash);
Query OK, 0 rows affected (0.11 sec)

mysql> show create table rolando\G
*************************** 1. row ***************************
       Table: rolando
Create Table: CREATE TABLE `rolando` (
  `num` int(11) NOT NULL,
  PRIMARY KEY (`num`) USING HASH
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

mysql> create table rolando2 (num int not null, primary key (num) using hash) engine=MyISAM;
Query OK, 0 rows affected (0.05 sec)

mysql> show create table rolando2\G
*************************** 1. row ***************************
       Table: rolando2
Create Table: CREATE TABLE `rolando2` (
  `num` int(11) NOT NULL,
  PRIMARY KEY (`num`) USING HASH
) ENGINE=MyISAM DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

MySQL은 불평하지 않았다.

최신 정보

나쁜 소식 !!! SHOW INDEXES FROM을 사용했습니다. 인덱스는 BTREE입니다.

INDEX 구문은 MySQL의 페이지 생성 에만 MEMORY 및 NDB 스토리지 엔진은 HASH 인덱스를 수용 할 수 있다고.

mysql> show indexes from rolando;
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table   | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| rolando |          0 | PRIMARY  |            1 | num         | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)

mysql> show indexes from rolando2;
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table    | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| rolando2 |          0 | PRIMARY  |            1 | num         | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)

mysql> create table rolando3 (num int not null, primary key (num)) ENGINE=MEMORY;
Query OK, 0 rows affected (0.03 sec)

mysql> show create table rolando3\G
*************************** 1. row ***************************
       Table: rolando3
Create Table: CREATE TABLE `rolando3` (
  `num` int(11) NOT NULL,
  PRIMARY KEY (`num`)
) ENGINE=MEMORY DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

mysql> show indexes from rolando3;
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table    | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| rolando3 |          0 | PRIMARY  |            1 | num         | NULL      |           0 |     NULL | NULL   |      | HASH       |         |               |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)

어떤 사람들 은 해시 알고리즘을 에뮬레이트하기 위해 " 고성능 MySQL : 최적화, 백업, 복제 등 "책 102-105 페이지 의 아이디어따르는 것이 좋습니다 .

Page 105 내가 좋아하는이 빠르고 더러운 알고리즘은 다음과 같습니다.

SELECT CONV(RIGHT(MD5('whatever value you want'),16),16,10) AS HASH64;

임의의 테이블에서이 열을 작성하고이 값을 색인화하십시오.

시도 해봐 !!!


5
프로덕션에서 의사 해시 인덱스 기술을 사용하기 전에 성능 분석을 수행하십시오. 큰 문자열의 경우는 큰 차이를 만들 수 있지만 어쨌든 결국 트리 인덱스를 탐색 결국, 그리고 당신은 추가 이렇게 작은 해시 값을 계산 값에 대한 해시 일치 발견 된 것과 올바른 행을 찾아야 할 비교해야 그것들을 저장하는 것은 가치가 없습니다. 이것은 실제로 해시 인덱스가 아니며 단순히 트리를 걷는 작업을 줄입니다 (예 : x00 바이트 문자열 대신 8 바이트 INT 비교).
David Spillett

@David Spillett 이것에, 나는 당신과 완전히 동의해야합니다. 11 장 "고성능을위한 인덱싱 전략"의 같은 책에서 다른 인덱싱 계층도 제안되어 있습니다. 내 대답에 대한 추가 향상으로,이 책은 실제로 행과 BTree 인덱스를 동일한 구조로 저장하는 클러스터형 인덱스를 사용한다고 언급합니다. 이것은 당신이 언급 한 작업 감소 속도를 높일 수 있습니다. 불행히도, 방금 언급 한 후프는 다소 피할 수 없습니다. 그럼에도 불구하고 귀하의 의견에 대한 +1입니다. 사실, 당신의 대답에 대해서도 +1입니다.
RolandoMySQLDBA

@RolandoMySQLDBA "custom hashing"에 대해 좀 더 자세히 설명해 주시겠습니까? 마지막 단락은 그다지 실마리가 아닙니다.
Pacerier

2

BTree는 단일 행 조회에서 해시보다 그리 느리지 않습니다. BTree는 매우 효율적인 범위 쿼리를 제공하기 때문에 BTree 이외의 다른 것을 방해하는 이유는 무엇입니까?

MySQL은 BTree 블록 캐싱을 매우 잘 수행하므로 BTree 기반 쿼리는 I / O를 수행 할 필요가 거의 없습니다. 이는 쿼리에서 가장 큰 시간 소비자입니다.

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