【问题标题】:how can i improve the performance of this joomla query?我怎样才能提高这个 joomla 查询的性能?
【发布时间】:2014-01-28 18:31:05
【问题描述】:

我正在尝试将一些 joomla 用户数据传递给 Datatable(jQuery 插件)。 涉及4张表

  • joo_users(~10k 行),
  • joo_user_groups(~1.7k 行),
  • joo_user_usergroup_map(约 56k 行),
  • joo_user_profiles(~1398k 行)

现在的查询:

SELECT SQL_CALC_FOUND_ROWS
    a.id,
    a.name,
    MAX( IF(profiles.profile_key = 'profile.email', profiles.profile_value, NULL) ) AS email,
    MAX( IF(profiles.profile_key = 'profile.codice_agente', profiles.profile_value, NULL) ) AS codice_agente,
    MAX( IF(profiles.profile_key = 'profile.codice_agente_fisso', profiles.profile_value, NULL) ) AS codice_agente_fisso,
    MAX( IF(profiles.profile_key = 'profile.codice_agente_VAR', profiles.profile_value, NULL) ) AS codice_agente_VAR,
    MAX( IF(profiles.profile_key = 'profile.cellulare', profiles.profile_value, NULL) ) AS cellulare,
    (SELECT
       GROUP_CONCAT(DISTINCT b.title)
    FROM
       joo_user_profiles AS up
    LEFT JOIN
       joo_usergroups AS b ON REPLACE(up.profile_value, '"', '') = b.id
    WHERE
       up.profile_key LIKE 'profile.agenzia_%'
       AND up.user_id = a.id
       AND profile_value != '""') AS agenzia,
   GROUP_CONCAT(DISTINCT REPLACE(IF(profiles.profile_key LIKE 'profile.canale_%' AND profiles.profile_value = '1', profiles.profile_key, NULL),'profile.canale_','') ) AS canale,
   GROUP_CONCAT(DISTINCT IF(profiles.profile_key LIKE 'profile.area_%' AND profiles.profile_value != '""', profiles.profile_value, NULL))  AS area,
   (SELECT
        GROUP_CONCAT(DISTINCT b.title)
        FROM
           joo_user_profiles AS up
        LEFT JOIN
           joo_usergroups AS b ON REPLACE(up.profile_value, '"', '') = b.id
        WHERE
           up.profile_key LIKE 'profile.ruolo_%'
           AND up.user_id = a.id
           AND profile_value != '"0"') AS ruolo,
    GROUP_CONCAT(IF(profiles.profile_key LIKE 'profile.status_%' AND profiles.profile_value != '""', profiles.profile_value, NULL))  AS status

    FROM   `joo_users` as a
    LEFT JOIN joo_user_profiles AS profiles ON a.id = profiles.user_id

    GROUP BY id

    ORDER BY  id
          asc
    LIMIT 0, 20

这个野兽在生产服务器上需要 40 秒,这真的不能接受。

我知道视图不会带来任何性能提升,并且我已经为我能想到的每一列编制了索引。 你对我有什么建议吗?

【问题讨论】:

  • 那些连接条件太可怕了。只要你有这些,你就会陷入糟糕的表现。
  • 重新设计并尝试将您的逻辑拆分为更小的部分。

标签: mysql joomla2.5 database-performance


【解决方案1】:

我不知道你为什么需要 calc_found_rows,但把它放在那里。 至于查询,由于您的所有 group_concats 主要针对配置文件表和可选的用户组,我做了一个预查询(通过 tmpGrpCat 别名),它只对用户进行了相应的 group_concat 和 max() 等元素的电子邮件,condice ...,单元格等。我还包括在此预查询中,以排除配置文件值不是“”的位置,因为这对于您感兴趣的值来说似乎是一致的。

然后我在 users 表上做了一个简单的左连接来获取他们的 id/name 和其余的聚合。这应该简化引擎,以便为每个 profile_key LIKE(或相等)条件运行一次所有记录,按用户 ID 对它们进行分组,然后准备好进行最外层的查询。

如果可行,请告诉我们性能改进。如果将来也可以在性能技术方面帮助其他人。

