3 천 5 백만 행 + 테이블에 대한 효과적인 MySQL 테이블 / 인덱스 디자인, 200 개 이상의 해당 열 (이중), 모든 조합을 쿼리 할 수 ​​있음


17

다음 상황에 대한 테이블 / 인덱스 디자인에 대한 조언을 찾고 있습니다.

복합 기본 키 (자산 (int), 날짜 (날짜))가있는 큰 테이블 (주가 기록 데이터, InnoDB, 3 천 5 백만 행 및 증가)이 있습니다. 가격 정보 외에도 각 레코드에 해당하는 200 개의 이중 값이 있습니다.

CREATE TABLE `mytable` (
`assetid` int(11) NOT NULL,
`date` date NOT NULL,
`close` double NOT NULL,
`f1` double DEFAULT NULL,   
`f2` double DEFAULT NULL,
`f3` double DEFAULT NULL,   
`f4` double DEFAULT NULL,
 ... skip a few 
`f200` double DEFAULT NULL, 
PRIMARY KEY (`assetid`, `date`)) ENGINE=`InnoDB` DEFAULT CHARACTER SET latin1 COLLATE
    latin1_swedish_ci ROW_FORMAT=COMPACT CHECKSUM=0 DELAY_KEY_WRITE=0 
    PARTITION BY RANGE COLUMNS(`date`) PARTITIONS 51;

나는 처음에 200 개의 이중 열을이 테이블에 직접 저장하여 쉽게 업데이트하고 검색 할 수 있었으며이 테이블에서 수행 된 유일한 쿼리는 assetid 및 날짜 (이 테이블에 대한 모든 쿼리에 종교적으로 포함 됨)로 제대로 작동했습니다 ), 200 개의 이중 열만 읽었습니다. 내 데이터베이스 크기는 약 45 기가였습니다

그러나 이제는 200 개의 열 (f1, f2, ... f200)을 조합 하여이 테이블을 쿼리 할 수 ​​있어야하는 요구 사항이 있습니다.

select from mytable 
where assetid in (1,2,3,4,5,6,7,....)
and date > '2010-1-1' and date < '2013-4-5'
and f1 > -0.23 and f1 < 0.9
and f117 > 0.012 and f117 < .877
etc,etc

나는 역사적으로 이전 에이 많은 양의 데이터를 처리 할 필요가 없었으므로 첫 번째 본능은이 200 열 각각에 인덱스가 필요하다는 것입니다. 또는 큰 테이블 스캔 등으로 마무리 될 것입니다. 기본 키, 값 및 값을 갖는 200 개의 열 각각에 대한 테이블이 필요했습니다. 그래서 나는 그와 함께 갔다.

CREATE TABLE `f1` (
`assetid` int(11) NOT NULL DEFAULT '0',
`date` date NOT NULL DEFAULT '0000-00-00',
`value` double NOT NULL DEFAULT '0',
PRIMARY KEY (`assetid`, `date`),
INDEX `val` (`value`)
) ENGINE=`InnoDB` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci ROW_FORMAT=COMPACT CHECKSUM=0 DELAY_KEY_WRITE=0;

200 개의 테이블을 모두 채우고 색인을 생성했습니다. 정기적으로 assetid 및 날짜 범위에 대해 쿼리되고 200 개의 열이 모두 선택되므로 기본 테이블을 200 열로 그대로 두었습니다. 읽기를 위해 부모 테이블에 해당 열을 남겨두고 (인덱싱되지 않은) 그대로 추가하고 조인 필터링을 위해 자체 테이블에 인덱스를 추가하는 것이 가장 효과적이라고 생각했습니다. 나는 새로운 형태의 쿼리에 대해 설명했다.

select count(p.assetid) as total 
from mytable p 
inner join f1 f1 on f1.assetid = p.assetid and f1.date = p.date
inner join f2 f2 on f2.assetid = p.assetid and f2.date = p.date 
where p.assetid in(1,2,3,4,5,6,7)
and p.date >= '2011-01-01' and p.date < '2013-03-14' 
and(f1.value >= 0.96 and f1.value <= 0.97 and f2.value >= 0.96 and f2.value <= 0.97) 

실제로 원하는 결과를 얻었습니다. Explain은 스캔 한 행 이이 쿼리에 대해 훨씬 작다는 것을 보여줍니다. 그러나 나는 바람직하지 않은 부작용으로 마무리했습니다.

1) 내 데이터베이스가 45 기가에서 110 기가로 이동했습니다. 더 이상 DB를 RAM에 보관할 수 없습니다. (하지만 256Gig의 RAM이 있습니다)

2) 새로운 데이터의 야간 삽입은 이제 한 번이 아니라 200 번 수행되어야합니다.

