【问题标题】:PHP function execution time taking longPHP函数执行时间长
【发布时间】:2020-12-13 11:23:35
【问题描述】:

我有很好的书面功能;我想,但是当我在一个有很多事务的客户端上使用这个函数时,它需要大约 20 秒,每次我添加新事务时都会增加。此功能从一开始就开始重新计算所有客户债务。 我正在使用:

  • WAMP 服务器 3.1.4
  • PHP 7.2.10
  • MYSQL 5.7.23

我已经尝试在 php.ini 中进行一些更改

php.ini

post_max_size = 256M
upload_max_filesize = 128M
memory_limit = 1G
max_input_vars = 10000

my.ini

key_buffer_size = 256M
max_allowed_packet = 128M
sort_buffer_size = 128M
net_buffer_length = 8K
read_buffer_size = 128M
read_rnd_buffer_size = 128M
myisam_sort_buffer_size = 512M
innodb_buffer_pool_size = 256M
innodb_log_file_size = 256M
innodb_log_buffer_size = 512M
[mysqldump]
quick
max_allowed_packet = 512M
[isamchk]
key_buffer_size = 512M
sort_buffer_size = 128M
read_buffer_size = 256M
write_buffer_size = 256M

[myisamchk]
key_buffer_size = 512M
sort_buffer_size_size = 512M
read_buffer_size = 256M
write_buffer_size = 256M

这是函数

function reCalculateAll($conn, $clid, $cltp){
        $stmt = $conn->prepare("SELECT * FROM transactions WHERE client_id = ? AND client_type = ? ORDER BY STR_TO_DATE(transaction_date, '%d/%m/%Y %H:%i:%s') ASC");
        $stmt->bind_param('is', $clid, $cltp);
        $stmt->execute();
        $results = $stmt->get_result();
        $stmt->fetch();
        $numberofrows = $stmt->num_rows;
        while ($row = $results->fetch_assoc()) {
            $r = getAllTransactionsClient($conn, $clid, $cltp);
            $result = array();
            foreach($r as $i => $p){
                $result[$p['client_type'].$p['client_id']][] = $p;
                foreach ($result as $rr){
                    foreach ($rr as $c => $k){
                        reset($rr);
                        $trid = $k['id'];
                        $trcn = $k['client_id'];
                        $trtp = $k['client_type'];
                        $trdt = $k['transaction_date'];
                        if($c === key($rr)){
                            // FIX TRANSACTION
                            $addm = 0;
                            $stmtf = $conn->prepare("UPDATE transactions SET client_oldfunds = ?, client_newfunds = ? + added_amount where id = ?");
                            $stmtf->bind_param('ssi', $addm, $addm, $trid);
                            $stmtf->execute();
                            $stmtf->close();
                            $addm = $k['client_newfunds'];
                            } else {
                            $stmtn = $conn->prepare("UPDATE transactions SET client_oldfunds = ?, client_newfunds = ? + added_amount where id = ?");
                            $stmtn->bind_param('ssi', $addm, $addm, $trid);
                            $stmtn->execute();
                            $stmtn->close();
                            $addm = $k['client_newfunds'];
                        }                               
                            $cnf = getLastDebtFromTransaction($conn, $trtp, $trcn);
                            setDebts($conn, $trtp, $cnf, $trcn);
                    }
                }
            }
            
        }
        $results->free();
        $stmt->execute();
        $stmt->store_result();
        $numberofrows = $stmt->num_rows;
        if($numberofrows == 0){
            setDebts($conn, $cltp, '0', $clid);
        }
        $stmt->close();
    }

getAllTransactionsClient 函数

    function getAllTransactionsClient($conn, $clid, $cltp){
        $stmt = $conn->prepare("SELECT * FROM transactions WHERE client_id = ? AND client_type = ? ORDER BY STR_TO_DATE(transaction_date, '%d/%m/%Y %H:%i:%s') ASC");
        $stmt->bind_param('is', $clid, $cltp);
        $stmt->execute();
        $result = $stmt->get_result();
        $products = array();
        
        while ($row = $result->fetch_assoc()) {
            $products[] = $row;
        }
        return $products;
        $stmt->close();
    }

【问题讨论】:

  • 您介意告诉我们一些指标吗?我们在谈论多少交易?正在执行多少更新查询?
  • @MagnusEriksson 什么样的指标?你是说数据吗?
  • 编辑问题以包含SHOW CREATE transactions 输出。桌子有多大。在循环中进行更新不是数据库友好的。 SQL 服务器可以很好地循环,因此您不需要这样做。 reCalculateAll 应该是一条 SQL 语句。语句准备应该在循环之外。欢迎来到 SO
  • 指标是指数字。如果您的选择查询返回 2 个要迭代的事务,那么 20 秒就很多了。如果您的选择查询返回 1.000.000 个事务,那么 20 秒是相当快的(因为您在循环中更新它们)。
  • @danblack 感谢您花时间阅读我的问题并回答它,我将尝试找到一种方法来替换这个多个循环,但我在压力下创建了这段代码,并且很快就修复了我的软件有问题,仍在寻找更好的想法