SELECT SQL_CALC_FOUND_ROWS
      a.id,
      a.name,
      coalesce( tmpGrpCat.email, '' ) as email,
      coalesce( tmpGrpCat.codice_agente, '' ) as codice_agente,
      coalesce( tmpGrpCat.codice_agente_fisso, '' ) as codice_agente_fisso,
      coalesce( tmpGrpCat.codice_agente_VAR, '' ) as codice_agente_VAR,
      coalesce( tmpGrpCat.cellulare, '' ) as cellulare,
      coalesce( tmpGrpCat.AgTitle, '' )  as Agenzia,
      coalesce( tmpGrpCat.RuoloTitle, '' )  as ruolo,
      coalesce( tmpGrpCat.Canale, '' )  as Canale,
      coalesce( tmpGrpCat.Area, '' )  as Area,
      coalesce( tmpGrpCat.Status, '' )  as Status
   FROM   
      joo_users as a
         LEFT JOIN 
         ( SELECT 
                 up.user_id,
                 GROUP_CONCAT( DISTINCT IF( up.profile_key LIKE 'profile.agenzia_%', b.title, NULL )) AgTitle,
                 GROUP_CONCAT( DISTINCT IF( up.profile_key LIKE 'profile.ruolo_%', b.title, NULL )) RuoloTitle,
                 GROUP_CONCAT( DISTINCT IF( up.profile_key LIKE 'profile.canale_%' AND up.profile_value = '1', 
                    REPLACE( up.profile_key, 'profile.canale_',''), NULL)) AS canale,
                 GROUP_CONCAT( DISTINCT IF( up.profile_key LIKE 'profile.area_%', 
                    up.profile_value, NULL))  AS area,
                 GROUP_CONCAT( DISTINCT IF( up.profile_key LIKE 'profile.status_%', 
                    up.profile_value, NULL))  AS status,
                 MAX( IF( up.profile_key = 'profile.email', up.profile_value, NULL) ) AS email,
                 MAX( IF( up.profile_key = 'profile.codice_agente', up.profile_value, NULL) ) AS codice_agente,
                 MAX( IF( up.profile_key = 'profile.codice_agente_fisso', up.profile_value, NULL) ) AS codice_agente_fisso,
                 MAX( IF( up.profile_key = 'profile.codice_agente_VAR', up.profile_value, NULL) ) AS codice_agente_VAR,
                 MAX( IF( up.profile_key = 'profile.cellulare', up.profile_value, NULL) ) AS cellulare
              FROM 
                 joo_user_profiles AS up
                    LEFT JOIN joo_usergroups AS b 
                       ON REPLACE(up.profile_value, '"', '') = b.id
                    WHERE
                        ( up.profile_key LIKE 'profile.agenzia_%'
                       OR up.profile_key LIKE 'profile.ruolo_%' 
                       OR up.profile_key LIKE 'profile.canale_%'
                       OR up.profile_key LIKE 'profile.area_%' 
                       OR up.profile_key LIKE 'profile.status_%'
                       OR up.profile_key = 'profile.email'
                       OR up.profile_key = 'profile.codice_agente'
                       OR up.profile_key = 'profile.codice_agente_fisso'
                       OR up.profile_key = 'profile.codice_agente_VAR'
                       OR up.profile_key = 'profile.cellulare' )
                       AND up.profile_value != '""' 
                       AND up.profile_value != '"0"'
                    GROUP BY
                       up.user_id ) as tmpGrpCat
            ON a.id = tmpGrpCat.user_id
   ORDER BY  
      id asc
   LIMIT 
      0, 20

【讨论】:

  • 我确实在我的本地机器上观察到了 10 倍的改进,但由于某种原因,当我在服务器上运行 quey 时,它无限期挂起(几分钟后我停止了它)我的开发机器是 mysql 5.6 而服务器是 5.5,但我怀疑这可能是问题。
【解决方案2】:

由于您在 Joomla 中执行此查询,您可以尝试使用 Tabulizer for Joomla (http://wwww.tabulizer.com),它可以通过使用数据源缓存(用于查询结果)和 ajax 加载来处理大数据集,所以只有第一页最初会获取您的表,仅在需要时才获取其余数据。当然,查询需要至少执行一次才能填充缓存,因此您必须进一步改进它或增加可用的系统资源(PHP 内存和最大执行时间)。这很可能是它在服务器上而不是在本地服务器上失败的原因,因为大多数共享托管环境的 PHP 脚本的最大执行时间为 30 秒。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-04-06
    • 2021-04-16
    • 2020-05-03
    • 2021-08-01
    • 1970-01-01
    • 2021-07-31
    • 2013-08-29
    相关资源
    最近更新 更多