【问题标题】:How can I optimize this PHP script?如何优化这个 PHP 脚本?
【发布时间】:2018-07-13 17:59:53
【问题描述】:

我将运行一个脚本来检查是否通过 Steam 的 Web API 接受了一些交易报价。我将每 15 秒使用一次 cronjob 运行它。但是我希望它能够尽可能快地优化和运行,我觉得我在这方面做得很糟糕。

如您所见,我放置了一些 cmets 来说明脚本在做什么。但我也会在这里尝试。

  • 它从数据库中收集所有新的交易报价
  • 它检查交易报价是否已被取消
  • 如果没有取消,也就是接受。然后它会收集有关优惠的信息。
  • 如果 bot-inventory 包含玩家存放的物品。数据库将设置 status = 1
  • 然后它会删除交易,因为它已经完成了

感觉这个脚本运行慢,要不要换成mysqli?或者也许用 assoc 替换 mysql_fetch_array?我能做些什么来优化它。运行速度非常重要,快于 15 秒。

    <?php
require('xxxxxx/xx.php');

        //Getting bot-items
         $jsonInventory = file_get_contents('https://steamcommunity.com/profiles/76561xxxxx8959977/inventory/json/730/2');
         $data = json_decode($jsonInventory, true);

        //Getting tradeoffers
        $tradeoffers = mysql_query("SELECT * FROM tradeoffers");
        while($trade = mysql_fetch_array($tradeoffers)) {

        //Getting information about trade-offer
        $url = file_get_contents("https://api.steampowered.com/IEconService/GetTradeOffer/v1/?key=3593xxxxxB6FFB8594D8561374154F7&tradeofferid=".$trade['tradeofferid']."&language=en_us");
        $json = json_decode($url, true);

        //Checking if trade has been completed
        if (isset($json['response']) && isset($json['response']['offer'])) {


        if($json['response']['offer']['trade_offer_state'] == 1 || $json['response']['offer']['trade_offer_state'] == 5 || $json['response']['offer']['trade_offer_state'] == 6 || $json['response']['offer']['trade_offer_state'] == 7 || $json['response']['offer']['trade_offer_state'] == 8 || $json['response']['offer']['trade_offer_state'] == 10 || $json['response']['offer']['trade_offer_state'] == 11) {
            mysql_query("DELETE FROM tradeoffers WHERE tradeofferid = '".$trade['tradeofferid']."'");
            mysql_query("DELETE FROM items WHERE tradeofferid = '".$trade['tradeofferid']."'");
        } 

            if($json['response']['offer']['trade_offer_state'] == 3) {


            if(isset($data['rgDescriptions'])) {

                $itemsinfo = mysql_query("SELECT * FROM items WHERE tradeofferid = '".$trade['tradeofferid']."'");
                while($item = mysql_fetch_array($itemsinfo)) {

                foreach($data['rgInventory'] as $inv) {
                $desc = $data['rgDescriptions'][ $inv['classid'] .'_'. $inv['instanceid'] ]; 

            if($desc['icon_url'] == $item['iconurl']) {
                mysql_query("UPDATE items SET assetid = '".$inv['id']."' WHERE iconurl = '".$item['iconurl']."'");
                mysql_query("UPDATE items SET status = 1 WHERE iconurl = '".$item['iconurl']."'");

                   }
                }    
              }
            }
            //Deleting the trade-offer from the database.
            mysql_query("DELETE FROM tradeoffers WHERE tradeofferid = '".$trade['tradeofferid']."'");
        }
    } else {
        mysql_query("DELETE FROM tradeoffers WHERE tradeofferid = '".$trade['tradeofferid']."'");
        mysql_query("DELETE FROM items WHERE tradeofferid = '".$trade['tradeofferid']."'");
    }
 }
 echo 'Finished';
?>

【问题讨论】:

  • mysql 已弃用,不应再使用。用户 mysqli 或 PDO。见php.net/manual/en/function.mysql-query.php
  • 是的,我知道如何使用 mysqli,但是由于一位老开发人员,该站点的其余部分在 mysql 上运行。但是我可以使用 mysqli 运行这个脚本,对吗?不打扰其余代码?
  • 从 mysql 更改为 mysqli 不太可能有助于提高性能。
  • @Karldrakar 是的,如果这是孤立的。此外,蒸汽 API 似乎有一个 GetTradeOffers 路由,因此您可以批量获取它们并从数据库中批量删除/更新内容(可能我不知道 API 是否会为您提供足够的信息以在批量请求)。
  • 您的内部file_get_contents() 总是访问相同的 URL。没有必要重复做。在脚本开头执行一次。

标签: php mysql mysqli


【解决方案1】:

提高性能的一个级别是从 file_get_contents 切换到 curl 以从 API 获取数据。 curl 通常更快。此外,使用 curl 您可以并行运行多个请求,这会带来另一个性能提升(如果您能够并行化您的请求)。

另见question。

另一个级别是并行化您在迁移到mysqli 后可以执行的数据库调用。有关详细信息,请参阅this question。 (再次假设它是可能的并且在逻辑上是有意义的)

【讨论】:

  • 我不习惯卷曲,是火箭科学吗?
  • 不,它实际上是用 PHP(和许多其他语言)进行 HTTP 请求的标准方式。
  • 执行多卷曲有点复杂,但只需为当前的每个 API 调用切换到简单的卷曲调用就应该会带来显着的改进。
