【问题标题】:MySQL to older MariaDB - slower performanceMySQL 到较旧的 MariaDB - 性能较慢
【发布时间】:2019-04-09 16:21:50
【问题描述】:

所以我在将数据库从一台网络服务器迁移到另一台时遇到了问题。 服务器 1 在 cPanel 托管下运行 MySQL 5.6 版... 服务器 2 在 Webmin/Virtualmin 下运行 MariaDB 5.5 版 两者的PHP版本相同...5.6

无论如何,我想将站点从服务器 1 移动到服务器 2。我使用 HeidiSQL 导出数据库,然后在服务器 2 上导入数据。数据导入正常,但查询性能差 10 倍.我检查了缓冲区大小变量和所有其他“关键”变量,它们在服务器 2 上是相同的或增加的。 我尝试将存储引擎从 MyIsam 更改为 Aria 或 InnoDB,但结果相同……我还优化了整个数据库,但还是没有运气。两台服务器上的索引相同。

然后我决定将数据库重新托管在原始服务器上,并从新服务器加载文件....我导出了新数据库(仅使用插入忽略的数据)并将该 SQL 导入回服务器 1。立即导入后,原始数据库也开始缓慢执行...... 除非我使用第一次移动数据库时的原始备份,否则无论我如何将数据库更新为新数据,它都会开始表现不佳......

现在需要 35 秒才能运行的查询示例,而以前需要 3 秒:

select  p.*, pd.ID detailID,
        s.title subject, s.displayTitle, s.memberPanCode,
        s.virtualDelivery,
        CASE WHEN (DATE_ADD(p.releaseDate, INTERVAL 2 WEEK) > NOW()) THEN 1 ELSE 0 END pNew,
        CASE WHEN(s.publicChoice=1) THEN s.memberPanCode ELSE '' END usableSubject,
        CASE WHEN(s.displayTitle=1) THEN s.ID ELSE '0' END subjectID from  sProduct p
    inner join  sProductDetail pd  ON pd.ID_sProduct=p.ID
    left join  sProductDetailWarehouse pdw  ON pdw.ID_sProductDetail=pd.ID
    left join  sProductDetailSubjectPrice pdsp  ON pdsp.ID_sProductDetail=pd.ID
    left join  sSubject s  ON (s.memberPanCode=pdsp.memberPanCode
                          and  s.shownOnSite=1)
    where  (      s.publicChoice=1
              OR  s.defaultSubject=1
              OR  s.memberPanCode=''
              OR  s.memberPanCode IS NULL
           )
      AND  (      (pd.ID > 0  AND  s.displayTitle IS NOT NULL)
              OR  (pd.ID IS NULL  AND  s.displayTitle IS NULL )
              OR  (pd.ID > 0  and  p.ID_sSupplier > 0 )
              OR  (pd.ID > 0  and  pdsp.ID IS NULL )
              OR  (pd.ID > 0 and  s.displayTitle IS NULL )
           )
      AND  (DATE_ADD(NOW(), INTERVAL 1 DAY) > p.showDate)
      AND  (      pdw.stock > 0
              OR  pd.stock > 0
              OR  (p.ID_sSupplier > 0  AND  p.ID_sSupplier <> '3')
           )
      and  p.published IN (1,2)
    GROUP BY  p.ID, s.memberPanCode
    order by  p.showDate desc
    limit  3 

解释新的、速度较慢的数据库的语句:

为较旧、较快的数据库解释语句:

知道还有什么需要检查的吗?我该怎么做才能解决这个问题?

感谢您的帮助

【问题讨论】:

  • 机器的规格是什么,网络接口是什么(网络问题?)我假设你在最新的 MariaDB 下运行?
  • 实际上是同一台机器,两者的硬件规格相同(最细微的细节)。我正在运行 Virtualmin 官方支持的最新 MariaDB,遗憾的是 5.5
  • 有无数种可能性。加快搜索速度... 如果您发现任何查询似乎较慢,让我们研究一下。如果没有,用long_query_time=0.2打开slowlog,收集一天的结果,通过pt-query-digest总结,然后我们看一下最慢的几个查询。从那以后,我们应该能够回溯到真正的原因是什么。
  • 还添加了说明语句...
  • 两台机器上好像有一组不同的索引??

标签: php mysql performance mariadb


【解决方案1】:

