MySQL View 比 Select 慢 20 倍

Posted

技术标签:

【中文标题】MySQL View 比 Select 慢 20 倍【英文标题】:MySQL View 20x slower than Select 【发布时间】:2021-07-30 10:11:14 【问题描述】:

我有一个选择约 8000 行的查询。当我执行查询时,它需要 0.1 秒。

当我将查询复制到视图中并执行视图时,大约需要 2 秒。在解释的第一行中,它选择了约 570K 行,我不知道为什么。

我不明白第一行以及为什么它只显示在视图解释中

1 主要所有 NULL NULL NULL NULL

这是查询(是的,我知道我不是 mysql pro 并且查询不是那么有效,但它可以工作,而且 0.1 sek 对我来说没问题。有谁知道为什么它在视图中这么慢?

MariaDB 10.5.9

select
   `xxxxxxx`.`auftraege`.`Zustandigkeit` AS `Zustandigkeit`,
   `xxxxxxx`.`auftraege`.`cms` AS `cms`,
   `xxxxxxx`.`auftraege`.`auftrag_id` AS `auftrag_id`,
   `xxxxxxx`.`angebot`.`angebot_id` AS `angebot_id`,
   `xxxxxxx`.`kunden`.`kunde_id` AS `kid`,
   `xxxxxxx`.`angebot`.`kunde_id` AS `kunde_id`,
   `xxxxxxx`.`kunden`.`firma` AS `firma`,
   `xxxxxxx`.`auftraege`.`gekuendigt` AS `gekuendigt`,
   `xxxxxxx`.`kunden`.`ansprechpartnerVorname` AS `ansprechpartnerVorname`,
   `xxxxxxx`.`kunden`.`ansprechpartner` AS `ansprechpartner`,
   `xxxxxxx`.`auftraege`.`ampstatus` AS `ampstatus`,
   `xxxxxxx`.`auftraege`.`autoMahnungen` AS `autoMahnungen`,
   `xxxxxxx`.`kunden`.`mail` AS `mail`,
   `xxxxxxx`.`kunden`.`ansprechpartnerAnrede` AS `ansprechpartnerAnrede`,
   case
      `xxxxxxx`.`kunden`.`ansprechpartnerAnrede` 
      when
         'm' 
      then
         concat('Herr ', ifnull(`xxxxxxx`.`kunden`.`ansprechpartnerVorname`, ''), ifnull(`xxxxxxx`.`kunden`.`ansprechpartner`, '')) 
      else
         concat('Frau ', ifnull(`xxxxxxx`.`kunden`.`ansprechpartnerVorname`, ''), ifnull(`xxxxxxx`.`kunden`.`ansprechpartner`, '')) 
   end
   AS `ansprechpartnerfullName`, `xxxxxxx`.`kunden`.`website` AS `website`, `xxxxxxx`.`personal`.`name_betrieb` AS `name_betrieb`, `xxxxxxx`.`kunden`.`prioritaet` AS `prioritaet`, `xxxxxxx`.`auftraege`.`infoemail` AS `infoemail`, `xxxxxxx`.`auftraege`.`keywords` AS `keywords`, `xxxxxxx`.`auftraege`.`ftp_h` AS `ftp_h`, `xxxxxxx`.`auftraege`.`ftp_u` AS `ftp_u`, `xxxxxxx`.`auftraege`.`ftp_pw` AS `ftp_pw`, `xxxxxxx`.`auftraege`.`lgi_h` AS `lgi_h`, `xxxxxxx`.`auftraege`.`lgi_u` AS `lgi_u`, `xxxxxxx`.`auftraege`.`lgi_pw` AS `lgi_pw`, `xxxxxxx`.`auftraege`.`autoRemind` AS `autoRemind`, `xxxxxxx`.`kunden`.`telefon` AS `telefon`, `xxxxxxx`.`kunden`.`mobilfunk` AS `mobilfunk`, `xxxxxxx`.`auftraege`.`kommentar` AS `kommentar`, `xxxxxxx`.`auftraege`.`phase` AS `phase`, `xxxxxxx`.`auftraege`.`datum` AS `datum`, `xxxxxxx`.`angebot`.`typ` AS `typ`, 
   case
      `xxxxxxx`.`auftraege`.`gekuendigt` 
      when
         '1' 
      then
         'Ja' 
      else
         'Nein' 
   end
   AS `Gekuendigt ? `, 
   (
      select
         count(`xxxxxxx`.`status`.`aenderung`) 
      from
         `xxxxxxx`.`status` 
      where
         `xxxxxxx`.`status`.`auftrag_id` = `xxxxxxx`.`auftraege`.`auftrag_id`
   )
   AS `aenderungen`,
   `xxxxxxx`.`auftraege`.`vertragStart` AS `vertragStart`,
   `xxxxxxx`.`auftraege`.`vertragEnde` AS `vertragEnde`,
   case
      `xxxxxxx`.`auftraege`.`zahlungsart` 
      when
         'U' 
      then
         'Überweisung' 
      when
         'L' 
      then
         'Lastschrift' 
      else
         'Unbekannt' 
   end
   AS `Zahlungsart`, `xxxxxxx`.`kunden`.`yyyyy_piwik` AS `yyyyy_piwik`, 
   (
      select
         max(`xxxxxxx`.`status`.`datum`) AS `mxDTst` 
      from
         `xxxxxxx`.`status` 
      where
         `xxxxxxx`.`status`.`auftrag_id` = `xxxxxxx`.`auftraege`.`auftrag_id` 
         and `xxxxxxx`.`status`.`typ` = 'SEO'
   )
   AS `mxDTst`,
   (
      select
         case
            `xxxxxxx`.`rechnungen`.`beglichen` 
            when
               'YES' 
            then
               'isOk' 
            else
               'isAffe' 
         end
         AS `neuUwe` 
      from
         (
            `xxxxxxx`.`zahlungsplanneu` 
            join
               `xxxxxxx`.`rechnungen` 
               on(`xxxxxxx`.`zahlungsplanneu`.`rechnungsnummer` = `xxxxxxx`.`rechnungen`.`rechnungsnummer`)
         )
      where
         `xxxxxxx`.`zahlungsplanneu`.`auftrag_id` = `xxxxxxx`.`auftraege`.`auftrag_id` 
         and `xxxxxxx`.`rechnungen`.`beglichen` <> 'STO' limit 1
   )
   AS `neuer`, 
   (
      select
         group_concat(`xxxxxxx`.`kunden_keywords`.`keyword` separator ',') 
      from
         `xxxxxxx`.`kunden_keywords` 
      where
         `xxxxxxx`.`kunden_keywords`.`kunde_id` = `xxxxxxx`.`kunden`.`kunde_id`
   )
   AS `keyword`,
   (
      select
         case
            count(0) 
            when
               0 
            then
               'Cool' 
            else
               'Uncool' 
         end
         AS `AusfallVor` 
      from
         `xxxxxxx`.`rechnungen` 
      where
         `xxxxxxx`.`rechnungen`.`rechnung_tag` < current_timestamp() - interval 15 day 
         and `xxxxxxx`.`rechnungen`.`kunde_id` = `xxxxxxx`.`kunden`.`kunde_id` 
         and `xxxxxxx`.`rechnungen`.`beglichen` = 'NO' limit 1
   )
   AS `Liquidiert` 
from
   (
((((`xxxxxxx`.`auftraege` 
      join
         `xxxxxxx`.`angebot` 
         on(`xxxxxxx`.`auftraege`.`angebot_id` = `xxxxxxx`.`angebot`.`angebot_id`)) 
      join
         `xxxxxxx`.`kunden` 
         on(`xxxxxxx`.`angebot`.`kunde_id` = `xxxxxxx`.`kunden`.`kunde_id`)) 
      left join
         `xxxxxxx`.`kunden_keywords` 
         on(`xxxxxxx`.`angebot`.`kunde_id` = `xxxxxxx`.`kunden_keywords`.`kunde_id`)) 
      join
         `xxxxxxx`.`personal` 
         on(`xxxxxxx`.`kunden`.`bearbeiter` = `xxxxxxx`.`personal`.`personal_id`)) 
      left join
         `xxxxxxx`.`status` 
         on(`xxxxxxx`.`auftraege`.`auftrag_id` = `xxxxxxx`.`status`.`auftrag_id`)
   )
group by
   `xxxxxxx`.`auftraege`.`auftrag_id` 
order by
   NULL

更新 1

1.视图本身(时长 1.83 秒)

1.1 创建视图:这是我创建的视图,它只包含上面的查询。

1.2 执行视图:执行视图需要 1.83 sek

1.3 分析视图:这是视图的解释

2。添加 where 子句的视图(持续时间 1.86 秒)

2.1 使用添加的 where 子句分析视图 @rick 希望我在视图中添加 where 子句,如果我理解正确的话。这是视图的解释,我在其中添加了 where 子句,需要 1.86 秒。

3.查询,即视图的来源(持续时间:0.1 秒)

3.1 直接执行查询 这是查询,即视图的来源,当我直接执行到服务器时。大约需要 0.1 - 0.2 秒。

3.2 分析直接查询这就是纯查询的解释。

为什么视图会这么慢,只在视图内封装查询?

更新 2

这些是我设置的索引 更改表angebot 添加索引angebot_idx_angebot_id (angebot_id);

改变表auftraege添加索引auftraege_idx_auftrag_id (auftrag_id);

ALTER TABLE kunden ADD INDEX kunden_idx_kunde_id (kunde_id);

ALTER TABLE kunden_keywords ADD INDEX kunden_keywords_idx_kunde_id (kunde_id);

改变表personal添加索引personal_idx_personal_id (personal_id);

ALTER TABLE rechnungen ADD INDEX rechnungen_idx_rechnungsnummer_beglichen (rechnungsnummer,beglichen);

ALTER TABLE rechnungen ADD INDEX rechnungen_idx_beglichen_kunde_id_rechnung (beglichen,kunde_id,rechnung_tag);

ALTER TABLE status ADD INDEX status_idx_auftrag_id (auftrag_id);

ALTER TABLE status ADD INDEX status_idx_typ_auftrag_id_datum (typ,auftrag_id,datum);

ALTER TABLE zahlungsplanneu ADD INDEX zahlungsplanneu_idx_auftrag_id (auftrag_id);

【问题讨论】:

***.com/help/minimal-reproducible-example ***.com/questions/62832483/…有些用户和我有同样的问题,有什么办法可以禁用视图只执行查询而不做任何更改的临时算法,因此执行时间与直接查询? 【参考方案1】:

表之间保持一致。例如,kunde_id 在表之间的声明似乎不同。这可能会阻止一些明显的优化。 (在 EXPLAIN 中有 6 个 JOINs 表示 func。)

删除JOINs 中多余的括号。他们可能会阻止优化器做的事情——重新排列 JOIN 中的表格。

把查询翻过来。通过这个,我的意思是做最少的工作来做主要的JOIN。主要收集 id。 然后 在外部选择中执行相关子查询。比如:

SELECT ... ( SELECT ... ), ...
    FROM ( SELECT a1.id
               FROM a AS a1
               JOIN b ON ..
               JOIN c ON .. )
    JOIN a AS a2  ON a2.id = a1.id
    JOIN d  ON ...

“由内而外”的组合可能消除了对GROUP BY 的需要。 (您的查询太复杂,我无法确定。)如果是这样,那么我将问题称为“explode-implode”——您的查询首先 JOIN,生成一个包含很多行的临时表(“explodes”)。然后它执行GROUP BY(“内爆”)。

更多

这些索引可能会有所帮助:

status:  (auftrag_id, typ, datum, aenderung)
rechnungen:  (beglichen, kunde_id, rechnung_tag)
rechnungen:  (rechnungsnummer, beglichen)
zahlungsplanneu:  (auftrag_id, rechnungsnummer)
kunden_keywords:  (kunde_id, keyword)  -- (unless `kunde_id` is the PK)

(我从所有 3 个EXPLAINs 中看到,您可能在kunden_keywordsstatus 上有足够的索引。告诉我您有哪些索引,这样我就可以查看现有索引是否与我的建议一样好。) "使用索引" == "覆盖索引"。

接近尾声的是LEFT JOIN,但我没有发现这张桌子有什么用处;或许可以去掉?

left join  `kunden_keywords` on(`angebot`.`kunde_id` = `kunden_keywords`.`kunde_id`))

