Время выполнения функции PHP занимает много времени

У меня есть хорошая письменная функция; Я думаю, но когда я использую эту функцию на клиенте, у которого много транзакций, это занимает около 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

и

мой.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();
    }

Не могли бы вы сообщить нам некоторые показатели? О каком количестве транзакций идет речь? Сколько запросов на обновление выполняется?

M. Eriksson 13.12.2020 12:30

@MagnusEriksson, какие показатели? вы имеете в виду данные?

crackry 13.12.2020 12:32

отредактируйте вопрос, чтобы включить вывод SHOW CREATE transactions. Насколько большой стол. Выполнение обновлений в цикле не является чем-то удобным для базы данных. Серверы SQL хорошо выполняют циклы, поэтому вам это не нужно. reCalculateAll должен быть одним оператором SQL. Подготовка заявления должна выходить за рамки цикла. Добро пожаловать в СО

danblack 13.12.2020 12:32

Под показателями я подразумеваю цифры. Если ваш запрос на выборку возвращает 2 транзакции для повторения, то 20 секунд — это много. Если ваш запрос на выборку возвращает 1 000 000 транзакций, то 20 секунд — это довольно быстро (поскольку вы обновляете их в цикле).

M. Eriksson 13.12.2020 12:35

@danblack спасибо, что нашли время прочитать мой вопрос и ответить на него, я постараюсь найти способ заменить эти несколько циклов, но я создал этот код под давлением и в кратчайшие сроки, просто чтобы решить проблему с моим программным обеспечением, все еще ищу для лучших идей

crackry 13.12.2020 12:36

@MagnusEriksson около 40 транзакций, но их количество увеличивается

crackry 13.12.2020 12:37

Что делает getAllTransactionsClient? Вы вызываете процедуру в цикле, каждый раз передавая одни и те же параметры. Вы также передаете $conn, пока у вас все еще есть строки из первого запроса в буфере.

slaakso 13.12.2020 12:43

@slaakso, проверь мой вопрос еще раз, я добавил эту функцию в конец

crackry 13.12.2020 12:48

Итак, вы повторяете один и тот же запрос в цикле, с которого он начался? Это не имеет смысла. Затем у вас есть вызовы getLastDebtFromTransaction и setDebts в цикле, которые, вероятно, делают похожие вещи, и снова вы передаете один и тот же $conn, пока основной запрос все еще выполняется. Может быть, просто перепишите всю функцию как один запрос или вызов хранимой процедуры.

slaakso 13.12.2020 12:54

Можете ли вы объяснить, что ваш код должен делать?

Steven 13.12.2020 14:49
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
2
10
122
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Пре-курсор

Я собираюсь пропустить "ответ на ваш вопрос" - я думаю, что многие проблемы освещены в комментариях выше - и перейду прямо к решению того, что, как я думаю, вы намереваетесь сделать в своем коде...

Проблема

Однако вашему коду нелегко следовать, я считаю, что суть проблемы заключается в следующем:

  1. Ваша база данных каким-то образом повреждена, и поля client_oldfunds и client_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». Это упрощает сортировку; форматирование должно происходить при выводе даты в браузер.

Ответ принят как подходящий

Ваш код нуждается в улучшении, я вижу, что в одном есть несколько циклов, Вот ваша первая функция, вы можете избавиться от последней, потому что она бесполезна.

Фактическая функция должна быть такой:

    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);
    }

Другие вопросы по теме