MySQL 分区请求缓慢 table

MySQL slow request on partitionned table

我面临着一个完全的谜。

我创建了一个 table 来存储本地气象数据。自 1979 年以来,我每小时有一个值,每 0.25 个纬度和经度。 这使我在数据库中拥有数十亿行。 根据多个建议,我对 table 进行了分区。 我选择按年划分。这是它的样子:

 CREATE TABLE `MyTable` (
  `latitude_100` SMALLINT NOT NULL, -- Smallint is 2 bytes, where float is 4. So we take latitude * 100
  `longitude_100` SMALLINT NOT NULL, -- Same logic here
  `time` DATETIME NOT NULL,
  `final` TINYINT UNSIGNED NOT NULL,
  `value` DOUBLE NOT NULL,
  PRIMARY KEY (`latitude_100` ASC, `longitude_100` ASC, `time` ASC)
)
PARTITION BY HASH(YEAR(time)) PARTITIONS 45 ; -- This will work until 2023 included

为了测试,我只注入了2015年到2021年的table数据

问题: 此 table 中的所有 SELECT 都非常长。

更糟糕的是,它们有时长得愚蠢。 例如:

SELECT time, latitude_100, longitude_100, value 
FROM MyTable 
WHERE  latitude_100 BETWEEN 500 AND 2000 
AND longitude_100 BETWEEN 11600 AND 12800 AND 
YEAR(time) = 1990 ;

请记住,没有 1990 年的数据。通过查看正确的分区,MySQL 应该会立即看到它,不是吗?

MySQL 解释一下它会查看所有分区,我不明白为什么:

EXPLAIN SELECT time, latitude_100, longitude_100, value 
FROM MyTable 
WHERE  latitude_100 BETWEEN 500 AND 2000 
AND longitude_100 BETWEEN 11600 AND 12800 AND 
YEAR(time) = 1990 ;
# id, select_type, table, partitions, type, possible_keys, key, key_len, ref, rows, filtered, Extra
1, SIMPLE, MyTable, p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19,p20,p21,p22,p23,p24,p25,p26,p27,p28,p29,p30,p31,p32,p33,p34,p35,p36,p37,p38,p39,p40,p41,p42,p43,p44, range, PRIMARY, PRIMARY, 4, , 118295536, 11.11, Using where

当我做的时候

SELECT * FROM information_schema.partitions WHERE TABLE_SCHEMA='MySchema' AND TABLE_NAME = 'MyTable' AND PARTITION_NAME IS NOT NULL

我看到只有6个分区有数据,其他都是空的

我最后想到的是用不同的方式来表述 WHERE,也许可以利用索引:

SELECT time, latitude_100, longitude_100, value 
FROM MyTable
WHERE  latitude_100 BETWEEN 500 AND 2000 
AND longitude_100 BETWEEN 11600 AND 12800 AND
time BETWEEN "1990-01-01 00:00:00" AND "1990-12-31 23:00:00" AND 
YEAR(time) = 1990 ;

但这并不能加速执行。只有 EXPLAIN 有点不同(但不是在分区读取方面):

# id, select_type, table, partitions, type, possible_keys, key, key_len, ref, rows, filtered, Extra
1, SIMPLE, MyTable, p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19,p20,p21,p22,p23,p24,p25,p26,p27,p28,p29,p30,p31,p32,p33,p34,p35,p36,p37,p38,p39,p40,p41,p42,p43,p44, range, PRIMARY, PRIMARY, 9, , 118295536, 1.23, Using where

我做错了什么? 为什么MySQL不想配合分区?

非常感谢!

[编辑] 在技​​术方面,数据库托管在 AWS RDS 上。它由“db.t4g.large”实例和用户 MySQL 8.0.27

提供支持

不要使用 PARTITION BY HASH! 在使用日期范围(就像你有的!)时,HASH 将无法执行任何 p运行ing。简而言之,优化器不够智能,无法看到您的范围适合单个分区。此外,HASH 可能不必要地将两个不同的年份归为同一个分区。相反,使用 PARTITION BY RANGE.

我知道 RANGE(TO_DAYS(time)) 有效;也许 RANGE(YEAR(time)) 可以工作,这取决于您使用的 MySQL 的版本;查看详情。

小时: 通过一些日期算法,您可以将 5 字节 DATETIME 缩小为 3 字节 MEDIUMINT。 (需要对 PARTITION BY RANGE 进行适当更改。)

不够: 由于您只使用 7 年的数据进行测试,因此我的分区建议只能提供 7 倍的帮助。

DOUBLE? 你在测量什么? DOUBLE 占用 8 个字节,并为您提供大约 16 位有效数字。即使是 FLOAT(4 个字节,7 个数字)也可能有点矫枉过正。对于温度 (°C),请考虑 DECIMAL(2)TINYINT (-128..+127) 或 DECIMAL(4,2);它们分别是 1,1,2 字节。极端记录:-89..+57。注意:°F 在任何 INTDECIMAL 编码中都需要多一个字节。 (我猜如果温度超过 99°C,仪器太靠近火山或野火将无法传输数据。)

缩小 DOUBLE 会将数据集大小缩小约 1/3——值得付出努力。

如果您最终会得到大约 400GB 的行,数据类型大小非常重要。

所以,让我们深入挖掘...请提供

  • RAM 容量
  • SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
  • 您可能 运行 的任何其他 SELECTs,包括 WHERE 一年以外的条款。
  • 你7年用了多少盘space?如果使用 MyISAM,我预计大约有 1.2TB;如果使用 InnoDB,3TB。
  • 样本 Select 中的 lat/lng 范围相对较小。这是典型的吗?如果是这样,我们也许可以利用它。

ENGINE -- 因为我认为这主要是一个只读数据集,所以 MyISAM 更好的情况可能很少见。见上面的估计;乘以 6 得到 43 年的估计值。

用法 -- 您将如何处理 SELECT 这样的结果?如果那是 'only' 查询,那么有更紧凑的方式来存储数据。但是它们对于 Insert 和 Select 来说会更复杂。然而,速度的提高可能是值得的。在进一步建议之前,我需要查看各种 Select。