【问题标题】:php mysql optimizationphp mysql 优化
【发布时间】:2011-09-01 15:46:58
【问题描述】:

我被分配了从一些保存的数据中为网站创建一些图形统计数据的任务。

事实: - 有 3 个数据库正在使用中。 dbCurrent、dbStats、dbBackup。 dbCurrent 是网站的主数据库 dbStats 保存各种统计表和跟踪数据 dbBackup 保存过去五年的统计/跟踪表。

  • 我将使用的数据来自两个数据库(dbStats、dbBackup)
  • 表名是:stats2006、stats2007、stats2008 等,除了当前的stats 只是“stats”。每个表格都有其年份的数据。
  • 每一年数据的表结构都是一样的: primaryID 字段为整数 productID 字段为整数 dateMonitor 字段是整数(unixtimestamp) pageName 字段为 varchar (20)
  • productID、dateMonitor、pageName 字段也有索引

换句话说,什么产品在什么日期从什么页面被浏览。

所以,我的想法是从每个表中创建一个循环并获取我的数据。 我的每个查询看起来像:

Select COUNT(primaryID) as myCounter FROM $tablename WHERE $conditions

其中 $tablename 和 $conditions 是基于每个循环的变量。 所有条件都类似于:

  • date1 和 date2 之间的 dateMonitor
  • pageName='some val'
  • productID IN ($comma_separated_values)
  • 以上组合

到目前为止,所有这些都运行良好(对于单个产品)。

当我尝试创建报告以比较“y”年中的“x”产品时(由管理员/版主动态选择),脚本运行时间超过 15 分钟。

我正在寻找一种方法来提高脚本的性能。 我目前使用的逻辑/结构如下:

Loop through products to find the ids to use (typical format is: x,y,z (comma separated values)
Open Loop through years/months
Execute one sql query for each affected table/database to get the number of affected rows.
Close year loop
Send data to graph script (jquery jqPlot to be exact) to print on screen

任何帮助/想法表示赞赏。

编辑: 基于@Narf 的 UNION ALL 建议,我基于 12 个子选择语句构建了 1 个单一查询:

SELECT COUNT(*) AS monthlyTotal FROM db1.table1 WHERE dateMonitor>='1167606001' AND dateMonitor<='1170284399' AND dateMonitor='test' 
UNION ALL 
SELECT COUNT(*) AS monthlyTotal FROM db1.table2 WHERE dateMonitor>='1170284401' AND dateMonitor<='1172703599' AND dateMonitor='test' ...

每个 select 语句都引用一个月的持续时间。演示代码:

for ($m=1; $m<=12; $m++)
{
$startDate = mktime(0, 0, 1, $m, 1, $myYear);
$daysOfMonth = date("t", mktime(10, 10, 10, $m, 10, $myYear));
$endDate = mktime(23, 59, 59, $m, $daysOfMonth, $myYear);

$query_chk1 .= "SELECT COUNT(*) AS monthlyTotal FROM db1.table1 WHERE dateMonitor>='$startDate' AND dateMonitor<='$endDate' AND pageName='test' UNION ALL ";
}

$query_chk1 = substr($query_chk1, 0, -10);

EDIT2:创建组合索引后(如@ypercube 所建议),我发现执行时间略有减少。

现在我的平均执行时间是 11 分钟(原来的时间是 15-17 分钟)

这对减少执行时间有很大帮助。

谢谢。

【问题讨论】:

  • 你的表有什么索引?
  • 附注:与COUNT(field) 相比,COUNT(*) 在 MySQL 中更快。并给出相同的结果,只要 field 不可为空。
  • @ypercube: productID, dateMonitor, pageName 字段是每个表中的索引
  • 对于涉及多个条件(在多个字段中)的查询,您将从复合索引中受益。例如,WHERE dateMonitor between date1 and date2 AND pageName='some val' 将受益于 (pageName, datemonitor) 索引。

标签: php mysql optimization


【解决方案1】:

您无能为力,至少因为您已经为所有列编制了索引……这是我能想到的最好的:

SELECT COUNT(*)
FROM `stats`
WHERE `productID IN(1,2,3)
    AND `dateMonitor` >= <unixtime from>
    AND `dateMonitor` <= <unixtime to>
    AND `pageName`='<value>'

...以及如何:

  • 正如 ypercube 所说 - 使用 COUNT(*) 更快。
  • 我不确定这一点,但我相信对整数使用 &gt;=&lt;= 而不是 BETWEEN 应该更快。

您应该尝试的另一件事是一次执行所有查询(如果不止一个)。用文字来解释会比较困难,而且我看你对SQL的掌握很好,所以你应该能看懂逻辑,所以举个例子:

假设我们需要搜索 2006 年 5 月至 2008 年 4 月期间 id 为 123、13、5 和 6 且 pageName 为“test”的产品:

  • 我们在生成查询之前计算时间戳,并准确确定我们需要在哪些表中搜索。

    选择计数(*)为myCounter 来自stats2006 在哪里productID IN(5,6,13,123) 和dateMonitor >= 1146430800 和pageName='test'

    /* 这里我们只需要检查 2006 年 5 月 1 日 00:00:00 的时间戳 */

    联合所有

    选择计数(*)为myCounter 来自stats2007 在哪里productID IN(5,6,13,123) 和pageName='test'

    /* 这里我们不需要检查dateMonitor 字段 因为全年与我们的时期相匹配 */

    联合所有

    选择计数(*)为myCounter 来自stats2008 在哪里productID IN(5,6,13,123) 和dateMonitor pageName='test'

    /* 这里我们只需要检查 2008 年 4 月 30 日 23:59:59 的时间戳 */

【讨论】:

  • 谢谢。我不知道 UNION ALL 语法。快速浏览一下您的示例,您的 sql 似乎会产生 3 个结果。每一个都将等于每个 select 语句的受影响的总行数。我的假设是否正确?我还假设如果我查询一个月,这种方法会失败吗?由于每年每个月的时间戳不同,因此每个 select 语句的 COUNT() 将导致 null 或 0。在每个 WHERE 语句中使用不同的条件是否安全?
  • 是的 - 它会产生 3 个结果,这可能是不正确的,因为我刚才注意到您可能想要每个产品的计数,但我假设不是这样,因为您的选择只包含一个计数。这取决于您要查询的确切内容 - 如果您只想获取数据,例如每年 8 月 - 是的,您需要不同的时间戳,但 UNION 语句的重点是您可以为两个不同的查询组合结果,所以 - 是的,在 WHERE 中使用不同的标准是安全的陈述。只要生成的列相同,您就可以更改所有内容。
【解决方案2】:

当您比较“y”年的“x”产品时,为什么不使用 GROUP BY?例如:

Select productID, COUNT(primaryID) as myCounter FROM $tablename WHERE $conditions GROUP BY productID

这将减少查询量并加快处理速度。

【讨论】:

  • 我认为 group by 会失败。我不想计算每个产品,而是要对每个产品求和。例如:计算某个页面中的 5 个产品在特定持续时间内的查看次数,而其他 5 个产品在同一持续时间内另一个页面中的查看次数。在网站的当前状态下,我们不关心单独的视图,而是关心总数。在后期,当我们想检查哪个产品更有效率时,我们会根据产品/页面对视图进行分组,以进行比较。
猜你喜欢
  • 2012-06-29
  • 2011-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多