Mysql数据库性能和内存消耗

时间:2014-12-14 10:27:06

标签: mysql database-performance

好日子,伙计们, 我需要改进这个查询:

SELECT `glpi_tickets`.`id`,`glpi_tickets`.`date`,
        `glpi_tickets`.`date_mod`,`glpi_tickets`.`status`, 
        `glpi_tickets`.`users_id_recipient`,`glpi_tickets`.`slas_id`, 
        `glpi_slalevels`.`execution_time`,`glpi_tickets`.`due_date`,
        `glpi_tickets`.`name`,`glpi_tickets`.`content`
    FROM `glpi_tickets` 
    JOIN `glpi_slas` on (`glpi_slas`.`id` = `glpi_tickets`.`slas_id`)
    JOIN `glpi_slalevels` on (`glpi_slas`.`id` = `glpi_slalevels`.`slas_id`) 
    LEFT JOIN `mail_sent` ON (`mail_sent`.`ticket_id` = `glpi_tickets`.`id`)
    WHERE  (`glpi_tickets`.`closedate` IS NULL 
        OR `glpi_tickets`.`solvedate` IS NULL ) 
        AND `glpi_tickets`.`is_deleted` = 0 
        AND `mail_sent`.`ticket_id` IS NULL 

问题在于,当数据库很大,就像很多行一样,那么它需要很多内存。基本上我有这个表:glpi_tickets有一些字段,你可以在查询中看到。我有glpi_slasglpi_slalevels,这两个我真正感兴趣的唯一字段是glpi_slalevelsexecution_time,然后glpi_tickets与{{{}相关1}}到glpi_slalevels。我需要查找是否已知并且是否已经从glpi_slas发送了一封电子邮件。mail_sent。这就对了。但我无法使用内存经济的方式完成它。 作为ticket_id mail_sent已经在表ticket_id中的一个附加细节,我甚至不需要再使用/阅读它...我很容易忘记它&# 39;存在它是一次性任务。 任何的想法?谢谢

编辑: 有没有办法可以在两个或三个中吐出这个查询,以便在内存消耗较少的解决方案中获得相同的结果并获得相同的结果?

1 个答案:

答案 0 :(得分:1)

使用外连接来检查不存在是一个可以导致巨大中间结果的技巧。一个例子:t中有100000条记录,其中99999条记录,每条记录100封,每封邮件发送100封,没有任何mail_sent。您的中间结果包含9999901个连接记录,然后扫描这些记录以查找mail_sent.ticket_id为null的一条记录。 dbms可能会找到一种方法来有效地处理这个问题。你有没有尝试过这种直截了当的方式?您现在想要存在记录,因此您将使用EXISTS子句。

我想在glpi_tickets上会使用全表扫描,因为OR条件不容易用索引快速处理,并且dbms很难估计可能会影响多少条记录。但是,如果glpi_tickets中有很多记录,但只有很小比例的未闭合未删除的未删除记录,那么希望使用索引。所需的索引是(is_deleted,closedate,solveate)或(is_deleted,solveate,closedate)。 (不幸的是,MySQL中没有函数索引可以在这里创建一个非常小的索引。)你甚至可以强制使用USE INDEX。

(我理所当然地认为您的表是使用主键和外键正确创建的,因此您有连接的索引。)

select 
  t.id,
  t.date,
  t.date_mod,
  t.status,
  t.users_id_recipient,
  t.slas_id,
  sl.execution_time,
  t.due_date,
  t.name,
  t.content
from glpi_tickets t 
join glpi_slas s on s.id = t.slas_id
join glpi_slalevels sl on s.id = sl.slas_id
where (t.closedate is null or t.solvedate is null) 
and t.is_deleted = 0 
and not exists
(
  select *
  from mail_sent
  where mail_sent.ticket_id = t.id
);

如果索引有帮助,但您仍然需要更快,那么您可以创建一个额外的列is_open,您可以在before-insert / before-update触发器中计算它。 E.g:

CREATE TRIGGER trg_insert_glpi_tickets BEFORE INSERT ON glpi_tickets 
  FOR EACH ROW BEGIN
    SET NEW.is_open = (NEW.closedate is null or NEW.solvedate is null) and NEW.is_deleted = 0;
  END;

然后在该字段上创建索引并将查询重新写入where is_open = 1。这将是一个非常小,高效的索引,访问速度应该非常快。