3) 새로운 200 테이블의 유지 보수 / 조각 모음은 1 테이블보다 200 배 더 오래 걸립니다. 밤에는 완료 할 수 없습니다.

4) f1 등 테이블에 대한 쿼리가 반드시 수행되는 것은 아닙니다. 예를 들면 다음과 같습니다.

 select min(value) from f1 
 where assetid in (1,2,3,4,5,6,7) 
 and date >= '2013-3-18' and date < '2013-3-19'

위의 쿼리는 Explain이 1000 개 미만의 행에서 조회하는 데 30 초 이상 걸릴 수 있음을 보여줍니다. 인덱스가 너무 커서 메모리에 맞지 않기 때문이라고 생각합니다.

그것은 나쁜 소식이 많았으므로 더 자세히보고 분할을 발견했습니다. 메인 테이블에 파티션을 구현하고 3 개월마다 날짜별로 파티션을 나습니다. 월간은 나에게 의미가있는 것처럼 보였지만 일단 120 개 이상의 파티션을 얻으면 성능이 저하된다는 것을 읽었습니다. 분기별로 분할하면 향후 20 년 정도 동안 저를 남겨 둘 것입니다. 각 파티션은 2Gig 미만입니다. 나는 파티션을 설명하고 모든 것이 올바르게 정리되는 것처럼 보이므로, 최소한 분석 / 최적화 / 수리 목적으로 파티션이 좋은 단계라고 생각합니다.

나는이 기사와 함께 많은 시간을 보냈다

http://ftp.nchu.edu.tw/MySQL/tech-resources/articles/testing-partitions-large-db.html

내 테이블은 현재 기본 키로 파티션되어 있습니다. 이 기사에서는 기본 키를 사용하면 분할 된 테이블을 느리게 만들 수 있지만이를 처리 할 수있는 머신이 있으면 분할 된 테이블의 기본 키가 더 빠릅니다. 내가가는 길에 큰 기계 (256 G RAM)가 있다는 것을 알았을 때, 나는 열쇠를 뒀다.

내가 보았을 때 여기 내 옵션이 있습니다.

옵션 1

1) 여분의 200 테이블을 제거하고 쿼리에서 테이블 스캔을 수행하여 f1, f2 등의 값을 찾으십시오. 고유하지 않은 인덱스는 실제로 파티션 된 테이블에서 성능을 저하시킬 수 있습니다. 사용자가 쿼리를 실행하기 전에 Explain을 실행하고 스캔 된 행 수가 정의한 임계 값을 초과하면 거부합니다. 거대한 데이터베이스의 고통을 저 자신에게 저장하십시오. 어쨌든 곧 모든 것이 기억에 남을 것입니다.

하위 질문 :

적절한 파티션 구성표를 선택한 것처럼 들립니까?

옵션 2

동일한 3 개월 구성표를 사용하여 200 개의 테이블을 모두 분할하십시오. 더 작은 행 스캔을 즐기고 사용자가 더 큰 쿼리를 실행할 수 있습니다. 이제 그들은 적어도 분할되어 유지 관리 목적으로 한 번에 하나의 파티션을 관리 할 수 ​​있습니다. 어쨌든 곧 모든 것이 기억에 남을 것입니다. 매일 밤 업데이트하는 효율적인 방법을 개발하십시오.

하위 질문 :

쿼리 할 때 항상 assetid와 날짜가 있다는 것을 알고 이러한 f1, f2, f3, f4 ... 테이블에서 기본 키 인덱스를 피할 수있는 이유가 있습니까? 나에게는 직관적이지 않지만이 크기의 데이터 세트에는 익숙하지 않습니다. 내가 생각하는 데이터베이스를 축소시킬 것입니다.

옵션 3

마스터 테이블에서 f1, f2, f3 열을 삭제하여 해당 공간을 확보하십시오. 200 기능을 읽어야하는 경우 200 조인을 수행하십시오. 소리가 나지 않을 수도 있습니다.

옵션 4

지금까지 내가 생각한 것보다 더 잘 구성 할 수있는 방법이 있습니다.

* 참고 : 곧 각 항목에 이러한 이중 값의 50-100을 추가 할 것이므로 앞으로 나올 것을 알고 설계해야합니다.

모든 도움을 주셔서 감사합니다

업데이트 # 1-2013 년 3 월 24 일

나는 아래에있는 의견에서 제안 된 아이디어를 가지고 다음 설정으로 하나의 새 테이블을 만들었습니다.

create table 'features'{
  assetid int,
  date    date,
  feature varchar(4),
  value   double
}

3 개월 간격으로 테이블을 분할했습니다.