标签: php mysql apache wamp


【解决方案1】:

您的代码需要改进,我看​​到一个中有多个循环, 这是你的第一个函数,你可以去掉最后一个,因为它有点没用。

实际功能应该是这样的:

    function reCalculateAll($conn, $client_id, $client_type){
        // THE FOLLOWING QUERY WILL REPLACE this function for you getAllTransactionsClient();
        $stmt = $conn->prepare("SELECT id, added_amount FROM transactions WHERE client_id = ? AND client_type = ? ORDER BY STR_TO_DATE(transaction_date, '%d/%m/%Y %H:%i:%s') ASC");
        $stmt -> bind_param("is", $client_id, $client_type);
        $stmt -> execute();
        $stmt -> store_result();
        $stmt -> bind_result($transaction_id, $added_amount);
        // THIS $oldfunds stands for your old $addm
        $oldfunds = 0;
        while($stmt->fetch()){
            $newfunds = $oldfunds + $added_amount;
            $stmtd = $conn->prepare("UPDATE transactions SET client_oldfunds = ?, client_newfunds = ? WHERE id = ?");
            $stmtd->bind_param("ssi", $oldfunds, $newfunds, $transaction_id);
            $stmtd->execute();
            $oldfunds = $newfunds;
        }
        $stmt->close();
        // this should send 0 if there is no transactions
        setDebts($conn, $client_type, $oldfunds, $client_id);
    }

【讨论】:

    【解决方案2】:

    前导

    我将跳过“回答您的问题”——我认为上面的 cmets 中突出显示了很多问题——并直接针对我认为您希望代码执行的操作找到解决方案……

    问题

    您的代码并不容易理解,但是,我认为问题的要点是:

    1. 您的数据库已因某种方式损坏,client_oldfundsclient_newfunds 字段不再包含正确的数据。

    2. 您相信added_amount 字段是正确的,并希望返回并为每笔交易重新计算上面的字段,以便全部一致?

    数据库

    表结构

    CREATE TABLE transactions (
        id bigint AUTO_INCREMENT PRIMARY KEY,
        client_id bigint,
        client_type varchar(20),
        client_oldfunds decimal(10,2),
        client_newfunds decimal(10,2),
        added_amount decimal(10,2),
        transaction_date varchar(20)
    );
    

    当前数据示例

    假设这里的交易是按日期排序的。

    id  | client_id   | client_type   | client_oldfunds   | client_newfunds   | added_amount
    --- | ----------- | ------------- | ----------------- | ----------------- | --------------
    1   | 1           | type_a        | 12.10             | 1.36              | 3.12
    2   | 1           | type_a        | 6.00              | 432.42            | 4.50
    3   | 1           | type_a        | 30.12             | 1.33              | 100.22
    4   | 1           | type_a        | 23.1              | 1.22              | 10.2
    5   | 1           | type_a        | 123.4             | 55.54             | 12.6
    

    正确数据示例

    假设这里的交易是按日期排序的。

    id  | client_id   | client_type   | client_oldfunds   | client_newfunds   | added_amount
    --- | ----------- | ------------- | ----------------- | ----------------- | --------------
    1   | 1           | type_a        | 0                 | 3.12              | 3.12
    2   | 1           | type_a        | 3.12              | 7.62              | 4.50
    3   | 1           | type_a        | 7.62              | 107.84            | 100.22
    4   | 1           | type_a        | 107.84            | 118.04            | 10.2
    5   | 1           | type_a        | 118.04            | 130.64            | 12.6
    

    代码

    我们希望它做什么

    reCalculateAll{
       0 > Initialise a balance of 0
       1 > SELECT id and amount_added for all related transactions, in order
       2 > UPDATE the client_oldfunds to the balance and client_newfunds to the balance + the added_amount
       3 > UPDATE the balance to the new value (balance + added_amount)
    }
    

    实际代码

    function reCalculateAll($mysqli, $client_id, $client_type){
        $select_sql = "
            SELECT id, added_amount
            FROM transactions
            WHERE client_id = ?
                AND client_type = ?
            ORDER BY STR_TO_DATE(transaction_date, '%d/%m/%Y %H:%i:%s') ASC
        ";
    
        $select_query = $mysqli->prepare($select_sql);
        $select_query->bind_param("is", $client_id, $client_type);
        $select_query->execute();
        $select_query->store_result();
        $select_query->bind_result($transaction_id, $added_amount);
    
        $old_balance = 0;
        
        while($select_query->fetch()){
            $new_balance = $old_balance + $added_amount;
    
            $update_sql  = "
                UPDATE transactions
                SET client_oldfunds = ?,
                    client_newfunds = ?
                WHERE id = ?
            ";
    
            $update_query = $mysqli->prepare($update_sql);
            $update_query->bind_param("ssi", $old_balance, $new_balance, $transaction_id);
            $update_query->execute();
    
            $old_balance = $new_balance;
        }
    }
    

    注意

    您确实应该以 MySQL 格式“Y-m-d H:i:s”存储日期。它使排序更容易;格式应该在日期输出到浏览器时进行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-11-02
      • 1970-01-01
      • 2018-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多