【问题标题】:Optimize a PHP / MySQL script to reduce server usage优化 PHP / MySQL 脚本以减少服务器使用率
【发布时间】:2015-04-14 15:34:49
【问题描述】:

我有一个每天浏览量超过 1'000'000 的网站和一个 php 脚本,可以在每个页面上显示 2-3 个横幅广告并计算展示次数

要计算展示次数,每个横幅后面都有一张图片:

<img src="https://example.com/impression.php?client=ABC&banner=XYZ" width=1 height=1>

这是展示脚本的示例:

require('global.php'); // connect to mysql, read default settings, etc

if( isset( $_GET['client'] ) ) && !empty( $_GET['client'] ) ) { $client = secure( $_GET['client'] ); } else { die('error param'); }
if( isset( $_GET['banner'] ) ) && !empty( $_GET['banner'] ) ) { $banner = secure( $_GET['banner'] ); } else { die('error param'); }

$check_client = mysqli_query( $con, "SELECT id FROM clients WHERE id = '$client' AND status = 1 LIMIT 1" ) or die( 'error mysql' );
if( mysqli_num_rows( $check_client ) == 0 ) { die('error client'); }

$check_banner = mysqli_query( $con, "SELECT campaign_id FROM banners WHERE id = '$banner' AND status = 1 LIMIT 1" ) or die( 'error mysql' );
if( mysqli_num_rows( $check_banner ) == 0 ) { die('error banner'); }    
$read_campaign_id = mysqli_fetch_array( $check_banner ); 

$check_campaign = mysqli_query( $con, "SELECT id FROM campaigns WHERE id = '$read_campaign_id[0]' AND status = 1 LIMIT 1" ) or die( 'error mysql' );
if( mysqli_num_rows( $check_campaign ) == 0 ) { die('error campaign' );  }

$check_unique = mysqli_query( $con, "SELECT id FROM impressions WHERE datetime LIKE '$today%' AND banner = '$banner' AND ip = '$ip' ORDER BY datetime DESC LIMIT 1" ) or die( 'error mysql');
if( mysqli_num_rows( $check_unique ) == 1 ) { $unique = 0; } else { $unique = 1; }

mysqli_query( $con, "INSERT INTO impressions ( client, campaign, banner, datetime, unique, ip, page ) VALUES ( '$client', '$read_campaign_id[0]', '$banner', '$now', '$unique', '$ip', '$url' )" ) or die( 'error mysql' );

header( 'Content-type: image/png' );
header( $path . '/img/pixel.png' );
mysqli_close( $con );
exit;

在这种情况下,优化脚本并减少服务器上的资源使用和并发连接的最佳方法是什么?

  • 例如,它可以在一个查询中加入一些 mysql 查询吗?我认为应该更快,但我不确定...
  • 另一种可能的方法是将展示次数保存在 CSV 文件中,然后每隔 2-5 分钟将这个文件导入 mysql?但它只保存 1 个查询...

编辑: 这是 mysql 表结构(仅包含此示例的有用字段) http://sqlfiddle.com/#!2/1ffe3/1

【问题讨论】:

  • 首先确保您的所有 MySQL 查询都经过优化。使用EXPLAIN 查询检查它们。
  • 您在展示次数表上特别有哪些索引?
  • @Mark Ba​​ker:所有 id 列都是主要的。 datetime(在印象表中)是一个索引。横幅表中的 campaign_id 和印象表中的 client,campaign,banner 对更新有外键约束,所以我认为它们是自动索引
  • 我怀疑您可以将其减少到 1 - 3 个查询,而不是 5 个。如果您可以为每个查询发布 SHOW CREATE TABLE 或创建 sqlfiddle,我们会更容易提出建议和帮助。
  • 为什么不解析 apache 日志文件,而不是“保存到 CSV”?他们有你需要的所有信息吗?这里没有任何会话

标签: php mysql optimization ads banner


【解决方案1】:

是的,您可以在单个查询中运行所有选择。

相当令人困惑(因为您只需要每个行,并且它们之间没有明显的关系)您可以使用笛卡尔积来做到这一点。

您的架构很糟糕(WHERE datetime LIKE '$today%' 使用字符串存储日期!!!!)

当您只想查看是否存在任何行时,为什么要订购 $check_unique 的结果?

SELECT clients.id AS client_id
, banner.campaign_id AS banner_campaign_id
, campaign_id
,(SELECT COUNT(*)
   FROM impressions 
   WHERE impressions.datetime LIKE '$today%' 
   AND impressions.banner = '$banner' 
   AND ip = '$ip' 
   LIMIT 1) AS check_unique
FROM clients
INNER JOIN banners
INNER JOIN campaigns
WHERE clients.id = '$client' AND clients.status = 1
AND banners.id = '$banner' AND banners.status = 1
AND campaigns.id = '$read_campaign_id[0]' AND campaigns.status = 1
LIMIT 1

或者,您可以将其展开为 UNION,但这不那么有趣。

【讨论】:

  • 谢谢! “日期时间”列是日期时间类型datetime datetime NOT NULL 为什么它很烂?我用ORDER BY datetime DESC是因为check是$today,避免mysql读取旧记录,不是更好吗?
  • 那么你的查询很糟糕 - 应该是一个范围操作,对文字进行任何隐式强制转换。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-11
相关资源
最近更新 更多