내 데이터베이스가 45 Gig로 돌아가서이 새로운 테이블을 채우기 시작하기 위해 이전 200 테이블을 날려 버렸습니다. 하루 반 후, 그것은 완성되었고, 내 데이터베이스는 이제 통통한 220 기가에 앉아 있습니다 !

하나의 조인에서 얻을 수 있으므로 마스터 테이블에서 이러한 200 값을 제거 할 수는 있지만 실제로는 25Gig 정도를 돌려줍니다.

나는 자산 ID, 날짜, 기능 및 가치 지수에 대한 기본 키를 만들도록 요청했으며 9 시간 동안 징징을 한 후에는 찌그러지지 않았으며 얼어 붙은 것처럼 보였으므로 그 부분을 죽였습니다.

나는 두 개의 파티션을 재건했지만 많은 공간을 확보하지 못하는 것 같습니다.

따라서 솔루션이 이상적이지 않은 것처럼 보입니다. 행이 열보다 훨씬 더 많은 공간을 차지합니까? 그렇기 때문에이 솔루션이 훨씬 더 많은 공간을 차지했을 수 있습니까?

이 기사를 보았습니다.

http://www.chrismoos.com/2010/01/31/mysql-partitioning-tables-with-millions-of-rows

그것은 나에게 아이디어를 주었다. 그것은 말한다 :

처음에는 날짜별로 RANGE 파티셔닝에 대해 생각했으며 쿼리에서 날짜를 사용하는 동안 쿼리의 날짜 범위가 매우 일반적이므로 모든 파티션에 쉽게 적용 할 수 있습니다.

이제 날짜 별 범위 분할도 가능하지만 큰 날짜 범위별로 검색을 허용하므로 분할의 효율성이 떨어집니다. 검색 할 때 항상 날짜 범위가 설정되지만 항상 자산 ID 목록이 표시됩니다. 아마도 내 솔루션은 assetid 및 날짜별로 파티션을 나누는 것이어야합니다. 일반적으로 검색 된 자산 범위를 식별합니다 (표준 목록, S & P 500, Russell 2000 등이 있습니다). 이렇게하면 전체 데이터 세트를 거의 보지 않을 것입니다.

어쨌든 나는 어쨌든 assetid와 date를 기본적으로 사용하므로 많은 도움이되지 않을 것입니다.

더 이상의 생각이나 의견이 있으면 감사하겠습니다.


2
왜 200 테이블이 필요한지 알지 못합니다. 있는 하나의 테이블은 (value_name varchar(20), value double)저장 모든 것을 할 수있을 것 ( value_namef1, f2, ...)
a_horse_with_no_name

감사. 내가 개별적으로 넣은 이유는 테이블에서 50 개의 색인 제한을 얻는 것이 었습니다. 나는 그것들을 5 개의 테이블, 각각 40 개의 값에 넣는 것에 대해 생각했지만 각각 17,000 개의 레코드를 삽입하고 각각 40 개의 인덱스가있는 테이블에서 어떤 삽입 성능이 될지 알지 못했습니다. assetid, date의 각 조합은 고유 한 f1, f2 ... 값을 갖습니다. 기본 키 assetid, date가 있고 (value_name, value)에 색인이 (assetid, date, value_name, value) 인 단일 테이블을 제안하고 있습니까? 그 테이블은 35 mil * 200 = 70 억 행을 가지고 있지만 아마도 잘 분할되어 작동합니까?
dyeryn

이 방법을 시도한 내 경험으로 업데이트 된 게시물
dyeryn

개발중인 최종 솔루션이 있습니다. 완료되면 업데이트됩니다. 본질적으로 특정 파티셔닝 및 논리적 샤딩으로 여기에서 제안 된 단일 테이블 솔루션입니다.
dyeryn

다른 스토리지 엔진이 도움이 될 수 있습니까? InnoDb 대신 InfiniDB를 사용해보십시오. 열 데이터, 액세스 패턴은 큰 배치 업데이트, 범위 기반 읽기 및 최소 테이블 유지 관리처럼 보입니다.
지저분한 지저분한

답변:


1