【解决方案2】:

首先,我建议您放弃 mysql_* 函数并使用 PDO 或 mysqli。

优化。我没有运行你的代码,但有一些指针:

"SELECT * FROM" 可能很慢。尽量只使用您需要的字段。

您正在更新 'WHERE iconurl = '".$item['iconurl']."'"'. 该字段是否已编入索引?

是否有必要删除这些记录?那是一个缓慢的操作。如果您标记它们会发生什么,例如complete = 1?(如果您的桌子太拥挤,您以后仍然可以一次性删除它们)

【讨论】:

  • 将更改查询"SELECT * FROM tradeoffers" 以仅选择特定列。 $item['iconurl'] 已编入索引。
  • “tradeofferid”字段呢,它也被索引了吗?
  • 我不这么认为
  • 好吧,您的代码中有多个“WHERE tradeofferid =”实例,因此它可能会加快数据库速度。但 DELETE 可能是最大的性能损失,因为表在丢失记录后需要再次重建其索引。
【解决方案3】:

这里有一些很好的答案,我将首先附议他们在简短摘要中所说的话,然后加上我的两分钱。

(1) 您最大的性能提升将来自 Erik 关于 cURL 的两个建议。切换到 cURL 会稍微提高性能(每次调用可能会增加 0.5 到 1 秒或更多),但使用 multi-curl 并行调用两个 URL 可能会提供此处所有建议的绝对最大好处,没有问题(因为您正在循环执行这些网络获取)。这是别人写的一个类,它稍微简化了多卷曲:

<?php
// LICENSE: PUBLIC DOMAIN
// The author disclaims copyright to this source code.
// AUTHOR: Shailesh N. Humbad
// SOURCE: https://www.somacon.com/p539.php
// DATE: 6/4/2008

// index.php
// Run the parallel get and print the total time
$s = microtime(true);
// Define the URLs
$urls = array(
  "http://localhost/r.php?echo=request1",
  "http://localhost/r.php?echo=request2",
  "http://localhost/r.php?echo=request3"
);
$pg = new ParallelGet($urls);
print "<br />total time: ".round(microtime(true) - $s, 4)." seconds";

// Class to run parallel GET requests and return the transfer
class ParallelGet
{
  function __construct($urls)
  {
    // Create get requests for each URL
    $mh = curl_multi_init();
    foreach($urls as $i => $url)
    {
      $ch[$i] = curl_init($url);
      curl_setopt($ch[$i], CURLOPT_RETURNTRANSFER, 1);
      curl_multi_add_handle($mh, $ch[$i]);
    }

    // Start performing the request
    do {
        $execReturnValue = curl_multi_exec($mh, $runningHandles);
    } while ($execReturnValue == CURLM_CALL_MULTI_PERFORM);
    // Loop and continue processing the request
    while ($runningHandles && $execReturnValue == CURLM_OK) {
      // Wait forever for network
      $numberReady = curl_multi_select($mh);
      if ($numberReady != -1) {
        // Pull in any new data, or at least handle timeouts
        do {
          $execReturnValue = curl_multi_exec($mh, $runningHandles);
        } while ($execReturnValue == CURLM_CALL_MULTI_PERFORM);
      }
    }

    // Check for any errors
    if ($execReturnValue != CURLM_OK) {
      trigger_error("Curl multi read error $execReturnValue\n", E_USER_WARNING);
    }

    // Extract the content
    foreach($urls as $i => $url)
    {
      // Check for errors
      $curlError = curl_error($ch[$i]);
      if($curlError == "") {
        $res[$i] = curl_multi_getcontent($ch[$i]);
      } else {
        print "Curl error on handle $i: $curlError\n";
      }
      // Remove and close the handle
      curl_multi_remove_handle($mh, $ch[$i]);
      curl_close($ch[$i]);
    }
    // Clean up the curl_multi handle
    curl_multi_close($mh);

    // Print the response data
    print_r($res);
  }

}

这里的问题是,这种方法很大程度上取决于您在任何给定时间有多少交易报价,因为您正在为每个报价进行网络调用。如果您有 1,000 个交易报价,您可能必须将它们分成更小的块,这样您就不会同时用大量调用来抨击 Steam API。

(2) 如果您每 15 秒运行一次,那么您可能会在脚本启动时产生一些开销。您可以在无限循环中运行此脚本以消除启动时间,但您必须确保没有内存泄漏,以便您的脚本最终不会耗尽内存:

<?php
set_time_limit(0);
while(true)
{
  ...your code here...

  // Wait 15 seconds before the next round
  sleep(15);
}

(3) 我假设您的数据库非常小,但如果您在任何给定表中有 10k 或更多记录,那么索引将很重要,正如 Herco 所提到的。如果没有良好的索引,您的 SQL 查询将会受到影响。

但是,为了您的最佳改进,我将较少关注#3,而更多地关注#1 和#2。

【讨论】:

  • 谢谢,我会调查的!
猜你喜欢
  • 1970-01-01
  • 2023-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-27
  • 1970-01-01
  • 1970-01-01
  • 2021-08-27
相关资源
最近更新 更多