优化 ORDER BY

2023-11-21

我正在尝试优化这个排序查询posts by reputation字段(第一个),然后id场(第二)。如果没有第一个字段查询,则需要约 0.250 秒,但如果有第一个字段查询,则需要约 2.500 秒(意味着慢了 10 倍,太糟糕了)。有什么建议吗?

SELECT -- everything is ok here
FROM posts AS p
ORDER BY 
    -- 1st: sort by reputation if exists (1 reputation = 1 day)
    (CASE WHEN p.created_at >= unix_timestamp(now() - INTERVAL p.reputation DAY) 
        THEN +p.reputation ELSE NULL END) DESC, -- also used 0 instead of NULL
    -- 2nd: sort by id dec
    p.id DESC
WHERE p.status = 'published' -- the only thing for filter
LIMIT 0,10 -- limit provided as well

Notes:
- 使用 InnoDB (MySQL 5.7.19)
- 主要是id on posts table
- 字段均被索引created_at and reputation

解释结果:



# id,  select_type, table, partitions, type,  possible_keys, key,  key_len, ref,  rows,    filtered, Extra
# '1', 'SIMPLE',    'p',   NULL,       'ALL', NULL,          NULL, NULL,    NULL, '31968', '100.00', 'Using filesort'
  

UPDATE^^

声誉规定:一个帖子,多少(n=声誉)天可以显示在列表顶部。

实际上,我试图为一些可以在列表顶部获取的帖子提供声誉,并找到解决方案:按“代表”订购帖子,但仅限“一天”限制。但一段时间后(大约两年),由于表数据量的增加,该解决方案现在变成了一个问题。如果我无法解决此问题,那么我应该从服务中删除该功能。

UPDATE^^

-- all date's are unix timestamp (bigint)
SELECT p.*
    , u.name user_name, u.status user_status
    , c.name city_name, t.name town_name, d.name dist_name
    , pm.meta_name, pm.meta_email, pm.meta_phone
    -- gets last comment as json
    , (SELECT concat("{", 
        '"id":"', pc.id, '",', 
        '"content":"', replace(pc.content, '"', '\\"'), '",', 
        '"date":"', pc.date, '",', 
        '"user_id":"', pcu.id, '",', 
        '"user_name":"', pcu.name, '"}"') last_comment_json 
        FROM post_comments pc 
        LEFT JOIN users pcu ON (pcu.id = pc.user_id) 
        WHERE pc.post_id = p.id
        ORDER BY pc.id DESC LIMIT 1) AS last_comment
FROM posts p
    -- no issues with these
    LEFT JOIN users u ON (u.id = p.user_id)
    LEFT JOIN citys c ON (c.id = p.city_id)
    LEFT JOIN towns t ON (t.id = p.town_id)
    LEFT JOIN dists d ON (d.id = p.dist_id)
    LEFT JOIN post_metas pm ON (pm.post_id = p.id)
WHERE p.status = 'published'
GROUP BY p.id
ORDER BY 
    -- everything okay until here
    -- any other indexed fields makes query slow, not just "case" part
    (CASE WHEN p.created_at >= unix_timestamp(now() - INTERVAL p.reputation DAY) 
        THEN +p.reputation ELSE NULL END) DESC, 
    -- only id field (primary) is effective, no other indexes 
    p.id DESC
LIMIT 0,10;

Explain;



# id, select_type, table, partitions, type, possible_keys, key, key_len, ref, rows, filtered, Extra
1, PRIMARY, p, , ref, PRIMARY,user_id,status,reputation,created_at,city_id-town_id-dist_id,title-content, status, 1, const, 15283, 100.00, Using index condition; Using temporary; Using filesort
# dunno, these join's are not using, but if i remove returning fields from select part show "Using index condition"
1, PRIMARY, u, , eq_ref, PRIMARY, PRIMARY, 2, p.user_id, 1, 100.00, 
1, PRIMARY, c, , eq_ref, PRIMARY, PRIMARY, 1, p.city_id, 1, 100.00, 
1, PRIMARY, t, , eq_ref, PRIMARY, PRIMARY, 2, p.town_id, 1, 100.00, 
1, PRIMARY, d, , eq_ref, PRIMARY, PRIMARY, 2, p.dist_id, 1, 100.00, 
1, PRIMARY, pp, , eq_ref, PRIMARY, PRIMARY, 2, p.id, 1, 100.00, 
2, DEPENDENT SUBQUERY, pc, , ref, post_id,visibility,status, post_id, 2, func, 2, 67.11, Using index condition; Using where; Using filesort
2, DEPENDENT SUBQUERY, pcu, , eq_ref, PRIMARY, PRIMARY, 2, pc.user_id, 1, 100.00, 
  