【讨论】:

感谢 Rick 的好回答,我会重新安排查询。但是我不明白为什么作为查询执行的查询比视图中的相同查询要快得多。 MySQL 服务器有什么区别?是否有可能加快速度?我将视图创建为 Temp Table 或 Merge,速度没有区别。 @user3236231 - 在 MySQL 中,VIEWs 只是语法糖。它们无法提高性能,而且可能会造成伤害。 是的,这就是为什么我不明白为什么执行查询作为查询(0.1 秒)和将查询保存为视图然后执行它(2.0 秒)之间存在如此大的差异的原因 唉,我还没有发现 SELECT 的“类型”会因为简单地封装在 VIEW 中而失去性能。 可能与应用于VIEWWHERE 子句未能渗透到可以更好地优化的SELECT 有关。请提供 (1) 独立、快速、选择、(2) 视图和 (3) 视图使用(慢速)的 SQL。 感谢 Rick 抽出时间来帮助我。我用你要求的所有陈述更新了帖子。

以上是关于MySQL View 比 Select 慢 20 倍的主要内容,如果未能解决你的问题,请参考以下文章

Google Cloud SQL 很慢:10GB RAM 的 mysql 实例比配置 125MB ram 的 Macbook Pro 慢 20 倍

MySQL 查询调优 - 为啥使用变量中的值比使用文字慢得多?

MySQL性能优化系列select count(*)走二级索引比主键索引快几百倍,你敢信?

Mariadb 5.5 比 MySQL 5.1 慢

MariaDB 10.4.13 性能比 MySQL 5.7.30 慢

“SELECT COUNT(column)”是不是比“SELECT COUNT(*)”快/慢? [复制]