本地MySQL查询和生产服务器查询执行时间之间的差异

时间:2018-12-02 17:28:34

标签: mysql query-optimization query-performance mysql-slow-query-log

我正在建立一个使用大量数据库查询的网站,所以我有点担心这种情况会发生。

所以,这里的问题是,我有几个使用大量JOIN的查询,一些表有几千个条目,而另一些表有约200-300,000个条目。我曾经历过网站变慢的经历,不得不优化一些查询。

问题是,在这种情况下,在我的本地计算机上,使用那些查询的特定部分需要花费大约2.5秒的时间来加载,并将网络限制启用为常规wi-fi。有了良好的Wi-Fi,加载大约需要1.3秒。

在作为DigitalOcean上的虚拟机的生产服务器上,大约需要5分钟!!使用完全相同的查询加载完全相同的内容。现在我已经不是专家了,但是我的计算机没有比DigitalOcean上的生产服务器快120倍。

我的笔记本电脑具有以下规格:英特尔酷睿i7-6700 HQ,16 GB DDR4 RAM,并且服务器在5400 RPM HDD上运行,它甚至不在我的SSD驱动器上,而这仅是MySQL引擎所在的位置。 / p>

生产服务器最初是具有1GB RAM和1个VCPU的基本DO实例。我以为它可能需要增强,所以我暂时对其进行了升级,使其具有2VCPU和2 GB的RAM,但这没什么区别。除了使用大量连接的那一部分外,其他部分的加载速度非常快。

现在,我还不是专家,但是我的计算机的运行速度并不比服务器快120倍,并且它还可以运行许多其他进程。我确实装有GeForce 1070M,但我认为这不会影响mysql的性能。

我尝试将查询尽可能少地JOIN中分离,然后执行多个简单查询以将附加信息添加到我的信息数组中,但是然后遇到了另一个问题。即使在我的计算机上也具有这种逻辑,卡住了大约4-5秒钟,然后突然加载了内容。

下面是Chrome的网络标签的屏幕截图,其中显示了时间差异。如您所见,除初始负载外,其他所有负载都非常快。我很确定这是一个MySQL问题,但是区别是惊人的。我正在考虑尝试将站点加载到DigitalOcean上具有6VCPU的16GB内存实例上,以查看其是否与内存/ cpu有关,但是我不确定我的客户愿意为这种VM每月支付80美元或以上

我正在考虑的一种可能的解决方案是将LocalidadesAsentamientos表(它们都有大约200-300k条目)划分为32个较小的表,每个表针对墨西哥州,并且有一个每个状态都有一个特殊功能来引用另一个表,但是我认为这既不是可扩展的,也不是好的做法。

我还在下面添加了查询的计算成本。

我的本​​地计算机具有:

  • Windows 10 1803
  • Apache / 2.4.25(Win64)
  • MySQL 5.7.23

我的生产服务器具有:

  • Ubuntu 18.04.1 LTS
  • Apache / 2.4.29(Ubuntu)
  • 5.7.24-0ubuntu0.18.04.1

有什么办法可以解决这个问题吗?

生成的查询如下:

SELECT 
    `Propiedades`.*,
    `Propiedades`.`directorio` AS `main_dir`,
    DATEDIFF(Propiedades.fecha_finalizacion,
            '2018-12-02 11:11:49') AS quedan,
    `OperacionesPorPropiedad`.*,
    `Operaciones`.`nombre_operacion`,
    `Operaciones`.`nombre_operacion_slug`,
    `TiposDePropiedades`.*,
    `FotografiasPorPropiedad`.*,
    `Empresas`.`nombre_empresa`,
    `Estados`.*,
    `Municipios`.*,
    `Localidades`.*,
    `Asentamientos`.*,
    `Clientes`.`nombres`,
    `Clientes`.`apellidos`,
    `Clientes`.`email`,
    `TiposDeClientes`.*
