2 억 2 천만 행 테이블 (9 기가 데이터)에서 쿼리 속도를 높이는 방법은 무엇입니까?


31

문제:

우리는 회원들이 서로 호환성이나 매칭에 대해 서로를 평가할 수있는 소셜 사이트를 가지고 있습니다. 이 user_match_ratings테이블에는 2 억 2 천만 개가 넘는 행 (9 개의 기가 데이터 또는 거의 20 개의 기가 색인)이 포함됩니다. 이 테이블에 대한 쿼리는 일반적으로 slow.log (임계 값> 2 초)에 표시되며 시스템에서 가장 자주 기록되는 느린 쿼리입니다.

Query_time: 3  Lock_time: 0  Rows_sent: 3  Rows_examined: 1051
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 395357 group by rating;"

Query_time: 4  Lock_time: 0  Rows_sent: 3  Rows_examined: 1294
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 4182969 group by rating;"

Query_time: 3  Lock_time: 0  Rows_sent: 3  Rows_examined: 446
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 630148 group by rating;"

Query_time: 5  Lock_time: 0  Rows_sent: 3  Rows_examined: 3788
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 1835698 group by rating;"

Query_time: 17  Lock_time: 0  Rows_sent: 3  Rows_examined: 4311
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 1269322 group by rating;"

MySQL 버전 :

  • 프로토콜 버전 : 10
  • 버전 : 5.0.77-log
  • 버전 bdb : Sleepycat 소프트웨어 : Berkeley DB 4.1.24 : (2009 년 1 월 29 일)
  • 버전 컴파일 머신 : x86_64 version_compile_os : redhat-linux-gnu

테이블 정보 :

SHOW COLUMNS FROM user_match_ratings;

제공합니다 :

╔═══════════════╦════════════╦════╦═════╦════════╦════════════════╗
 id             int(11)     NO  PRI  NULL    auto_increment 
 rater_user_id  int(11)     NO  MUL  NULL                   
 rated_user_id  int(11)     NO  MUL  NULL                   
 rating         varchar(1)  NO       NULL                   
 created_at     datetime    NO       NULL                   
╚═══════════════╩════════════╩════╩═════╩════════╩════════════════╝

샘플 쿼리 :

select * from mutual_match_ratings where id=221673540;

제공합니다 :

╔═══════════╦═══════════════╦═══════════════╦════════╦══════════════════════╗
 id         rater_user_id  rated_user_id  rating  created_at           
╠═══════════╬═══════════════╬═══════════════╬════════╬══════════════════════╣
 221673540  5699713        3890950        N       2013-04-09 13:00:38  
╚═══════════╩═══════════════╩═══════════════╩════════╩══════════════════════╝

인덱스

테이블에는 3 개의 인덱스가 설정되어 있습니다.

  1. 단일 인덱스 rated_user_id
  2. 에 지수 rater_user_idcreated_at
  3. 에 지수 rated_user_idrater_user_id
user_match_ratings의 색인을 보여줍니다.

제공합니다 :

╔════════════════════╦════════════╦═══════════════════════════╦══════════════╦═══════════════╦═══════════╦═════════════╦══════════╦════════╦═════════════════════════╦════════════╦══════════════════╗
 Table               Non_unique  Key_name                   Seq_in_index  Column_name    Collation  Cardinality  Sub_part  Packed  Null                     Index_type  Comment          
╠════════════════════╬════════════╬═══════════════════════════╬══════════════╬═══════════════╬═══════════╬═════════════╬══════════╬════════╬═════════════════════════╬════════════╬══════════════════╣
 user_match_ratings  0           PRIMARY                    1             id             A          220781193    NULL      NULL    BTREE                                                 
 user_match_ratings  1           user_match_ratings_index1  1             rater_user_id  A          11039059     NULL      NULL    BTREE                                                 
 user_match_ratings  1           user_match_ratings_index1  2             created_at     A          220781193    NULL      NULL    BTREE                                                 
 user_match_ratings  1           user_match_ratings_index2  1             rated_user_id  A          4014203      NULL      NULL    BTREE                                                 
 user_match_ratings  1           user_match_ratings_index2  2             rater_user_id  A          220781193    NULL      NULL    BTREE                                                 
 user_match_ratings  1           user_match_ratings_index3  1             rated_user_id  A          2480687      NULL      NULL    BTREE                                                 
╚════════════════════╩════════════╩═══════════════════════════╩══════════════╩═══════════════╩═══════════╩═════════════╩══════════╩════════╩═════════════════════════╩════════════╩══════════════════╝

인덱스를 사용하더라도 이러한 쿼리는 느립니다.

내 질문:

이 테이블 / 데이터를이 데이터를 메모리에 저장하기에 충분한 램이있는 서버의 다른 데이터베이스로 분리하면 쿼리 속도가 향상됩니까? 어쨌든 이러한 쿼리를 더 빠르게 만들기 위해 개선 할 수있는 테이블 / 인덱스가 설정되어 있습니까?

현재 16GB의 메모리가 있습니다. 그러나 우리는 기존 머신을 32GB로 업그레이드하거나 최소한 그 정도의 솔리드 스테이트 드라이브를 가진 새로운 머신을 추가하려고합니다.


