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_id및created_at
  3. 에 지수 rated_user_id및rater_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.