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

Difference between local MySQL query and production server query execution times

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

所以,这里的问题是,我有几个使用大量 JOIN 的查询,其中一些 table 有几千个条目,而其他一些则有大约 200-30 万个条目。我有过网站变慢的经历,我不得不优化一些查询。

问题是,在这种情况下,在我的本地计算机上,使用这些查询的特定部分需要大约 2.5 秒才能加载,并且启用网络节流作为常规 wi-fi。使用 Good Wi-Fi 加载大约需要 1.3 秒。

在我的生产服务器上,它是 DigitalOcean 上的一个虚拟机,它需要大约 5 分钟! 使用完全相同的查询加载完全相同的内容。现在我不是专家,但我的电脑比 DigitalOcean 上的生产服务器快 120 倍。

我的笔记本电脑具有以下规格:Intel Core i7-6700 HQ,16 GB DDR4 RAM,服务器 运行 在 5400 RPM HDD 上,它甚至不在我的 SSD 驱动器上,那只是MySQL 引擎是。

生产服务器最初是一个具有 1GB RAM 和 1 个 VCPU 的基本 DO 实例。我认为它可能需要一些提升,所以我临时将它升级为具有 2 个 VCPU 和 2 GB 的 RAM,但这没有任何区别。其他部分加载速度非常快,除了使用大量连接的部分。

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

我尝试将查询分隔成尽可能少的 JOIN 秒,然后执行多个简单查询以将附加信息添加到我的信息数组中,但后来我遇到了一个不同的问题。即使在我的电脑上也有这种逻辑,它卡住了大约 4-5 秒,然后突然加载了内容。

下面是 Chrome 的网络选项卡的屏幕截图,显示了时间差异。正如您所看到的,除了初始加载之外,其他所有内容都加载得非常快。我很确定这是一个 MySQL 问题,但差异是惊人的。我正在考虑尝试在 DigitalOcean 上使用 6VCPU 的 16GB 内存实例加载网站,看看它是否与 memory/cpu 相关,但我不确定我的客户是否愿意每月支付 80 美元或更多虚拟机。

我想到的一个可能的解决方案是将 LocalidadesAsentamientos table(它们都有大约 200-300k 个条目)分成 32 个较小的 tables,一个代表墨西哥的每个州,每个州都有一个特殊的功能来引用另一个州 table,但我认为这既不是可扩展的也不是好的做法。

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

我的本地计算机有:

我的生产服务器有:

知道我能做些什么来解决这个问题吗?

生成的查询如下:

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

抱歉,大家耽误了时间...这是一个新手错误,我在导入数据库时​​没有阅读错误消息。

当我生成 mysqldump 时,一些 table 名称被错误地生成为仅包含小写字母,导致导入时出错。

由于所有的索引都在错误指令之后,它们从未被执行,所以我基本上进行了非索引完整 table 扫描,这就是为什么加载结果需要很长时间。

我更正了我的 SQL 文件并再次创建了数据库,它非常有效。抱歉浪费你们的时间。

PS:我实际上将服务器提升到 16GB 内存和 6 个 VCPU,但没有任何区别。

这会给你一个合理的 24 行吗?还是您依赖其他 table 的过滤?

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

如果是,请考虑以下事项:

您当前的查询正在从 table 的 中拖出整行,然后打乱它们,最后只提供 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 更有价值?
  • 您正在从大约一打 table 的许多行中获取所有列。
  • 您(当前)首先收集大量 潜在 输出行,然后使用 GROUP BY 消除重复项,最后 LIMITing 到仅 24。 temp table 的大小可能很大。 ("inflate-deflate" JOIN 加上 GROUP BY 的综合征。
  • 您可能在某些 * 列列表中有 TEXT 列;这加剧了温度 table 问题。

这些结合起来会导致 fast/slow 性能。我的建议,如果可行,可以消除大部分。

另外 FotografiasPorPropiedad 需要 INDEX(id_propiedad, orden)(任意顺序)。