如何使用group by,where和order by order

时间:2017-04-11 11:11:48

标签: mysql performance indexing myisam database-partitioning

我正在尝试在一个巨大的MySQL-Table(超过38.700.000行)上加速我的查询,但我无法弄清楚要做什么。我阅读了几十篇相关的文章,甚至还搞乱了MySQL文档。 我承认,我需要帮助!

这是我目前的表格结构:

    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) */;

字段 **数据** ** datumuhrzeit ** 是Unix-Timestamps。我认为它们比 datetime 更快。

正如您所看到的,我有很多分区(当我选择较小的句点时,它们会加快查询速度)和一些测试索引。他们帮助不大。

我读了很多关于多列索引中的列应该是正确的顺序(因为它们在查询中使用)。我尝试使用索引 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 行以获得更快的结果。

还有什么想法吗?

1 个答案:

答案 0 :(得分:0)

   KEY `test1` (`ztyp`,`storno`,`ma`),
   KEY `test2` (`ztyp`,`storno`),

第二个是多余的,因为它是第一个的前缀,放弃它。

切换到InnoDB 提供PRIMARY KEY。 MyISAM将在下一个版本中消失。

stornoTINYINT更改为ENUM不会影响表格大小或查询效果。但是,更改为NULL可能会产生很小的负面影响。

48个分区可能将查询放慢一些,而不是加快速度。您已拥有“完美”索引:(ztypstornodatumuhrzeit)。

“526607行206秒” - 但是要扫描多少行来执行子查询?我想,是代价高昂的部分。事实上,为什么不把子查询拉出来,把它计算在内;那么如果它是总数的大部分,那就让我们专注于它。

但是,够了。可能这个就是答案......

构建并维护摘要表。

它可能会,也许......

PRIMARY KEY(salon, ztyp, storno, month)
SUM(zart) AS sum_zart
SUM(...)  etc

month datumuhrzeit截断到月份(或一天或任何有意义的事情)。 想一想这样的表是否会让你运行子查询。

More on Summary Tables

这可能会以10倍的速度运行内部查询。