桌子有多大? InnoDB 现在做FULLTEXT。 MyISAM 正在成为孤儿。我同意不使用像 5.5 一样古老的东西。 MariaDB 有 10.0、10.1、10.2、10.3。 MySQL 有 5.6、5.7、8.0。其中大部分都做了很多优化工作。通过回溯到 5.5,您可能会丢失一些优化功能。唉,我还没有发现丢失的具体东西。

ORs 对性能来说是致命的。它们基本上阻止了索引的使用。我看不到任何明显的重新排列方式——因为ORs 跨越多个表。

这里有一些复合的、覆盖的索引,可能有帮助:

pd:   INDEX(ID_sProduct, ID, stock)  -- perhaps this order is best
pdw:  INDEX(ID_sProductDetail, stock)  -- in this  order
pdsp: INDEX(ID_sProductDetail, memberPanCode, ID)  -- in this order
s:    INDEX(memberPanCode, shownOnSite)  -- in either order

另外,添加

p:  INDEX(showDate, published, ID, ID_sSupplier) -- in this order

并通过将p.* 拉出主流来重组查询。目前,笨重的 p.* 在减少到只有 3 行之前通过连接等拖拉。通过重构,我们可以找出哪 3 行 firstthen 获取所有的东西:

SELECT p2.*, etc.
       p2.releaseDate > NOW() - INTERVAL 2 WEEK  AS pNew,
       etc.
    FROM (
        SELECT  toss p.*, add p.ID, keep other columns
            FROM ...
            LEFT JOIN ...
            ORDER BY...
            LIMIT 3
         ) AS x
    JOIN sProduct AS p2  ON x.ID = p2.ID
    ORDER BY p2.showDate desc

新索引是“覆盖”的,因为子查询中p 的所有使用都在索引中。我观察到releaseDate 可以被忽略,并在第二次使用sProduct 时被拾取。

我将sShowDate 放在索引的首位,假设它至少进行了一些过滤 (p.showDate &lt; NOW + INTERVAL 1 DAY)。

GROUP BYORDER BY 组合需要一两个文件排序;它们不能被消除。我所做的是通过减小它们的体积来最小化它们的成本。

【讨论】:

  • 您好,非常感谢您花时间回答。我肯定会检查查询并尝试优化它们。您的回答肯定会为我指明正确的方向。我不得不说,虽然开始这个问题的原因是两个数据库在同一组数据的性能方面的差异......我只是不明白为什么一个比另一个快。
  • @Bostjan - 我的回答对你来说是一个安慰奖——我无法弄清楚造成这种差异的具体原因。 (不具体的原因是 5.5 较旧,因此可能更慢。)
  • 谢谢 :) 这无疑是朝着正确方向迈出的一大步。我有一种感觉,我一定是错过了一些微不足道的事情,才会发生这种差异。
  • @Bostjan - 再次查看EXPLAINs,我注意到一台机器上可能有一个索引,而另一台机器上可能没有。我添加了对s 的推荐。
  • 即使问题仍然存在,我也会奖励这个答案。在我看来,新数据库中有一些错误/损坏
【解决方案2】:

这个查询很高级,那些WHERE的条件太复杂了

您的原始查询(格式化)

   SELECT p.*,
          pd.ID,
          detailID, /** include table alias? */
          s.title subject,
          s.displayTitle,
          s.memberPanCode,
          s.virtualDelivery,
          (p.releaseDate < NOW() - INTERVAL 2 WEEK) pNew /** Booleans are resolve to 1/0 in MySQL */
          CASE WHEN s.publicChoice = 1 THEN s.memberPanCode ELSE '' END usableSubject,
          CASE WHEN s.displayTitle = 1 THEN s.ID ELSE '0' END subjectID 

     FROM sProduct p

     JOIN sProductDetail pd 
       ON pd.ID_sProduct = p.ID

LEFT JOIN sProductDetailWarehouse pdw 
       ON pdw.ID_sProductDetail = pd.ID

LEFT JOIN sProductDetailSubjectPrice pdsp 
       ON pdsp.ID_sProductDetail = pd.ID 

