有效的mysql表/索引设计,可处理3500万行以上的表,并具有200+相应的列(双精度),可以查询其任意组合


17

我正在针对以下情况寻求有关表/索引设计的建议:

我有一个大表(股价历史数据,InnoDB,3500万行,并且还在不断增长),它具有复合主键(资产(整数),日期(日期))。除了定价信息外,我还有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个双列存储在此表中,以便于更新和检索,并且工作得很好,因为在此表上进行的唯一查询是按资产ID和日期进行的(这些宗教性地包含在针对该表的任何查询中) ),只读取了200个双列。我的数据库大小约为45 Gig

但是,现在我有一个要求,我需要能够通过这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张桌子。我将所有200列的表保留完整,因为通常会在资产ID和日期范围内查询该表,并选择所有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) 

的确,确实达到了我想要的结果,解释说明给我显示此查询扫描的行要小得多。但是,我最后有一些不良的副作用。

1)我的数据库从45 Gig升至110 Gig。我无法再将数据库保留在RAM中。(但是我有256Gig的RAM)

2)现在需要每晚进行200次新数据插入,而不是一次

3)新200张桌子的维护/碎片整理时间比一张桌子长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'

上面的查询虽然解释说明了它在<1000行处的外观,但可能需要30秒钟以上的时间才能完成。我认为这是因为索引太大而无法容纳在内存中。

由于那是很多坏消息,因此我进一步查找并发现了分区。我在主表上实现了分区,每3个月按日期分区一次。月刊对我来说似乎很有意义,但我已经读到,一旦获得超过120个分区,性能就会下降。按季度划分将使我在接下来的20年左右处于该水平之下。每个分区略低于2 Gig。我跑了解释分区,一切似乎都在适当地修剪,所以无论我觉得分区是一个好步骤,至少出于分析/优化/修复的目的。

我花了很多时间写这篇文章

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

我的表当前已分区,并且主键仍在上面。本文提到主键会使分区表变慢,但是如果您有一台可以处理的表,则分区表上的主键会变快。知道我正在路上有一台大机器(256 G RAM),所以我一直不停地按键。

如我所见,这是我的选择

选项1

1)删除多余的200个表,然后让查询进行表扫描以找到f1,f2等值。非唯一索引实际上会损害正确分区表上的性能。在用户运行查询之前运行解释,如果扫描的行数超过我定义的某个阈值,则拒绝它们。为自己省去了巨型数据库的痛苦。哎呀,无论如何,所有这些都会很快被保存在内存中。

子问题:

听起来我已经选择了合适的分区方案吗?

选项2

使用相同的3个月方案对所有200个表进行分区。享受较小的行扫描,并允许用户运行较大的查询。现在,它们至少已分区,出于维护目的,我可以一次管理1个分区。哎呀,无论如何,所有这些都会很快被保存在内存中。开发每晚更新它们的有效方法。

子问题:

您是否知道我在查询时总是拥有资产编号和日期的原因而避免在这些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个月的间隔对表进行了分区。

我销毁了之前的200张表,以便数据库降到45 Gig,并开始填充此新表。一天半后,它完成了,现在我的数据库只有 220 Gigs!

它确实允许从主表中删除这200个值,因为我可以从一个联接中获取它们,但这实际上只能给我25个Gigs左右的可能

我要求它在资产编号,日期,功能和价值指数上创建一个主键,经过9个小时的修改之后,它实际上并未产生任何凹痕,而且似乎冻结了,所以我将这部分内容销毁了。

我重建了几个分区,但似乎并没有回收太多/任何空间。

因此,该解决方案可能看起来并不理想。我想知道,行是否比列占用的空间大得多,这可能就是为什么此解决方案占用了更多的空间吗?

我碰到了这篇文章:

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

它给了我一个主意。它说:

起初,我想到了按日期对RANGE进行分区,而当我在查询中使用日期时,查询具有很大的日期范围是很常见的,这意味着它可以轻松地跨越所有分区。

现在,我也按日期对区域进行分区,但也将允许按较大的日期范围进行搜索,这将降低分区的有效性。当我搜索时,我将始终具有日期范围,但是我还将始终具有资产编号列表。也许我的解决方案应该是按资产ID和日期进行划分,在这里我可以识别通常搜索的资产ID范围(我可以提供标准列表,S&P 500,Russell 2000等)。这样,我几乎永远不会查看整个数据集。

再说一次,无论如何,我还是以资产号和日期为主要键,所以也许这没有太大帮助。

任何更多的想法/意见,将不胜感激。


2
我不明白为什么您需要200张桌子。与单台(value_name varchar(20), value double)将能够存储一切(value_namef1f2...)
a_horse_with_no_name

