[英]MySQL slow request on partitionned table
我面临着一个完全的谜团。
我已经创建了一个表来存储 meterolocal 数据。 自 1979 年以来,我每小时有一个值,每 0.25 个纬度和经度。 这使我在数据库中拥有数十亿行。 根据多项建议,我对表进行了分区。 我选择按年划分。 这是它的样子:
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年的表数据。
问题:该表中的所有 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 在使用日期范围(如您所用)时将无法进行任何修剪。 优化器不够聪明,无法看到您的范围适合单个分区,此外, HASH
可能不必要地将两个不同的年份归为同一个分区。 相反,使用PARTITION BY RANGE
。
我知道RANGE(TO_DAYS(time))
有效; 也许RANGE(YEAR(time))
可能有效,具体取决于您使用的 MySQL 的版本; 检查细节。
小时:通过一些日期算法,您可以将 5 字节的DATETIME
缩减为 3 字节的MEDIUMINT
。 (需要对PARTITION BY RANGE
进行适当的更改。)
不够:由于您仅使用 7 年的数据进行测试,因此我的分区建议只能提供 7 倍的帮助。
双倍的? 你在测量什么? DOUBLE
占用 8 个字节,并为您提供大约 16 个有效数字。 即使是FLOAT
(4 字节,7 位数字)也可能有点矫枉过正。 对于温度 (°C),请考虑DECIMAL(2)
或TINYINT
(-128..+127) 或DECIMAL(4,2)
; 它们分别是 1,1,2 字节。 极端记录:-89..+57。 注意:°F 在任何INT
或DECIMAL
编码中都需要多一个字节。 (我猜如果温度超过 99°C,那么太靠近火山或野火的仪器将无法传输数据。)
缩小DOUBLE
会将数据集大小缩小大约 1/3——值得付出努力。
如果您最终得到大约 400GB 的行,数据类型的大小就非常重要。
所以,让我们深入挖掘......请提供
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
SELECTs
,包括恰好一年以外的WHERE
子句。ENGINE——因为我认为这主要是一个只读数据集,所以 MyISAM 更好的情况可能很少见。 见上面的估计; 乘以 6 得到 43 年的估计值。
用法——你将如何处理SELECT
的结果? 如果那是“唯一”的查询,那么有更紧凑的方式来存储数据。 但是它们对于 Insert 和 Select 会更复杂。但是,速度的提高可能是值得的。 在进一步建议之前,我需要查看各种选择。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.