【问题标题】:WordPress MySQL query optimization for the BuddyPress member typesBuddyPress 成员类型的 WordPress MySQL 查询优化
【发布时间】:2021-12-12 15:17:21
【问题描述】:

我正在制作一款已经拥有超过 10,000,000 名用户的自定义应用程序。

我必须创建一个从特定成员类型中提取用户的自定义查询。

问题是,如果我只想查看 20 个用户并进行分页,则需要很长时间才能将数据从数据库中取出,并且由于查询非常繁重,经常会出现严重错误。

如果我使用BP_User_Query(Buddypress 类)也会发生同样的情况,因为我的代码是基于它的。

最大的问题是 Buddypress member_type 已经被记录在 WP 术语中,并且通过复杂的关系到达。

这是我的查询:

SELECT 
    wp_users.*,
    t_ex.name AS member_type,
    ( SELECT meta_value FROM wp_usermeta WHERE wp_usermeta.meta_key='first_name' AND wp_usermeta.user_id=wp_users.ID ) AS first_name,
    ( SELECT meta_value FROM wp_usermeta WHERE wp_usermeta.meta_key='last_name' AND wp_usermeta.user_id=wp_users.ID ) AS last_name,
    ( SELECT meta_value FROM wp_usermeta WHERE wp_usermeta.meta_key='nickname' AND wp_usermeta.user_id=wp_users.ID ) AS nickname,
    ( SELECT meta_value FROM wp_usermeta WHERE wp_usermeta.meta_key='description' AND wp_usermeta.user_id=wp_users.ID ) AS description,
    ( SELECT meta_value FROM wp_usermeta WHERE wp_usermeta.meta_key='last_update' AND wp_usermeta.user_id=wp_users.ID ) AS last_update,
    ( SELECT meta_value FROM wp_usermeta WHERE wp_usermeta.meta_key='wp_capabilities' AND wp_usermeta.user_id=wp_users.ID ) AS caps,
    ( SELECT meta_value FROM wp_usermeta WHERE wp_usermeta.meta_key='rich_editing' AND wp_usermeta.user_id=wp_users.ID ) AS rich_editing
FROM
    wp_users
    LEFT JOIN wp_term_relationships tr_ex ON tr_ex.object_id = wp_users.ID
    LEFT JOIN wp_term_taxonomy tt_ex ON tt_ex.term_taxonomy_id = tr_ex.term_taxonomy_id
    LEFT JOIN wp_terms t_ex ON t_ex.term_id = tt_ex.term_id
WHERE
    1=1
AND
    tt_ex.taxonomy = 'bp_member_type'
AND
    t_ex.name = 'platformuser'
GROUP BY wp_users.ID
ORDER BY wp_users.display_name ASC
LIMIT 0, 20

有没有更好的解决方案,我可以在不给数据库带来如此困难的情况下获得所需的结果。

我要查找的参数位于t_ex.name,在我的情况下为'platformuser'

我认为问题出在组和顺序上:

...

GROUP BY wp_users.ID
ORDER BY wp_users.display_name ASC

...

但不知道为什么。我也需要那个。

【问题讨论】:

  • 请提供SHOW CREATE TABLE wp_term_relationships -- 它的索引可能很差。 (我知道 wp_postmeta 可以。)还提供 wp_term_taxonomywp_userswp_terms 以防他们需要新索引。
  • WordPress 架构是 here 供您参考。
  • Mirabilu dictewp_term_relationships 上的索引很好。这是一个修改后的多对多连接表(带有term_order 列)。

标签: mysql wordpress query-optimization buddypress


【解决方案1】:

一些需要处理的东西。

首先,一千万用户对于 WordPress 来说是一个非常大的数字。你已经知道了。

第二,请考虑重写您的查询以执行所谓的“延迟连接”。你有臭名昭著的性能反模式SELECT many_long_rows ... ORDER BY something LIMIT small_number。这迫使 MySQL 对一大堆数据进行排序,只丢弃除一小部分之外的所有数据。

建议重写:从子查询开始以获取您感兴趣的 wp_users.ID 值。根据您的示例,子查询是这样的。

         SELECT wp_users.ID
            FROM wp_users
            LEFT JOIN wp_term_relationships tr_ex ON tr_ex.object_id = wp_users.ID
            LEFT JOIN wp_term_taxonomy tt_ex ON tt_ex.term_taxonomy_id = tr_ex.term_taxonomy_id
            LEFT JOIN wp_terms t_ex ON t_ex.term_id = tt_ex.term_id
           WHERE 1=1
             AND tt_ex.taxonomy = 'bp_member_type'
             AND t_ex.name = 'platformuser'
           GROUP BY wp_users.ID
           ORDER BY wp_users.display_name ASC
           LIMIT 0, 20

这必须对更少的数据进行排序......只是 ID 值。所以它应该更快,并且在 MySQL 中占用更少的 RAM。