谢谢。我将它们单独放置的原因是要在一个表上限制50个索引。我曾考虑过将它们放入5个表中,每个表40个值,但是我要为每个表插入17000左右条记录,并且不知道在具有40个索引的表上的插入性能如何。请注意,assetid,date的每个组合都有其自己的f1,f2 ...值。您是否建议使用(资产,日期,值名称,值),主键资产ID,日期或可能是(值名称,值)的索引的单个表?该表将具有3500万* 200 = 70亿行,但是将分区很好地行得通吗?
dyeryn13年

我尝试过此方法的经验更新后的帖子
dyeryn

我拥有开发中的最终解决方案,完成后我将进行更新。它本质上是这里提出的具有特定分区和逻辑分片的单表解决方案。
dyeryn13年

可能会有其他存储引擎帮助吗?可以尝试使用InfiniDB而不是InnoDb吗?列数据,访问模式看起来像是大批量更新,基于范围的读取和最小的表维护。
凌乱的

Answers:


1

碰巧的是,我也正在研究一种客户支持,其中我们设计了键值对结构以提高灵活性,并且目前表超过1.5B行,而ETL太慢了。好吧,在我看来,还有很多其他事情,但是您是否考虑过这种设计。您将有一行包含全部200列的当前值,该行将在键值对设计中转换为200行。根据给定AssetID和Date的多少,您将通过这种设计获得空间优势?实际上有200行f1至f200值存在多少行?如果您说甚至30%的od列都具有NULL值,那将节省您的空间。因为在键值对设计中,如果值id为NULL,则该行不需要在表中。但是在现有的列结构设计中,即使NULL也会占用空间。(我不确定100%,但是如果表中的NULL超过30列,则NULL占用4个字节)。如果您看到此设计并假定所有35M行的所有200列均具有值,那么您当前的数据库将立即变为表中的200 * 35M = 700M行。但是在表空间上并不会比您在单个表中所有列所拥有的高很多,因为我们只是将列转置为行。在此转置操作中,实际上我们将没有值为NULL的行。因此,您实际上可以针对该表运行查询,并查看其中有多少个空值,并在实际实现之前估算出目标表的大小。但是在表空间上并不会比您在单个表中所有列所拥有的高很多,因为我们只是将列转置为行。在此转置操作中,实际上我们将没有值为NULL的行。因此,您实际上可以针对该表运行查询,并查看其中有多少个空值,并在实际实现之前估算出目标表的大小。但是在表空间上并不会比您在单个表中所有列所拥有的高很多,因为我们只是将列转置为行。在此转置操作中,实际上我们将没有值为NULL的行。因此,您实际上可以针对该表运行查询,并查看其中有多少个空值,并在实际实现之前估算出目标表的大小。

第二个优点是读取性能。正如您提到的那样,查询数据的新方法是where子句中f1至f200列的任意组合。键值对设计为f1至f200的内容出现在第一栏中,例如“ FildName”,而其值出现在第二栏中,则是“ FieldValue”。您可以在两列上都具有CLUSTERED索引。您的查询将是那些选择的UNION。

在哪里(FiledName ='f1'和FieldValue在5和6之间)

联盟

(FiledName ='f2',并且FieldValue在8和10之间)

等等.....

我会给您一些实际产品服务器的性能数字。我们为每个证券TICKER提供75个价格栏。


1

在处理此类数据时,您需要插入很多行,并且还需要非常好的分析查询性能(我假设这里就是这种情况),您可能会发现列式RDBMS非常适合。看一下Infobright CE和InfiniDB CE(两个插入到MySQL的列式存储引擎),以及Vertica CE(更多类似于PostgreSQL而不是类似MySQL)...所有这些社区版本都是免费的(尽管Vertica并非免费)开源,它确实可以扩展到3个节点和1Tb的数据免费)。列式RDBMS通常提供的“大查询”响应时间比基于行的响应时间快10-100倍,而加载时间则可提高5-50倍。您必须正确使用它们,否则它们会发臭(不要执行单行操作...以批量方式执行所有操作),但是正确使用它们确实会造成麻烦。;-)

HTH,Dave Sisk


1
在3节点Vertica安装中,我们有将近10亿行clickstream类型的数据(与股票报价器数据没有什么不同)……我们可以在15秒钟左右的时间内加载整天的数据,并且得到查询响应时间为500毫秒范围。就您而言,这听起来确实值得一看。
Dave Sisk

我可以保证相同。在我的上一家公司,我们有一个8节点的Vertica集群,该集群具有大约相同的行数,并且在1-3秒(平均)内返回的整个集合中具有简单的聚合查询。这也大约是我们早期Greenplum集群成本的1/4。
bma
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.