这是一个非常有趣的查询。在优化过程中,您可能会发现并了解很多有关 MySQL 工作原理的新信息。我不确定我是否有时间一次详细地写出所有内容,但我可以逐渐更新。

为什么很慢

基本上有两种情况:quick and a slow.

In a quick在这种情况下,您正在按某种预定义的顺序遍历表,并且可能同时通过 id 从其他表中的每一行快速获取一些数据。在这种情况下,一旦 LIMIT 子句指定了足够的行,您就会停止行走。订单从哪里来?来自表上的 B 树索引或子查询中结果集的顺序。

In a slow如果您没有预定义的顺序,MySQL 必须隐式地将所有数据放入临时表中,根据某些字段对表进行排序并返回nLIMIT 子句中的行。如果您放入该临时表的任何字段的类型为 TEXT(不是 VARCHAR),MySQL 甚至不会尝试将该表保留在 RAM 中,而是在磁盘上刷新和排序它(因此需要额外的 IO 处理)。

首先要解决的事情

在很多情况下,您无法构建允许您遵循其顺序的索引(例如,当您对不同表中的列进行 ORDER BY 时),因此在这种情况下的经验法则是尽量减少 MySQL 将放入的数据在临时表中。你怎么能这样做?您仅选择子查询中行的标识符,获得 id 后,将 id 连接到表本身和其他表以获取内容。也就是说,您制作一个带有订单的小表,然后使用快速场景。 (这与一般的 SQL 略有矛盾,但每种 SQL 风格都有自己的方式来优化查询)。

巧合的是,你的SELECT -- everything is ok here看起来很有趣,因为这是第一个不好的地方。

SELECT p.*
    , u.name user_name, u.status user_status
    , c.name city_name, t.name town_name, d.name dist_name
    , pm.meta_name, pm.meta_email, pm.meta_phone
    , (SELECT concat("{", 
        '"id":"', pc.id, '",', 
        '"content":"', replace(pc.content, '"', '\\"'), '",', 
        '"date":"', pc.date, '",', 
        '"user_id":"', pcu.id, '",', 
        '"user_name":"', pcu.name, '"}"') last_comment_json 
        FROM post_comments pc 
        LEFT JOIN users pcu ON (pcu.id = pc.user_id) 
        WHERE pc.post_id = p.id
        ORDER BY pc.id DESC LIMIT 1) AS last_comment
FROM (
    SELECT id
    FROM posts p
    WHERE p.status = 'published'
    ORDER BY 
        (CASE WHEN p.created_at >= unix_timestamp(now() - INTERVAL p.reputation DAY) 
            THEN +p.reputation ELSE NULL END) DESC, 
        p.id DESC
    LIMIT 0,10
) ids
JOIN posts p ON ids.id = p.id  -- mind the join for the p data
LEFT JOIN users u ON (u.id = p.user_id)
LEFT JOIN citys c ON (c.id = p.city_id)
LEFT JOIN towns t ON (t.id = p.town_id)
LEFT JOIN dists d ON (d.id = p.dist_id)
LEFT JOIN post_metas pm ON (pm.post_id = p.id)
;

这是第一步,但即使现在您也可以看到,您不需要为不需要的行进行这些无用的 LEFT JOINS 和 json 序列化。 (我跳过了GROUP BY p.id,因为我看不到哪个 LEFT JOIN 可能会导致多行,所以您不进行任何聚合)。

还没有写:

  • indexes
  • 重新表述 CASE 子句(使用 UNION ALL)
  • 可能强制索引
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

优化 ORDER BY 的相关文章