LEFT JOIN sSubject s 
       ON s.memberPanCode = pdsp.memberPanCode 
      AND s.shownOnSite=1

    WHERE (s.publicChoice=1 OR s.defaultSubject=1 OR s.memberPanCode='' OR s.memberPanCode IS NULL)  
      AND /** (
            (pd.ID > 0 AND s.displayTitle IS NOT NULL)
         OR (pd.ID IS NULL AND s.displayTitle IS NULL) 
         OR (pd.ID > 0 AND p.ID_sSupplier > 0) 
         OR (pd.ID > 0 AND pdsp.ID IS NULL) 
         OR (pd.ID > 0 AND s.displayTitle IS NULL)
          ) */ pd.ID > 0 /** see below */
      AND p.showDate < NOW() + INTERVAL 1 DAY 
      AND (pdw.stock > 0 OR pd.stock > 0 OR (p.ID_sSupplier > 0 AND p.ID_sSupplier <> '3'))  
      AND p.published IN (1,2)

 GROUP BY p.ID, s.memberPanCode 
 ORDER BY p.showDate DESC 
    LIMIT 3

让我们从这个条件开始

  AND (
         /** This with the last subcondition is just pd.ID > 0 */
         (pd.ID > 0 AND s.displayTitle IS NOT NULL)  

         /** This is impossible due to your INNER JOIN */
      OR (pd.ID IS NULL AND s.displayTitle IS NULL)

      OR (pd.ID > 0 AND p.ID_sSupplier > 0) 
      OR (pd.ID > 0 AND pdsp.ID IS NULL) 

         /** This with the first subcondition is just pd.ID > 0 */
      OR (pd.ID > 0 AND s.displayTitle IS NULL)
       ) 

整个条件解析为pd.ID &gt; 0,除非您手动添加了 ID 为 0 的产品,否则这始终为 TRUE

出于同样的原因,我怀疑 (p.ID_sSupplier &gt; 0 AND p.ID_sSupplier &lt;&gt; '3') 可以变成 p.ID_sSupplier &lt;&gt; 3

第一个条件似乎也超级包容

WHERE (
      s.publicChoice=1 
     OR s.defaultSubject=1 
     OR s.memberPanCode='' 
     OR s.memberPanCode IS NULL
      )  
   ...

这让我质疑在这种情况下您实际上试图避免哪些行?

那个 GROUP BY 子句也令人担忧,因为您没有选择聚合列.. 很多最终列将被任意选择

您实际上想通过此查询实现什么目标?

值得记住的是,在使用查询时,OR 条件的解析往往比 AND 条件慢

【讨论】:

  • 您好,也感谢您发布这个经过深思熟虑的答案。我同意这个查询和大多数网站一样一团糟......我在移动服务器时接管了它的维护......我将优化大多数查询,但就像我在上面的答案中所说的那样,我很困惑两个数据库之间的区别。非常感谢您的努力。
  • 没问题,乐于助人!我理解这种困惑,虽然看起来你有一些其他的答案/cmets 指向可能发生的事情。尽管从学术的角度来看这很有趣.. 如果您无论如何都在优化查询,那么您最终可能会在两台服务器上得到截然不同的解释计划,因此对该问题的确切答案可能最终会过时!跨度>
【解决方案3】:

执行计划(显示在 EXPLAIN 输出中)不同。所以我们有理由期待不同的性能特征。

正如@RickJames 在评论中指出的那样,目标环境中似乎缺少一些索引

问题指出:“两台服务器上的索引相同。”

但所提供的信息使我们得出一个结论,即索引相同。

我们看到在第一个 EXPLAIN 的输出中引用了一些索引。并且在第二个 EXPLAIN 的输出中找不到这些索引名称。在架构定义脚本中也找不到这些索引名称。


问:为什么架构定义中有一些索引(第一个 EXPLAIN 中报告的缺失)?

问:mysqldump 文件中用于迁移的输出是否已修改以删除一些索引定义?

问:是否使用了除mysqldump 之外的其他工具来提取迁移的架构定义,是否省略了索引?

问:是否某些“创建索引”语句在目标环境中执行失败? (可能是因为索引中列大小的限制?)


或者也许我有相反的方法,也许在目标中添加了源中不存在的索引。

【讨论】:

  • 嗨,是的...在新的、较慢的环境中添加了索引...我使用 HeidiSQL 和 mysqldump 创建数据库备份,但两者都提供了同样糟糕的性能
猜你喜欢
  • 2018-11-17
  • 1970-01-01
  • 2021-09-27
  • 2020-09-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-02
相关资源
最近更新 更多