우연히도 유연성을 위해 키-값 쌍 구조를 설계했으며 현재 테이블이 1.5B 행 이상이고 ETL이 너무 느린 클라이언트 지원 중 하나를 찾고 있습니다. 내 경우에는 다른 많은 것들이 있지만 그 디자인에 대해 생각해 보았습니다. 키-값 쌍 설계에서 200 개 열 모두에 현재 값이있는 하나의 행이 있으며이 행은 200 개 행으로 변환됩니다. 주어진 AssetID 및 날짜에 따라 실제로 200 f1 ~ f200 값이 모두있는 행 수에 따라이 디자인으로 공간 이점을 얻을 수 있습니까? 30 % od 열조차도 공간 값보다 NULL 값이 있다고 말합니다. 키-값 쌍 디자인에서 value id가 NULL이면 해당 행이 테이블에있을 필요가 없기 때문입니다. 그러나 기존 열 구조 디자인에서는 NULL조차도 공간을 차지합니다. (100 % 확신 할 수는 없지만 테이블에 NULL이 30 열을 초과하면 NULL에 4 바이트가 걸립니다). 이 디자인을보고 모든 35M 행에 200 개의 열에 모두 값이 있다고 가정하면 현재 db는 테이블에서 200 * 35M = 700M 행이됩니다. 그러나 열을 행에 옮길 때 단일 테이블의 모든 열에 대해 가지고있는 테이블 공간이 그리 높지 않습니다. 이 조옮김 작업에서는 실제로 값이 NULL 인 행이 없습니다. 따라서 실제로이 테이블에 대해 쿼리를 실행하고 몇 개의 널이 있는지 확인하고 실제로 구현하기 전에 테이블 크기를 목표로 추정 할 수 있습니다. 그러나 열을 행에 옮길 때 단일 테이블의 모든 열에 대해 가지고있는 테이블 공간이 그리 높지 않습니다. 이 조옮김 작업에서는 실제로 값이 NULL 인 행이 없습니다. 따라서 실제로이 테이블에 대해 쿼리를 실행하고 몇 개의 널이 있는지 확인하고 실제로 구현하기 전에 테이블 크기를 목표로 추정 할 수 있습니다. 그러나 열을 행에 옮길 때 단일 테이블의 모든 열에 대해 가지고있는 테이블 공간이 그리 높지 않습니다. 이 조옮김 작업에서는 실제로 값이 NULL 인 행이 없습니다. 따라서 실제로이 테이블에 대해 쿼리를 실행하고 몇 개의 널이 있는지 확인하고 실제로 구현하기 전에 테이블 크기를 목표로 추정 할 수 있습니다.

두 번째 장점은 읽기 성능입니다. 데이터를 쿼리하는 새로운 방법은 where 절 에서이 f1 ~ f200 열의 조합입니다. 키 값 쌍 디자인을 사용하면 f1 ~ f200이 하나의 열에 있고 "FildName"이라고 말하고 값이 두 번째 열에 있으면 "FieldValue"라고합니다. 두 열 모두에서 CLUSTERED 인덱스를 가질 수 있습니다. 귀하의 검색어는 해당 선택의 UNION입니다.

어디에 (FiledName = 'f1'및 FieldValue 5와 6 사이)

노동 조합

(FiledName = 'f2'및 8과 10 사이의 FieldValue)

기타.....

실제 제품 서버에서 성능 수치를 알려 드리겠습니다. 각 보안 티커에 대해 75 개의 가격 열이 있습니다.


1

많은 행을 삽입해야하고 실제로 분석 쿼리 성능이 필요한 이러한 종류의 데이터를 처리 할 때 (이 경우에 해당한다고 가정합니다), 기둥 형 RDBMS가 적합하다는 것을 알 수 있습니다 . Infobright CE 및 InfiniDB CE (MySQL에 연결된 기둥 형 스토리지 엔진 모두)와 Vertica CE (MySQL과 유사한 PostgreSQL과 유사)도 살펴보십시오 ... Vertica는 무료입니다. 오픈 소스 인 경우 3 개의 노드와 1Tb의 데이터로 무료로 확장됩니다. Columnar RDBMS는 일반적으로 행 기반보다 10-100X 더 나은 "큰 쿼리"응답 시간과 5-50X 더 나은로드 시간을 제공합니다. 그것들을 올바르게 사용해야하거나 악취가납니다 (단일 행 작업을 수행하지 않고 대량 작업으로 모든 작업을 수행하십시오). ;-)

데이브 시스 크


1
우리는 3 노드 Vertica 설치에 약 10 억 행의 클릭 스트림 유형 데이터 (주식 시세 데이터와 다르지 않음)가 있습니다 ... 약 15 초 안에 하루 종일의 데이터를로드 할 수 있으며 쿼리 응답 시간이 500 밀리 초 범위 귀하의 경우에는 이것이 가치가있는 것처럼 들립니다.
Dave Sisk

나는 같은 것을 보증 할 수 있습니다. 마지막 회사에는 약 같은 수의 행을 가진 8 노드 Vertica 클러스터가 있었고 전체 집합에 대해 간단한 집계 쿼리가 평균 1-3 초 안에 반환되었습니다. 초기 Greenplum 클러스터의 약 1/4 비용이었습니다.
bma
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.