随机推荐

  • 核心数据内存使用和内存警告

    我有这个问题 我在核心数据中有一个图像数据库 我获取所有图像 大约 80MB 并放入 NSMutableArray 中 对象被正确错误 NSArray fetchResults self managedObjectContext execu
  • Unity PerRequestLifetimeManager 在不同请求中重用对象

    我已经为我们的项目设置了 Unity 的依赖注入 该项目本身是一个同时使用 MVC 和 Web API 的 ASP NET 应用程序 对于数据库上下文 我正在使用PerRequestLifetimeManager 这样做是为了使业务逻辑的不
  • wpf - 我可以在 wpf 中使用 System.Drawing 吗?

    我正在将图像保存在数据库中 但是如何从数据库中检索该图像 当我尝试使用system drawing 它显示错误 一些人说我不能在wpf中使用system drwaing 甚至不能使用dll文件 我的代码是 private void btnS
  • 子 pom 中存在重复的artifactId

    我希望父 pom 为众多子 pom 定义一些要继承的属性 但是 当我尝试在父 pom 中的这些属性之一中使用 artifactId 时 它会在子项的有效 pom 中重复 下面是非常基本的示例 假设我拥有 poms 所需的所有有效字段 gro
  • ModelSim-Altera 错误

    我正在使用 Ubuntu Linux 14 04 LTS 和 Altera Quartus 15 0 网络版 由于许可错误 我很难模拟我的设计 我正在设计一个 LCD driverVEEK MT友晶科技的液晶触摸屏旋风 IV EP4CE11
  • 如何使用freopen_s函数

    为了从文本文件读取输入 我编写了以下代码 int main int x ifndef ONLINE JUDGE freopen input txt r stdin endif scanf d x printf d n x system pa
  • 为什么“git clone”不采用 refspec?

    看来很多人都去换了git clone与组合git init git fetch 这看起来相当愚蠢 不幸的是像 Jenkins 这样的工具不会为你做这件事 那么为什么 git clone 不像 git fetch 那样采用 refspec 呢
  • 如何在 Ruby 中获取 Enumerable 的第 n 个元素

    例如 要返回第 10 000 个质数 我可以编写 require prime Prime first 10000 last gt 104729 但是创建一个巨大的中间数组 只是为了检索它的最后一个元素感觉有点麻烦 鉴于 Ruby 是一种如此
  • 强制调用父方法

    是否有 或模式 强制调用父方法 我有一个像这样的抽象类 abstract class APrimitive public function validate Do some stuff that applies all classes th
  • 多个main方法有什么用?

    c 使我们能够使用方法定义多个类 Main方法是程序执行的入口点 那么为什么我们要拥有多个地方来执行程序呢 多个 main 方法相对于单个 main 方法有什么优点 Edit 示例 cs Class Example 1 public sta
  • 打字稿用只读属性初始化对象

    有没有办法初始化对象文字并同时声明其具有只读属性的接口 例如 let a readonly b 2 readonly c 3 您可以使用as const断言 let a b 2 c 3 as const typed as readonly
  • 为什么我的异步 ASP.NET Web API 控制器阻塞主线程?

    我有一个 ASP NET Web API 控制器thought将异步操作 控制器设计为在第一个请求时休眠 20 秒 但立即为任何后续请求提供服务 所以我预计的时间表是这样的 提出要求1 提出要求2 提出要求3 要求 2 次退货 请求 3 个
  • 如何在 Ruby 中交错不同长度的数组

    如果我想在 Ruby 中交错一组数组 并且每个数组的长度相同 我们可以这样做 a zip b zip c flatten 但是 如果数组的大小可以不同 我们如何解决这个问题呢 我们可以做这样的事情 def interleave args r
  • UITextView - 根据 SwiftUI 中的内容调整大小

    我试图弄清楚如何使 UITextView 的大小取决于它在 SwiftUI 中的内容 我将 UITextView 包裹在UIViewRepresentable如下 struct TextView UIViewRepresentable Bi
  • 如何以编程方式截取屏幕截图(Swift、SpriteKit)

    我尝试了建议的方法 但输出是白色的空白屏幕截图 这让我假设我没有在视图中添加任何内容 以下是我向视图添加图形的方法 addChild 方法随 SpriteKit 一起提供 它接受 SKSpriteNodes addChild backgro
  • 为什么安装 Visual Studio 2008 后,catch(TException) 处理块行为在调试器下有所不同?

    考虑下面的控制台应用程序 该应用程序具有一个带有通用捕获处理程序的方法 用于捕获类型的异常TException 当此控制台应用程序使用 调试 配置构建并在 Visual Studio 调试器下执行 即通过 vshost exe 时 在 Vi
  • h1 标签类别(备用)

    我知道 h1 标签对于 SEO 很重要 所以我所有的标题都是 H1 太棒了 现在 我需要在某些页面上有一个稍微不同的标题 作为文本的第一行 通常 我只是将 h1 复制为 h2 并交替 问题 是否可以在标题标签中添加一个类 我尝试过但没有成功
  • 是否可以在handlebars.js模板中使用JavaScript

    描述说明了一切 如何将 JavaScript 脚本放入车把模板中 我想为我的网站制作一个动态 Paypal 按钮
  • PyQt5:对象没有属性“连接”

    我目前正在关注thisPyQt 中线程的教程 代码来自here 由于它是用 PyQt4 和 Python2 编写的 因此我调整了代码以使其能够与 PyQt5 和 Python3 一起使用 这是 gui 文件 newdesign py cod
  • 优化 ORDER BY

    我正在尝试优化这个排序查询posts by reputation字段 第一个 然后id场 第二 如果没有第一个字段查询 则需要约 0 250 秒 但如果有第一个字段查询 则需要约 2 500 秒 意味着慢了 10 倍 太糟糕了 有什么建议吗