다음 상황에 대한 테이블 / 인덱스 디자인에 대한 조언을 찾고 있습니다.
복합 기본 키 (자산 (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를 기본적으로 사용하므로 많은 도움이되지 않을 것입니다.
더 이상의 생각이나 의견이 있으면 감사하겠습니다.
(value_name varchar(20), value double)
저장 모든 것을 할 수있을 것 (value_name
인f1
,f2
, ...)