FROM
    `Propiedades`
        JOIN
    `OperacionesPorPropiedad` ON `OperacionesPorPropiedad`.`id_propiedad` = `Propiedades`.`id_propiedad`
        JOIN
    `Operaciones` ON (`Operaciones`.`id_operacion` = `OperacionesPorPropiedad`.`id_operacion`
        AND `OperacionesPorPropiedad`.`id_propiedad` = Propiedades.id_propiedad)
        JOIN
    `TiposDePropiedades` ON `TiposDePropiedades`.`id_tipo` = `Propiedades`.`id_tipo`
        JOIN
    `FotografiasPorPropiedad` ON (`FotografiasPorPropiedad`.`id_propiedad` = `Propiedades`.`id_propiedad`
        AND `FotografiasPorPropiedad`.`orden` = 1)
        JOIN
    `Empresas` ON `Empresas`.`id_empresa` = `Propiedades`.`id_empresa`
        JOIN
    `Estados` ON `Estados`.`id_estado` = `Propiedades`.`id_estado`
        LEFT OUTER JOIN
    `Municipios` ON `Municipios`.`id_municipio` = `Propiedades`.`id_municipio`
        LEFT OUTER JOIN
    `Localidades` ON `Localidades`.`id_localidad` = `Propiedades`.`id_localidad`
        LEFT OUTER JOIN
    `Asentamientos` ON `Asentamientos`.`id_asentamiento` = `Propiedades`.`id_asentamiento`
        JOIN
    `Clientes` ON `Clientes`.`id_cliente` = `Empresas`.`id_cliente`
        JOIN
    `TiposDeClientes` ON (`Clientes`.`id_tipo_cliente` = `TiposDeClientes`.`id_tipo_cliente`
        AND `Clientes`.`id_cliente` = `Empresas`.`id_cliente`)
WHERE
    `Propiedades`.`id_estatus_propiedad` = 1
GROUP BY `Propiedades`.`id_propiedad`
ORDER BY FIELD(`Propiedades`.`destacada`, '1', '0') , FIELD(`Clientes`.`id_tipo_cliente`, 1, 2, 3) , RAND()
LIMIT 24

Query cost

This is my local benchmark with throttling enabled and cache disabled

This is my remote benchmark with cache disabled

2 个答案:

答案 0 :(得分:1)

很抱歉让您花费时间...这是一个菜鸟错误,其中导入数据库时​​我没有阅读错误消息。

当我生成mysqldump时,某些表名使用小写字母错误地生成,并且在导入时导致错误。

由于所有内容的索引都在错误的指令之后,所以它们从未执行过,所以我基本上进行了非索引的全表扫描,这就是为什么要永远加载结果的原因。

我更正了我的SQL文件,并再次创建了数据库,它的工作原理很吸引人。抱歉浪费您的时间。

PS:实际上,我将服务器提升到16GB的RAM和6VCPU,这没有任何区别。

答案 1 :(得分:0)

这会给您合理的24行吗?还是依赖其他表的筛选?

    WHERE  P.`id_estatus_propiedad` = 1
    ORDER BY  FIELD(P.`destacada`, '1', '0') ,
              FIELD(C.`id_tipo_cliente`, 1, 2, 3) ,
              RAND()
    LIMIT  24

如果是,请考虑以下因素:

您当前的查询是从表的很多中查找整行,然后对它们进行改组,最后只提供24条。

更好的方法是先找出其中的24个,然后再 详细了解:

SELECT lots-of-stuff
    FROM ( SELECT id_propiedad
               FROM Propiedades AS P1
               JOIN ...   -- as few as needed to get to Clientes
               JOIN  `Clientes` AS C1  ON C1.`id_cliente` = Em.`id_cliente`
               WHERE  P1.`id_estatus_propiedad` = 1
               ORDER BY  FIELD(P1.`destacada`, '1', '0') ,
                         FIELD(C1.`id_tipo_cliente`, 1, 2, 3) ,
                         RAND()
               LIMIT  24
         ) AS x
    JOIN  `Propiedades` AS P  ON P.id_propiedad = x.id_propiedad
    JOIN  `OperacionesPorPropiedad` AS OP  ON OP.`id_propiedad` = P.`id_propiedad`
    JOIN  `Operaciones` AS O  ON (O.`id_operacion` = OP.`id_operacion` ...
    ...
    -- no WHERE, GROUP BY, or LIMIT, but repeat the ORDER BY:
    ORDER BY  FIELD(P.`destacada`, '1', '0') ,
              FIELD(C.`id_tipo_cliente`, 1, 2, 3) , RAND()

回到性能差异的问题...

  • 您的个人计算机对innodb_buffer_pool_size的价值大于云中的小型VM?
  • 您正在从大约一打表格的许多行中获取所有列。
  • (当前)您正在(目前)首先收集潜在输出行的质量,然后使用GROUP BY消除重复,最后使用LIMITing减少到24。临时的大小桌子可能很大。 (JOINGROUP BY的“充气-放气”综合症。
  • 您可能在某些TEXT列列表中有*列;这会加剧临时表的问题。

这些组合会导致快速/缓慢的性能。我的建议(如果可行)消除了大部分建议。

FotografiasPorPropiedad也需要INDEX(id_propiedad, orden)(以任意顺序)。