然后在您的主查询中使用该子查询来处理您关心的二十个用户。

SELECT 
    wp_users.*,
    'platformuser' AS member_type,
    ( SELECT meta_value FROM wp_usermeta WHERE wp_usermeta.meta_key='first_name' AND wp_usermeta.user_id=wp_users.ID ) AS first_name,
    ( SELECT meta_value FROM wp_usermeta WHERE wp_usermeta.meta_key='last_name' AND wp_usermeta.user_id=wp_users.ID ) AS last_name,
    ( SELECT meta_value FROM wp_usermeta WHERE wp_usermeta.meta_key='nickname' AND wp_usermeta.user_id=wp_users.ID ) AS nickname,
    ( SELECT meta_value FROM wp_usermeta WHERE wp_usermeta.meta_key='description' AND wp_usermeta.user_id=wp_users.ID ) AS description,
    ( SELECT meta_value FROM wp_usermeta WHERE wp_usermeta.meta_key='last_update' AND wp_usermeta.user_id=wp_users.ID ) AS last_update,
    ( SELECT meta_value FROM wp_usermeta WHERE wp_usermeta.meta_key='wp_capabilities' AND wp_usermeta.user_id=wp_users.ID ) AS caps,
    ( SELECT meta_value FROM wp_usermeta WHERE wp_usermeta.meta_key='rich_editing' AND wp_usermeta.user_id=wp_users.ID ) AS rich_editing
FROM
    wp_users
WHERE wp_users.ID IN (
         SELECT wp_users.ID
            FROM wp_users
            LEFT JOIN wp_term_relationships tr_ex ON tr_ex.object_id = wp_users.ID
            LEFT JOIN wp_term_taxonomy tt_ex ON tt_ex.term_taxonomy_id = tr_ex.term_taxonomy_id
            LEFT JOIN wp_terms t_ex ON t_ex.term_id = tt_ex.term_id
           WHERE 1=1
             AND tt_ex.taxonomy = 'bp_member_type'
             AND t_ex.name = 'platformuser'
           GROUP BY wp_users.ID
           ORDER BY wp_users.display_name ASC
           LIMIT 0, 20
      )     
GROUP BY wp_users.ID
ORDER BY wp_users.display_name ASC
LIMIT 0, 20

查找仅 20 个用户的元数据不会花费很长时间。

第三WordPress's wp_users table,由https://ma.tt 和公司定义,在display_name 列上没有索引。所以 MySQL 用于避免巨大排序的索引技巧不起作用。您可以修复此问题

  • 使用ORDER BY wp_users.user_nicename 代替display_name,或通过
  • 像这样在display_name 列上创建索引。
    ALTER TABLE wp_users ADD KEY display_name (display_name);
    

第四,如果您运行的是旧版本的 MySQL(8 之前)或 MariaDB(10.3 之前),请升级它。并将您的表转换为使用 InnoDB 和 DYNAMIC 行格式。您正在努力挑战一千万用户的极限。这样做时为什么要使用过时的软件?

第五(如果您执行上述操作则不那么重要)wp_usermeta 上的索引对于您执行的查找类型不是最佳的。这一系列 MySQL 语句将重新索引表,以便您更快地查找。这可能需要一段时间。

ALTER TABLE wp_usermeta ADD UNIQUE KEY umeta_id (umeta_id);
ALTER TABLE wp_usermeta DROP PRIMARY KEY;
ALTER TABLE wp_usermeta ADD PRIMARY KEY (user_id, meta_key, umeta_id);
ALTER TABLE wp_usermeta DROP KEY user_id;
ALTER TABLE wp_usermeta DROP KEY meta_key;
ALTER TABLE wp_usermeta ADD KEY meta_key (meta_key, user_id);

@RickJames 和我发布了一个(免费开源)WordPress plugin 来为您处理 InnoDB 转换和重新索引,或者您当然可以自己做。您可以将它与WP-CLI 一起使用以避免超时。

我认为我们插件的下一个版本还应该将display_name 索引添加到wp_users 以涵盖您的情况。 (我听说其他人也有类似的问题。)

【讨论】:

  • 谢谢!这是一个很好的答案。我会进行更改、测试、查看您的插件并让您知道。
  • 这正是我所需要的。工作完美!谢谢!只是,您提供的重新索引效果不佳。仍在探索这个问题。
【解决方案2】:

OJones 所说的,以及

为了避免排序:

       GROUP BY wp_users.ID
       ORDER BY wp_users.display_name ASC

-->

       GROUP BY wp_users.display_name ,    wp_users.ID
       ORDER BY wp_users.display_name ASC, wp_users.ID ASC

(通过使这两行基本相等,可以防止额外的排序。)

【讨论】:

    猜你喜欢
    • 2015-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-13
    • 2011-01-22
    • 2011-07-07
    相关资源
    最近更新 更多