mysql - 围绕唯一数据对 MySQL 表进行分区

标签 mysql sql database optimization

我有一个 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

最佳答案

  • 保留股票代码。 (它是什么??idcompany??)
  • 抛出 idcompany 中的另一个。
  • 构建另一个公司表——股票代码、公司名称等。这将第一个查询解决了几个数量级。
  • 更改为 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_atupdated_at —— 它们本质上是无用的。它们占用 10 个字节。

其他查询呢?特别是,您每天将为每个代码添加一个新行??我建议的 PK 会起作用,尽管速度很慢。而且,由于您有整晚的时间来执行插入操作,所以我们不要添加其他索引。

如果您有任何其他疑问,那么我们可以考虑PARTITIONs。到目前为止,分区只会减慢速度。

不用担心“向后索引扫描”。查询的主要成本是 I/O,我已经解决了这个问题。

请注意您的每个查询现在如何需要触及某些表中的连续行。这与扫描一个巨大的表(您的查询 1)或在索引和数据之间跳来跳去(查询 2)相反。因此,无论缓存有多“冷”,我的设计对于这两个查询都会快得多。

附言。不需要二级索引。

关于mysql - 围绕唯一数据对 MySQL 表进行分区,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48054406/

相关文章:

SQLite 按字符串中的最后一个字符对行进行排序

sql-server - 如何在 SQL Server 2012 中将数据类型从 date 更改为 int?

mysql - 我使用的是MySql数据库 我们可以获取最后发生死锁的信息吗?

sql - Oracle中触发器语句的编译器错误

c# - C# 中的 Firebird 连接

mysql - 如何使用Left Join和子查询修复SQL查询?

jquery - FullCalendar使用jquery从url获取事件

javascript - 使用Javascript(Ajax)将大量小请求传递给PHP(登录mysql)

mysql - MySQL 嵌套循环中的多个游标

php - 使用帐户获取/解析数据库数据