MySQL 인덱싱 VarChar


10

blogentries더 나은 성능을 위해 데이터베이스 를 색인하려고 하지만 문제를 발견했습니다.

구조는 다음과 같습니다.

CREATE TABLE IF NOT EXISTS `blogentries` (
  `id_id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `title_id` varchar(100) COLLATE latin1_german2_ci NOT NULL,
  `entry_id` varchar(5000) COLLATE latin1_german2_ci NOT NULL,
  `date_id` int(11) NOT NULL,
  PRIMARY KEY (`id_id`)
)
ENGINE=MyISAM
DEFAULT CHARSET=latin1
COLLATE=latin1_german2_ci
AUTO_INCREMENT=271;

다음과 같은 쿼리는 인덱스를 올바르게 사용합니다.

EXPLAIN SELECT id_id,title_id FROM blogentries ORDER by id_id DESC
+ ---- + ------------- + --------------- + ------- + ---------- ------- + --------- + --------- + ------ + ------ + ---------- ----- +
| 아이디 | select_type | 테이블 | 타입 | possible_keys | 열쇠 | key_len | 심판 | 행 | 추가 |
+ ---- + ------------- + --------------- + ------- + ---------- ------- + --------- + --------- + ------ + ------ + ---------- ----- +
| 1 | 단순 | 블로그 색인 | NULL | 기본 | 114 | NULL | 126 | 색인 사용 |
+ ---- + ------------- + --------------- + ------- + ---------- ------- + --------- + --------- + ------ + ------ + ---------- ----- +

나는를 추가 할 때, entry_idSELECT질의 그것은 filesort를 사용

EXPLAIN SELECT id_id,title_id,entry_id FROM blogentries ORDER by id_id DESC
+ ---- + ------------- + --------------- + ------ + ----------- ------ + ------ + --------- + ------ + ------ + -------------- ---- +
| 아이디 | select_type | 테이블 | 타입 | possible_keys | 열쇠 | key_len | 심판 | 행 | 추가 |
+ ---- + ------------- + --------------- + ------ + ----------- ------ + ------ + --------- + ------ + ------ + -------------- ---- +
| 1 | 단순 | 블로그 모두 | NULL | NULL | NULL | NULL | 126 | filesort 사용하기 |
+ ---- + ------------- + --------------- + ------ + ----------- ------ + ------ + --------- + ------ + ------ + -------------- ---- +

왜 이런 일이 일어나고 어떻게 피할 수 있을지 궁금했습니다. 때문입니까 VarChar, 다른 것으로 변경되어야합니까?

높은 값 으로 실행되면서 모든 쿼리에서 인덱스를 사용하도록 노력 Handler_read_rnd하고 Handler_read_rnd_next있습니다.

다른 정보가 필요하면 게시 할 수도 있습니다.


filesort는 디스크에서 정렬을 수행하고 있음을 의미합니다.
커밋

WHERE 1=1두 번째 쿼리에 추가 하십시오.
커밋

어떤 버전의 MySQL입니까? 정렬 버퍼 크기 ( SELECT @@sort_buffer_size)는 얼마입니까?

@njk filesort 쿼리의 'ORDER BY'부분의 결과이다

1
@TashPemhiwa 반드시 그런 것은 아닙니다. 첫 번째 진술을보십시오.
커밋

답변:


6

당신이 가지고 있지 않기 때문에 WHERE두 쿼리의 절을 내가 사용 또는 인덱스의 비 사용이 예에서 성능에 거의 영향을 미칠 것 같아, 그래서 당신은 두 경우 모두에서 모든 행을 반환하고 있습니다.


확실히 MySQL은 ORDER BY?
eggyal

@eggyal 메모리가 너무 크면 아닙니다.
커밋

@ njk : 이해가되지 않습니다 ... 모든 것을 메모리에로드하지 않고도 인덱스를 순서대로 탐색 할 수 있습니다. 파일 정렬을 수행 할 필요없이 결과가 정렬됩니다.
eggyal

@ eggyal의 크기에 의문을 제기합니다 varchar(5000).
커밋

@ njk : 그러나 그 열은 색인에 없거나 정렬에 사용되지 않습니다.
eggyal 2009 년

2

ORDER BY최적화 아래에 문서화 된 바와 같이 :

filesort사용되지 않는 느린 쿼리의 max_length_for_sort_data경우를 트리거하기에 적합한 값으로 낮추십시오 filesort.

Peter Zaitsev 자신의 블로그 기사 인 정확히 무엇 read_rnd_buffer_size 에서 다음과 같이 설명합니다.

저에게 이것은 MySQL 4.1부터이 옵션이 좁은 범위의 경우에 사용됨을 의미합니다 -max_length_for_sort_data 보다 적은 수의 필드를 검색하면 정렬 버퍼 및 정렬 파일에 데이터를 저장해야하므로 선택한 열이 read_rnd_buffer가 필요하지 않습니다. max_length_for_sort_data 보다 길기 때문에 종종 TEXT / BLOB 열이 있음을 의미합니다. 그러나 많은 수의 열이 있거나 긴 VARCHAR 열이 사용 된 경우 사용됩니다 . 정적 표시에서 max_length_for_sort_data 보다 긴 행을 작성하는 데 몇 개의 UTF8 VARCHAR (255) 만 있으면됩니다 .

이것은 이것이 max_length_for_sort_data선택하는 열의 총 크기에 대한 제한이며, filesort그보다 큰 것은 인덱스 기반 정렬 대신 사용됩니다.

귀하의 경우 entry_id(5002 바이트)를 선택 하면이 변수의 1KiB 기본값보다 전체 크기 filesort가 사용 되므로 사용됩니다. 8KiB로 제한을 높이려면 다음을 수행하십시오.

SET SESSION max_length_for_sort_data = 8192;

이 설정과 매우 유사한 설정이있는 테이블이 있으며이 설정은 파일 정렬 사용시 변경 사항을 유발하지 않는 것으로 보입니다.

@muffinista : 흥미 롭습니다. @RolandoMySQLDBA의 답변 에 따라 다른 버퍼 설정과 관련이 있다고 가정합니다 .
eggyal

2

여기서 흥미로운 답변을 많이 받았지만 아무도 그 질문에 정확히 대답하지 못했습니다. 왜 이런 일이 발생합니까? 내가 이해하는 것처럼 SELECT 쿼리에 MySQL의 가변 길이 데이터가 포함되어 있고 요청 된 모든 열과 일치하는 인덱스가 없으면 항상 파일 정렬을 사용합니다. 데이터의 크기는 여기에별로 관련이 없습니다. MySQL 문서 에서이 질문에 대한 직접적인 대답을 찾기는 어렵지만 여기 에 누군가가 귀하와 매우 비슷한 문제를 겪고있는 좋은 블로그 게시물 이 있습니다.

MySQL 쿼리 최적화를위한 10 가지 팁을 참고하십시오 .

따라서 entry_id에 색인을 보유 할 수있는 경우 색인을 추가하고 모두 설정할 수 있습니다. 그러나 이것이 옵션이라는 것을 의심하므로 어떻게해야합니까?

이것에 대해 무엇을 해야하는지 여부는 별도의 질문입니다. 'filesort'는 MySQL에서 이름이 잘못 되었다는 것을 알아야합니다 . 실제로이 특정 쿼리를 정렬하는 데 사용되는 알고리즘의 이름 일뿐이며 많은 경우 정렬은 실제로 메모리에서 발생합니다. 이 테이블이 많이 성장하지 않을 것으로 예상된다면 큰 문제는 아닐 것입니다.

반면이 테이블에 백만 개의 행이 있으면 문제가있을 수 있습니다. 이 테이블에서 쿼리 페이지 매김을 지원해야하는 경우 여기에서 심각한 성능 문제가 발생할 수 있습니다. 이 경우 가변 길이 데이터를 새 테이블로 분할하고 JOIN을 수행하여이를 검색하는 것이 고려할 올바른 최적화입니다.

이 질문에 대해 이야기하는 SO에 대한 몇 가지 다른 답변이 있습니다.


OP의 첫 번째 쿼리 " 는 MySQL에 가변 길이 데이터를 포함하고 있으며 요청 된 모든 열과 일치하는 인덱스는 없지만 " filesort이 경우에는 사용되지 않은 것 같습니다. 또한 메모리에서 작은 테이블을 정렬하는 것조차도 용인 할 수없는 성능 히트로 입증 될 수 있다고 생각합니다. 예 : 쿼리가 많이 수행되고 캐시가 사용될 수 없도록 테이블이 변경되는 경우.
eggyal

테스트 할 시간이 없지만 dev.mysql.com/doc/refman/5.1/en/char에 지정된 길이를 저장하는 데 2 ​​바이트가 필요한 VARCHAR을 사용하여 이것이 트리거되는지 궁금합니다 . html- 첫 번째 쿼리는 해당 제한 내에 들어가지만 두 번째 쿼리는 그렇지 않습니다.

0

WHERE검색어에 절을 추가 하십시오.

인덱스 의 사용되지 않은 모든 부분과 모든 추가 ORDER BY 열이 WHERE 절 에서 상수 인 경우 ORDER BY 가 인덱스와 정확하게 일치하지 않더라도 인덱스를 사용할 수 있습니다 . 경우에 따라 MySQL은 인덱스를 사용하여 ORDER BY 를 해결할 수 없지만 여전히 인덱스를 사용하여 WHERE 절 과 일치하는 행을 찾습니다 .

http://dev.mysql.com/doc/refman/5.0/en/order-by-optimization.html


그러나이 경우는 ORDER BY 않습니다 정확히 인덱스와 일치하므로 가질 필요가 없습니다 WHERE절.
eggyal 2009 년

사이트의 실제 쿼리에 "where"절이 있으므로 파일 정렬의 원인이 아니라는 것을 알고 있습니다. varchar를 사용하는지 궁금합니다.

0

내가 아는 한 varchar는 최대 8000 바이트 (대략 4000 자) 만 보유 할 수 있습니다. 따라서 5000은 저장 한도를 초과하는 것으로 보이며이 경우 정렬이 엉망인 이유 일 수 있습니다.

"varchar [(n | max)] 가변 길이, 비 유니 코드 문자 데이터. n은 1-8,000 사이의 값이 될 수 있습니다. max는 최대 스토리지 크기가 2 ^ 31-1 바이트임을 나타냅니다. 스토리지 크기는 실제입니다 입력 한 데이터 길이 + 2 바이트. 입력 한 데이터의 길이는 0 자일 수 있습니다. varchar에 대한 SQL-2003 동의어는 문자 변경 또는 문자 변경입니다. "

이것이 귀하의 질문에 답변되기를 바랍니다.


아래에 설명 된 바와 같이 분류 " VARCHAR 열의 값은 가변 길이의 문자열 길이는 5.0.3 및 이후 버전 65,535 5.0.3 전의 0 ~ 255의 값으로 지정하고, 0 수있는 효과적인.. MySQL 5.0.3 이상에서 최대 길이는 최대 행 크기 (모든 열에서 공유되는 65,535 바이트) 및 사용 된 문자 세트에 따라CHARVARCHARVARCHAR
달라집니다

0

테이블에는 126 개의 행만 있습니다. 모든 행의 크기가 최대 약 5KB 인 경우에도 디스크에서 읽을 수있는 총 크기는 약 600KB에 불과합니다. 이는 전체가 아닙니다. 솔직히 말하면, 그것은 현대의 대부분의 디스크 드라이브의 캐시 크기보다 적은 양일 것입니다.

이제 서버가 쿼리를 수행하기 위해 데이터를 검색해야하는 경우 가장 비싼 작업은 디스크에서 데이터를 읽는 것입니다. 그러나 인덱스 순서에 따라 읽는 것이 항상 가장 빠른 방법은 아닙니다. 특히 데이터 양이 너무 적은 경우에 그러합니다.

귀하의 경우, 디스크에서 단일 블록으로 디스크에서 전체 테이블 데이터를 메모리로 읽고 (아마도 하나의 디스크 읽기 작업 또는 탐색에서) 디스크로 즉시 정렬하는 ORDER BY를 충족시키기 위해 RAM으로 정렬하는 것이 훨씬 효율적입니다. 읽기 작업. 서버가 인덱스에 따라 데이터를 읽는 경우 동일한 데이터 파일 내에서 여러 번 검색을 수행하여 최대 126 (oops!) 읽기 작업을 실행해야합니다.

즉, 순차적 스캔이 항상 나쁜 것은 아니며 mysql이 반드시 어리석은 것은 아닙니다. mysql이 해당 인덱스를 사용하도록 강요하면 현재 가지고있는 순차 스캔보다 느리게 작동합니다.

그리고 5KB 필드가 포함되지 않았을 때 인덱스를 사용하는 이유는 검색된 데이터가 테이블에있는 데이터의 99 %를 구성하지 않기 때문입니다. 5KB 필드를 포함 시키면 쿼리는 99 %의 데이터를 읽어야하므로 전체 내용을 읽고 나중에 메모리에서 정렬하는 것이 더 저렴합니다.


절 전체가 아닌 조건과 절 을 만족시키는 인덱스 사용과 관련하여 전체 테이블 스캔을 피하는 방법 에서 여러 가지를 혼란스럽게하는 것처럼 들립니다 . JOINWHEREORDER BY
eggyal

정확히 반대입니다. 이 경우 전체 테이블 스캔은 인덱스 순서로 읽는 것보다 더 빠르기 때문에 GOOD 일입니다.

0

어떤 버전의 MySQL을 사용하고 있습니까?

5.1에서 시나리오를 설정하려고 시도하고 더미 데이터를 채웠습니다. 제공 한 SQL을 사용하여 EXPLAIN에 따라 매번 테이블 스캔 만합니다. MYSQL에서 order를 사용할 때 기본적으로 primary index가 order by에 사용 되더라도 filesort를 사용합니다.

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