【发布时间】:2017-09-06 17:50:31
【问题描述】:
我正在尝试加快对大型 MySQL 表(超过 ~38.700.000 行)的查询,但我不知道该怎么做。我阅读了几十篇相关的文章,甚至对 MySQL-Documentation 感到困惑。 我承认,我需要帮助!
这是我当前的表结构:
CREATE TABLE `bz_all_part` (
`salon` smallint(6) unsigned NOT NULL DEFAULT '0',
`datum` int(10) unsigned NOT NULL DEFAULT '936868149',
`datumuhrzeit` int(10) unsigned NOT NULL DEFAULT '936868149',
`ma` mediumint(6) unsigned NOT NULL DEFAULT '0',
`ztyp` int(10) unsigned NOT NULL DEFAULT '0',
`zart` int(10) unsigned NOT NULL DEFAULT '0',
`storno` tinyint(1) unsigned NOT NULL DEFAULT '0',
`betrag` int(10) NOT NULL DEFAULT '0',
`betragorig` int(10) NOT NULL DEFAULT '0',
KEY `test1` (`ztyp`,`storno`,`ma`),
KEY `test2` (`ztyp`,`storno`),
KEY `test3` (`ma`),
KEY `test4` (`ztyp`,`datum`),
KEY `test5` (`datumuhrzeit`,`ztyp`,`storno`),
KEY `test6` (`ma`,`ztyp`,`datumuhrzeit`),
KEY `test7` (`ztyp`,`storno`,`datumuhrzeit`),
KEY `test8` (`ztyp`,`storno`,`datumuhrzeit`,`zart`),
KEY `test9` (`salon`,`ma`,`zart`,`ztyp`,`storno`,`datumuhrzeit`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
/*!50100 PARTITION BY RANGE (datumuhrzeit)
(PARTITION s0 VALUES LESS THAN (1391210639) ENGINE = MyISAM,
PARTITION s1 VALUES LESS THAN (1393889039) ENGINE = MyISAM,
...
PARTITION s47 VALUES LESS THAN (1514762639) ENGINE = MyISAM) */;
**datum** 和 **datumuhrzeit** 字段是 Unix 时间戳。我认为它们比 datetime 快。
如您所见,我有很多分区(当我选择较小的时段时,它们会加快查询速度)和一些测试索引。他们没有多大帮助。
我读了很多关于多列索引中的列应该按照正确的顺序(因为它们在查询中使用)。我用 Index test7 和 test9 试过这个,但我没有运气。
这是我使用的查询:
SELECT
*,
ROUND(betrag / anzBelege, 2) as umsJeBeleg,
ROUND(betragorig / anzBelege, 2) as umsJeBelegOrig
FROM
(SELECT
count(distinct concat(salon, datumuhrzeit)) as anzBelege,
SUM(zart) as anzServices,
SUM(ROUND(betrag / 100, 2)) as betrag,
SUM(ROUND(betragorig / 100, 2)) as betragorig
FROM
bz_all_part
WHERE
ztyp = 0
AND
storno = 0
AND
datumuhrzeit >= unix_timestamp("2013-01-01 00:00:00")
AND
datumuhrzeit <= unix_timestamp("2018-07-01 00:00:00")
GROUP BY
salon,
ma,
zart) core
当我运行此查询时,结果包含 526607 行,大约需要 206 秒。
解释输出:
id|select_type|table |type|possible_keys |key |key_len|ref |rows |Extra
--|-----------|-----------|----|-----------------|-----|-------|-----------|--------|-----
1 |PRIMARY |<derived2> |ALL |NULL |NULL |NULL |NULL |22955178|NULL
2 |DERIVED |bz_all_part|ref |test1,2,4,5,7,8,9|test2|5 |const,const|22955178|Using where; Using filesort
可能的键 包含索引 test7 和 test9,但它不使用它们,而是使用 test2。我不知道为什么!
非常感谢有关此主题的一些帮助。
非常感谢!
附加信息:
- 服务器不是最快的(它只是一个测试服务器)
- 我们预计每年增长约 15.000.000 行
- 上述查询将是几十个查询之一(它们都完全不同,用于获取统计信息)
编辑 1:
输出:
SELECT * FROM bz_all_part PROCEDURE ANALYSE();
Field_name |Min_value| Max_value|Min_length|Max_length|Empties_or_zeros|Nulls|Avg_value_or_avg_length|Std |Optimal_fieldtype
----------------------------|---------|----------|----------|----------|----------------|-----|-----------------------|-----------|-----------------------------
db.bz_all_part.salon |0 |60974 |1 |5 |1 |0 |13357.9473 |17056.3346 |SMALLINT(5) UNSIGNED NOT NULL
db.bz_all_part.datum |0 |1490911200|1 |10 |1 |0 |1440703052.6328 |427695.2284|INT(10) UNSIGNED NOT NULL
db.bz_all_part.datumuhrzeit |0 |1490989761|1 |10 |1 |0 |1440755042.1115 |460817.3074|INT(10) UNSIGNED NOT NULL
db.bz_all_part.ma |0 |60127 |1 |5 |43 |0 |1166.8595 |7840.3516 |SMALLINT(5) UNSIGNED NOT NULL
db.bz_all_part.ztyp |0 |40181 |1 |5 |26257963 |0 |1140.0327 |4650.5316 |SMALLINT(5) UNSIGNED NOT NULL
db.bz_all_part.zart |0 |55158 |1 |5 |1 |0 |3338.9947 |8165.2681 |SMALLINT(5) UNSIGNED NOT NULL
db.bz_all_part.storno |0 |2 |1 |1 |38604746 |0 |0.0074 |0.1111 |ENUM('0','1','2') NOT NULL
db.bz_all_part.betrag |-45240 |269900 |1 |6 |2123326 |0 |1438.2915 |1916.8851 |MEDIUMINT(6) NOT NULL
db.bz_all_part.betragorig |-45240 |1711250 |1 |7 |2546610 |0 |1491.6800 |2089.4486 |MEDIUMINT(7) NOT NULL
编辑 2:
CREATE TABLE `bz_all_part2` (
`salon` smallint(6) unsigned NOT NULL DEFAULT '0',
`datum` timestamp NOT NULL,
`datumuhrzeit` timestamp NOT NULL,
`ma` mediumint(6) unsigned NOT NULL DEFAULT '0',
`ztyp` int(10) unsigned NOT NULL DEFAULT '0',
`zart` int(10) unsigned NOT NULL DEFAULT '0',
`storno` enum('0','1','2') DEFAULT NULL,
`betrag` mediumint(7) NOT NULL DEFAULT '0',
`betragorig` mediumint(7) NOT NULL DEFAULT '0',
KEY `test1` (`ztyp`,`storno`,`datum`,`zart`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
新的解释输出:
id|select_type|table |type|possible_keys |key |key_len|ref |rows |Extra
--|-----------|-----------|----|-----------------|-----|-------|-----------|--------|-----
1 |PRIMARY |<derived2> |ALL |NULL |NULL |NULL |NULL |2243987 |NULL
2 |DERIVED |bz_all_part|ref |test1 |test1|6 |const,const|2243987 |Using index condition; Using where; Using filesort
输出:
SELECT * FROM bz_all_part PROCEDURE ANALYSE();
和上面差不多
我将表格缩小到 5.000.000 行以获得更快的结果。
还有什么想法吗?
【问题讨论】:
-
您的索引过多。您使用依赖于 HDD 的 MyISAM。 InnoDB 能够将工作数据存储在 RAM 中,从而更快地执行 I/O 操作。
TIMESTAMP数据类型在内部存储一个整数,但它会将值显示为DATETIME- 如果int,您应该使用它来代替。添加索引不会使查询更快,如果像您的情况一样,有很多满足条件的记录(在 WHERE 子句或 JOIN/子查询等中)。您的日期范围在 2013 年到 2018 年之间 - 可能会有很多记录满足该条件。 -
你能把 SELECT * FROM bz_all_part PROCEDURE ANALYSE(); 的输出发给我们吗?这可能需要一些时间。键 test2、test7 和 test8 也相同。你只使用test8。另外我不确定您是否使用最好的存储引擎。 MyISAM 在写入方面表现不佳
-
@BerndBuffen:我已经编辑了我的问题。
-
@Mjh:我将根据您的建议更改我的表格。这需要一些时间。我会回复你的。
-
您可以做一些事情。但是,这些取决于表格的使用(即使在写入时)。它从 STORAGE ENGINE 开始,遍历索引中字段的顺序,还可以优化查询。如果你喜欢你可以很高兴地给我打电话,然后我们可以讨论这些事情并只发布结果。我今天从大约 18:00 时钟有空
标签: mysql performance indexing myisam database-partitioning