【问题标题】:My MySQL procedure is randomly running really slow我的 MySQL 程序随机运行非常慢
【发布时间】:2019-09-19 09:25:21
【问题描述】:

我在 Raspberry Pi 3b+ 上运行 MySQL 数据库,并且正在使用一些存储过程。 如果我运行它们,它们会随机运行,它们真的很慢。

这是我的代码:

DROP procedure IF EXISTS `Zeiterfassung`.`auswertung_jahr`;
DELIMITER $$
CREATE PROCEDURE `Zeiterfassung`.`auswertung_jahr` (IN `var_benutzer_id` INT(11), IN `var_jahr` INT(11))
BEGIN
#Variablen
DECLARE var_letzte_id, var_auswertung_jahr_id, var_sum_arbeitszeit_soll, var_sum_arbeitszeit_ist, var_sum_arbeitszeit_saldo, var_ferien_tage, var_diff_tage, var_arbeitspensum INT DEFAULT 0;
DECLARE var_sum_ferien_ist, var_sum_ferien_soll, var_sum_ferien_saldo, var_sum_krank_saldo, var_ferien_uebertrag, var_arbeitszeit_uebertrag FLOAT DEFAULT 0;
DECLARE var_eintritt, var_austritt, var_datum_von DATE;

#Abfrage, dieser Monat
SELECT auswertung_jahr_id INTO var_auswertung_jahr_id FROM auswertung_jahr WHERE jahr = var_jahr AND benutzer_id = var_benutzer_id;
IF var_auswertung_jahr_id = 0 THEN #wenn Jahr vorhanden
#wenn kein Jahr vorhanden Eintrag erstellen
    INSERT INTO auswertung_jahr (benutzer_id, jahr) VALUES (var_benutzer_id, var_jahr);
    SET var_letzte_id = LAST_INSERT_ID();
ELSE
    SET var_letzte_id = var_auswertung_jahr_id;
END IF;

#Eintritt, Austritt wird gebraucht, um Anteil an Ferien in diesem Jahr zu berechnen.
SELECT
benutzer.eintritt, benutzer.austritt, benutzer.uebertrag_ueberzeit, benutzer.uebertrag_ferien, benutzer.arbeitspensum, benutzer.ferien_tage
INTO
var_eintritt, var_austritt, var_arbeitszeit_uebertrag, var_ferien_uebertrag, var_arbeitspensum, var_ferien_tage
FROM benutzer
WHERE benutzer.benutzer_id = var_benutzer_id;

#Summen für diesen Monat werden ausgerechnet
SELECT
IFNULL( SUM(arbeitszeit_soll), 0 ), IFNULL( SUM(arbeitszeit_ist), 0 ), IFNULL( SUM(ferien_saldo), 0 ), IFNULL( SUM(krank_saldo), 0 )
INTO
var_sum_arbeitszeit_soll, var_sum_arbeitszeit_ist, var_sum_ferien_ist, var_sum_krank_saldo
FROM
auswertung_monat
WHERE benutzer_id = var_benutzer_id AND jahr = var_jahr;

#Ferien SOLL ausrechnen
#Eintritt und Austritt im selben Jahr
IF var_jahr = YEAR(var_eintritt) AND var_jahr = YEAR(var_austritt) THEN
    SELECT DATEDIFF(var_austritt, var_eintritt) INTO var_diff_tage;
    SELECT var_ferien_tage / 365 * (var_diff_tage + 1) INTO var_sum_ferien_soll;
#Nur Eintritt in diesem Jahr
ELSEIF var_jahr = YEAR(var_eintritt) THEN
    #Eintritt
    SELECT CONCAT(var_jahr,'-12-31') INTO var_datum_von;
    SELECT DATEDIFF(var_datum_von, var_eintritt) INTO var_diff_tage;
    SELECT var_ferien_tage / 365 * (var_diff_tage + 1) INTO var_sum_ferien_soll;
#Nur Austritt in diesem Jahr
ELSEIF var_jahr = YEAR(var_austritt) THEN
    SELECT CONCAT(var_jahr,'-01-01') INTO var_datum_von;
    SELECT DATEDIFF(var_austritt, var_datum_von) INTO var_diff_tage;
    SELECT var_ferien_tage / 365 * (var_diff_tage + 1) INTO var_sum_ferien_soll;
