How can I optimize this PHP script?

时间:2018-02-03 10:08:00

标签: php mysql mysqli

I will run a script to check if some trade-offers are accepted through steam's web API. I will run it using cronjob every 15th second. But I want it to be optimized and run as fast as possible, I feel like I have done this very poorly.

As you can see, I've put some comments that tells what the script is doing. But I will try here as well.

  • It collects all the new trade-offers from the database
  • It checks if the trade offer has been cancelled or not
  • If it is not cancelled, aka accepted. Then it collects information about the offer.
  • If the bot-inventory contains the item, that the player deposited. The database will set status = 1
  • Then it will delete the trade-offer, as it has been completed

I feel like this script is running slowly, should I change to mysqli? or maybe replace mysql_fetch_array with assoc? What can I do to optimize this. It is pretty important that it runs fast, quicker than 15 seconds.

    <?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';
?>

3 个答案:

答案 0 :(得分:1)

First, I'd advise you to move away from mysql_* functions and use either PDO or mysqli.

Optimization. I've not run your code but some pointers:

"SELECT * FROM" might be slow. Try to use only the fields you need.

You are updating on 'WHERE iconurl = '".$item['iconurl']."'"'. Is this field indexed?

Is it necessary to DELETE these records? That is a slow operation. What happens if you flag them, e.g. complete = 1? (you may later still delete them in one go if your table gets too crowded)

答案 1 :(得分:1)

One level for increasing performance is to switch from file_get_contents to curl for getting data from the API. curl is usually much faster. Also, with curl you can run multiple requests in parallel, which brings another performance boost (if you are able to parallelize your requests).

See also this question.

Another level is to parallelize your database calls which you could do after you migrated to mysqli. See this question for details. (again assuming its possible and makes sense logic-wise)

答案 2 :(得分:0)

这里有一些很好的答案,我首先会在简短的总结中借用他们所说的内容然后加上我的两分钱。

(1)你最大的表现收益来自Erik关于cURL的两个建议。切换到cURL将提供性能的小幅提升(每次调用可能需要0.5到1秒或更多),但使用多卷曲并行调用两个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的最佳改进。