我有一个 Mysql 表,其架构如下:
CREATE TABLE `historical_pricing` (
`date` date DEFAULT NULL,
`company` varchar(12) DEFAULT NULL,
`price` double(20,5) DEFAULT NULL,
`vol` double DEFAULT NULL,
`id` varchar(25) NOT NULL,
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `date_idx` (`date`),
KEY `company_idx` (`company`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
已经加载了大约 12GB 的数据,并且在性能合理的计算机上运行查询以获取不同的代码不会在 15 分钟内完成。
我有几个查找的微服务:
- 我们在
SELECT distinct(company) from historical_pricing
上有数据的所有公司列表 - 我们拥有的每家公司的所有定价数据
SELECT * FROM historical_pricing where company = 'AAPL' ORDER BY date desc
我通读了 MySQL 5.7 文档,我认为我可以通过按公司名称1) 分区和2) 设置另一个索引来加快查询速度日期。有一个8192 partition limit in MySQL 5.7 ,所以我考虑使用基于起始名称的分区:a%
、b%
、1%
、2%
等
我想弄清楚:
添加分区的正确 MySQL ALTER TABLE 语法是什么?我一直无法弄清楚这是一个列表、范围等。
为 company_date_idx 做一个索引是否有意义(公司优先, 然后是日期),还是一个 date_idx 就足够了,因为索引将在每个分区内创建?
有没有更有效的方法来优化我的查询?
编辑::
我还有另一个做公司、日期的索引作为多列索引。我注意到,无论如何,每次查找都需要重新运行整个表。如果我应该扩大我的 innodb_buffer_pool_size
,我运行了以下查询:
SELECT engine,
count(*) as TABLES,
concat(round(sum(table_rows)/1000000,2),'M') rows,
concat(round(sum(data_length)/(1024*1024*1024),2),'G') DATA,
concat(round(sum(index_length)/(1024*1024*1024),2),'G') idx,
concat(round(sum(data_length+index_length)/(1024*1024*1024),2),'G') total_size,
round(sum(index_length)/sum(data_length),2) idxfrac
FROM information_schema.TABLES
WHERE table_schema not in ('mysql', 'performance_schema', 'information_schema')
GROUP BY engine
ORDER BY sum(data_length+index_length) DESC LIMIT 10;
结果:
Engine Table rows data idx total_size idxfrac
InnoDB 9 288.85M 36.28G 58.59G 94.87G 1.61
机器只有3.5GB内存,设置1GB用于Mysql
最佳答案
- 保留股票代码。 (它是什么??
id
或company
??) - 抛出
id
或company
中的另一个。 - 构建另一个公司表——股票代码、公司名称等。这将第一个查询解决了几个数量级。
- 更改为
PRIMARY KEY(ticker, date)
这将比简单的INDEX(ticker, date)
好几个数量级。为什么?因为第二个查询的所有行都彼此相邻。也就是说,您不会为SELECT *
在磁盘上来回跳动。
但是,你应该到此为止。
缩小数据类型有助于避免代价高昂的 I/O...
DOUBLE(m,n)
不好。我希望他们能摆脱这种语法。它导致两次舍入。要么只说DOUBLE
,要么用DECIMAL
做点什么。price
...感谢 BRK,小数点左边至少需要 6 位数字。感谢“一分钱”或几近退市的股票,你需要几个小数位。也许DECIMAL(12, 6)
可以吗?这需要 6 个字节(与DOUBLE
的 8 个字节相比)。- 对于
volume
...一些指数基金可以超过40亿,所以INT UNSIGNED
是不够的。也许您需要一个 8 字节的DOUBLE
,或者使用 4 字节的FLOAT
会损失一些精度。或者使用一些DECIMAL
。 - 抛出
created_at
和updated_at
—— 它们本质上是无用的。它们占用 10 个字节。
其他查询呢?特别是,您每天将为每个代码添加一个新行??我建议的 PK 会起作用,尽管速度很慢。而且,由于您有整晚的时间来执行插入操作,所以我们不要添加其他索引。
如果您有任何其他疑问,那么我们可以考虑PARTITIONs
。到目前为止,分区只会减慢速度。
不用担心“向后索引扫描”。查询的主要成本是 I/O,我已经解决了这个问题。
请注意您的每个查询现在如何需要触及某些表中的连续行。这与扫描一个巨大的表(您的查询 1)或在索引和数据之间跳来跳去(查询 2)相反。因此,无论缓存有多“冷”,我的设计对于这两个查询都会快得多。
附言。不需要二级索引。
关于mysql - 围绕唯一数据对 MySQL 表进行分区,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48054406/