1
당신의 질문은 믿어지지 않습니다. <= 2 초 안에 결과를 얻는 방법에 대한 귀하의 현재 솔루션에 매우 관심이 있습니까? 하나의 테이블에 2 천만 개의 레코드 만 있고 여전히 30 초가 걸리기 때문입니다 SELECT QUERY. 제안 하시겠습니까? 추신 당신의 질문으로이 커뮤니티에 가입하게되었습니다 (y);)
NullPointer

2
쿼리하는 테이블의 인덱스를보십시오. 적절한 인덱스를 작성하여 쿼리를 많이 개선 할 수 있습니다. 항상 그런 것은 아니지만 쿼리에서 where 절의 열에 대한 인덱스를 제공하여 쿼리가 빠르게 이루어지는 많은 인스턴스를 보았습니다. 특히 테이블이 커지면 커집니다.
Ranknoodle

물론 @Ranknoodle. 고맙습니다. 각각 확인하겠습니다.
NullPointer

답변:


28

무작위 순서로 발생하는 문제에 대한 생각 :

  • 이 쿼리의 명백한 인덱스는 다음과 같습니다 (rated_user_id, rating). 백만 명의 사용자 중 하나에 대해서만 데이터를 가져오고 17 초가 필요한 쿼리는 (rated_user_id, rater_user_id)인덱스에서 읽은 다음 테이블에서 rating열에 대한 (수백에서 수천) 값을 읽는 것과 같은 잘못된 작업을 수행하는 것 rating입니다. 따라서 쿼리는 여러 다른 디스크 위치에있는 테이블의 많은 행을 읽어야합니다.

  • 테이블에 수많은 인덱스를 추가하기 전에 전체 데이터베이스, 전체 느린 쿼리 세트의 성능을 분석하고 데이터 유형 선택, 사용하는 엔진 및 구성 설정을 다시 확인하십시오.

  • 최신 버전의 MySQL, 5.1, 5.5 또는 5.6 (Percona 및 MariaDB 버전)으로 이동하는 것을 고려하십시오. 버그가 수정되고 옵티마이 저가 개선되었으며 느린 쿼리에 대한 낮은 임계 값을 1 초 미만으로 설정할 수있는 몇 가지 이점 (예 : 10 밀리 초) 이렇게하면 느린 쿼리에 대한 정보가 훨씬 향상됩니다.

  • 의 데이터 유형에 대한 선택 rating이 이상합니다. VARCHAR(1)? 왜 안돼 CHAR(1)? 왜 안돼 TINYINT? 이렇게하면 테이블과 해당 열을 포함하는 인덱스에 공간을 절약 할 수 있습니다. varchar (1) 열은 char (1)보다 1 바이트 더 필요하며 utf8 인 경우 (var) char 열에는 1 (tinyint) 대신 3 (또는 4) 바이트가 필요합니다.


2
잘못된 데이터 유형을 사용하는 경우 성능 측면에서 얼마나 많은 영향을 미치거나 스토리지가 낭비됩니까 (%)?
FlyingAtom

1
@FlyingAtom 경우에 따라 다르지만 여전히 스캔해야하는 일부 인덱스 열 (예 : where 절이없고 해당 열만 검색하는 경우)에서 엔진이 대신 인덱스를 스캔하기로 결정할 수 있습니다. 데이터 유형을 절반 크기로 최적화하면 스캔 속도가 두 배 빨라지고 응답 크기는 절반이됩니다. 인덱스 대신 테이블을 계속 스캔하는 경우 (예 : 인덱스의 열뿐만 아니라 더 많은 열을 검색하는 경우) 이점이 덜 중요합니다.
Sebastián Grignoli

-1

나는 때때로 6 천만 건의 기록으로 독일 정부의 테이블을 처리했습니다.

우리는이 테이블을 많이 가지고있었습니다.

그리고 테이블의 총 행 수를 여러 번 알아야했습니다.

오라클 및 Microsoft 프로그래머와 대화 한 후 우리는 그렇게 행복하지 않았습니다 ...

따라서 데이터베이스 프로그래머 그룹은 모든 테이블에서 항상 총 레코드 번호가 저장되는 레코드 인 레코드 하나를 결정했습니다. INSERT 또는 DELETE 행에 따라이 숫자를 업데이트했습니다.

우리는 다른 모든 방법을 시도했습니다. 이것은 가장 빠른 방법입니다.

우리는 1998 년부터 지금이 방식을 사용하고 있으며 수백만 레코드 테이블에서 행 수가 잘못되었습니다.


7
지난 18 년 동안 소개 된 기능 중 일부를 살펴볼 것을 제안합니다. 그 중에서도 count(*)몇 가지 개선 사항이 있습니다.
dezso

당신이 그들을 계산할 수없는 경우 당신은 잘못된 번호가 없었어요 어떻게 알 수 있습니까? uhmmmm ...
Tonca

-3

다음과 같은 등급 유형으로 분할하려고합니다.

mutual_match_ratings_N, mutual_match_ratings_S 등

각 유형에 대해 쿼리를 수행해야하지만 다른 방법보다 빠를 수도 있습니다. 시도 해봐.

여기에는 고정 된 등급 유형이 있으며이 새로운 구조에서 최악의 다른 쿼리에는이 테이블이 필요하지 않다고 가정합니다.

이 경우 다른 접근 방식을 찾거나 공간 및 유지 관리 성 (또는 응용 프로그램 논리) 측면에서 저렴한 경우 테이블의 두 복사본 (초기 테이블 및 파티션 된 테이블)을 유지 관리해야합니다.

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