#Sonst wurde ganzes Jahr gearbeitet = volle Anzahl Ferien
ELSE
    SELECT var_ferien_tage INTO var_sum_ferien_soll;
END IF;

#Bei Teilzeitarbeit wird das Ferien SOLL noch heruntergerechnet
SET var_sum_ferien_soll = var_sum_ferien_soll / 100 * var_arbeitspensum;

#Nach dem Prozentualen Ferien Ergebnis wird noch der Übertrag dazugerechnet, falls dieses Jahr das Eintrittsjah ist
IF var_jahr = YEAR(var_eintritt) THEN
    #Übertrag von Ferien aus z.B. anderer Software werden dazugerechnet aber nur einmalig (Eintrittsjahr)
    UPDATE
    auswertung_jahr
    SET
    ferien_uebertrag = var_ferien_uebertrag,
    arbeitszeit_uebertrag = var_arbeitszeit_uebertrag * 3600
    WHERE
    auswertung_jahr_id = var_letzte_id;
END IF;

#Ferien SALDO ausrechnen
SET var_sum_ferien_saldo = var_sum_ferien_soll - var_sum_ferien_ist;

#Arbeitszeit SALDO ausrechnen
SET var_sum_arbeitszeit_saldo = var_sum_arbeitszeit_soll - var_sum_arbeitszeit_ist;

#Summen eintragen
UPDATE
auswertung_jahr
SET
arbeitszeit_soll = var_sum_arbeitszeit_soll,
arbeitszeit_ist = var_sum_arbeitszeit_ist,
arbeitszeit_saldo = var_sum_arbeitszeit_saldo,
ferien_ist = var_sum_ferien_ist,
ferien_soll = var_sum_ferien_soll,
ferien_saldo = var_sum_ferien_saldo,
krank_saldo = var_sum_krank_saldo
WHERE
auswertung_jahr_id = var_letzte_id;
END$$

我认为这与代码无关,因为我可以执行该过程,而且大部分速度非常快。 然后,如果我用完全相同的数据重复该过程,它会随机变慢。

这是我用完全相同的数据连续几次执行完全相同的过程的图片:

如果我用 TOP 查看系统负载,则没有其他使用 power..

这里是慢日志的一个例子:

    CALL auswertung_jahr(5,2019);
    # Time: 190430 20:13:43
    # User@Host: root[root] @ localhost []
    # Thread_id: 2  Schema: Zeiterfassung  QC_hit: No
    # Query_time: 3.389289  Lock_time: 0.000000  Rows_sent: 0  Rows_examined: 5
    # Rows_affected: 0
    SET timestamp=1556648023;
    CALL auswertung_jahr(5,2019);
    # Time: 190430 20:13:44
    # User@Host: root[root] @ localhost []
    # Thread_id: 2  Schema: Zeiterfassung  QC_hit: No
    # Query_time: 0.186437  Lock_time: 0.000000  Rows_sent: 0  Rows_examined: 5
    # Rows_affected: 0
    SET timestamp=1556648024;

第一个超过 3 秒,第二个 0.18 秒

我禁用了缓存:

SET SESSION query_cache_type=0;

【问题讨论】:

  • 发布存储过程时,您应该始终包含带有参数的实际 CREATE PROCEDURE。也就是说,在检查这种性能时要考虑的一点是 MySQL 可以缓存查询结果,因此完全相同的查询的连续执行实际上并不总是完全重新处理。
  • 好的,我附上了完整的代码,抱歉。我知道缓存可以有所作为,但 8 秒?我还禁用了缓存以测试没有任何改变..
  • 我会 enable slow query 记录日志并将其设置为 1 秒然后检查它,您可能能够准确地看到您的过程中哪个查询运行缓慢
  • 你提到的时间在哪里?
  • 我从我的慢查询日志中添加了一个示例。

标签: mysql sql performance stored-procedures raspberry-pi


【解决方案1】:

我现在很确定,这种缓慢的性能对于 Raspberry Pi 和 InnoDB 来说是正常的...... 如果我用 MyISAM 表测试相同的插入,它会快得多,并且有些插入突然需要超过 2 秒而不是 0.17 秒。

这里是 InnoDB 的一个例子:

这里是 MyISAM 的一个例子:

差别太大了!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多