我有一个带有架构的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%
等。
我想弄明白:
编辑::
我还有另一个做公司的索引,日期为多列索引。我注意到的是,无论如何,每次查找都需要重新运行整个表。如果我应该放大我的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)之间徘徊相反。因此,无论缓存有多“冷”,我的设计对于两个查询都会快得多。
PS。不需要二级索引。
分区不如使用索引灵活。例如,请记住https://dev.mysql.com/doc/refman/5.7/en/partitioning-limitations-partitioning-keys-unique-keys.html上记录的规则:
“表中的每个唯一键必须使用表的分区表达式中的每一列”
这意味着您的分区不能使用company列,因为该列不是表的主键的一部分。
mysql> ALTER TABLE historical_pricing PARTITION BY KEY(company) PARTITIONS 37;
ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table's partitioning function
相反,我建议你创建一个复合索引。这取代了company
上的单列索引。
ALTER TABLE historical_pricing
DROP KEY company_idx, ADD KEY (company, date);
这有助于优化两个查询:
mysql> EXPLAIN SELECT DISTINCT company FROM historical_pricing;
+----+-------------+--------------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| 1 | SIMPLE | historical_pricing | NULL | index | company | company | 19 | NULL | 1 | 100.00 | Using index |
+----+-------------+--------------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
mysql> EXPLAIN SELECT * FROM historical_pricing WHERE company = 'AAPL' ORDER BY date DESC;
+----+-------------+--------------------+------------+------+---------------+---------+---------+-------+------+----------+----------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------------+------------+------+---------------+---------+---------+-------+------+----------+----------------------------------+
| 1 | SIMPLE | historical_pricing | NULL | ref | company | company | 15 | const | 1 | 100.00 | Using where; Backward index scan |
+----+-------------+--------------------+------------+------+---------------+---------+---------+-------+------+----------+----------------------------------+
(注意“后向索引扫描”是MySQL 8.0中的优化.MySQL 5.7也可以向后扫描索引,但成本较高。请参阅https://mysqlserverteam.com/mysql-8-0-labs-descending-indexes-in-mysql/)