Дублирующие записи после оператора вставки в Mysql с использованием PHP - PullRequest
0 голосов
/ 04 января 2019

Сегодня я выпустил новое приложение для одного из моих клиентов, и с оператором INSERT произошло нечто необычное. Во время тестов мы не сталкивались с чем-то похожим, и я не уверен, произошло ли это из-за одновременного доступа большого количества пользователей (100 пользователей) или это связано с чем-то другим. Таким образом, после 1 вставки несколько раз он вставил 1 запись в таблицу базы данных, некоторые другие 2-3 дубликата ... Я не использую транзакции, это может решить эту проблему? Ниже вы можете найти мой код и код с транзакциями в на случай, если это будет решением. Большое спасибо за вашу помощь!

$submit_the_bill = "INSERT INTO bill_status
(`bill_status_bill_id`,`bill_status_username`, `bill_status_phone_number`, `bill_status_status`,`bill_paid`, `deduction`)
VALUES ('".$bill_id."', '".$_SESSION['username']."', '".$_SESSION['phone_number']."','1','0','".$deduction."')";
$submit_the_bill_result = $conn->query($submit_the_bill);

//TRANSACTION STARTS HERE
try {
$conn->autocommit(FALSE);
$submit_the_bill = "INSERT INTO bill_status
(`bill_status_bill_id`,`bill_status_username`, `bill_status_phone_number`, `bill_status_status`,`bill_paid`, `deduction`)
VALUES ('".$bill_id."', '".$_SESSION['username']."', '".$_SESSION['phone_number']."','1','0','".$deduction."')";
$submit_the_bill_result = $conn->query($submit_the_bill);
$conn->commit();
} catch (Exception $e) {
// An exception has been thrown
// We must rollback the transaction
$conn->rollback();
}

Это полная страница, информация поступает с предыдущей страницы с формой и переходит на домашнюю страницу после выполнения запроса и отправки электронного письма. Есть идеи?

<?php session_start();

require('include/config.php'); //database connection

require "PHPMailer/PHPMailerAutoload.php";

$bill_id = $_POST['bill_id'];
$user_phone_number = $_POST['user_phone_number'];
$deduction = $_POST['deduction'];

$net_value_hidden = round($_POST['net_value_hidden'],2);
$official_cost_hidden = round($_POST['official_cost_hidden'],2);
$personal_cost_hidden = round($_POST['personal_cost_hidden'],2);
$tax_hidden = round($_POST['tax_hidden'],2);
$total_hidden = round($_POST['total_hidden'],2);
$total_private_price_with_vat = ($personal_cost_hidden/100)*20;
$total_private_price_with_vat = round($total_private_price_with_vat,2);
$total_payable_ammount = round($personal_cost_hidden + $total_private_price_with_vat,2);

if($total_hidden == 0) {$deduction = 4;}

if($deduction == 1) {$deduction_email = "Salary";}
else if($deduction == 2) {$deduction_email = "Cash";}
else if($deduction == 3) {$deduction_email = "Bank Transfer";}
else {$deduction_email = "No deduction";}

$submit_the_bill = "INSERT INTO bill_status
(`bill_status_bill_id`,`bill_status_username`, `bill_status_phone_number`, `bill_status_status`,`bill_paid`, `deduction`)
VALUES ('".$bill_id."', '".$_SESSION['username']."', '".$_SESSION['phone_number']."','1','0','".$deduction."')";
$submit_the_bill_result = $conn->query($submit_the_bill);

$sql_smtp = "SELECT * FROM smtp_settings";
$result_smtp = $conn->query($sql_smtp);
if ($result_smtp->num_rows > 0) {   
    while($row = $result_smtp->fetch_assoc()) {
        $smtp_host = $row['smtp_server'];
        $smtp_username = $row['smtp_user_email'];
        $smtp_password = $row['smtp_user_password'];
        $smtp_ssl = $row['smtp_ssl'];
        $smtp_port = $row['smtp_port'];
    }
}
if ($smtp_ssl == 1) {$smtp_ssl = "ssl";} else {$smtp_ssl = "tls";}

$mail = new PHPMailer;

$mail->isSMTP();                                      // Set mailer to use SMTP
$mail->Host = $smtp_host;  // Specify main and backup SMTP servers
$mail->SMTPAuth = true;                               // Enable SMTP authentication
$mail->Username = $smtp_username;                 // SMTP username
$mail->Password = $smtp_password;                           // SMTP password
$mail->SMTPSecure = $smtp_ssl;                            // Enable TLS encryption, `ssl` also accepted
$mail->Port = $smtp_port;                                    // TCP port to connect to

$mail->setFrom('email@example.com', 'Email');
$mail->addAddress($_SESSION['username']);               // Name is optional
$mail->isHTML(true);                                  // Set email format to HTML

$mail->Subject = $_SESSION['username']." submitted new bill - ".$_SESSION['bill_period'];
$mail->Body    = $_SESSION['username']."(".$_SESSION['phone_number'].") Submit New Bill - ".$_SESSION['bill_period']." <br/><br/>";

$mail->AltBody = $_SESSION['username']."(".$_SESSION['phone_number'].") Submit New Bill - ".$_SESSION['bill_period']." <br/><br/>";

if(!$mail->send()) {
    echo 'Message could not be sent.';
    echo 'Mailer Error: ' . $mail->ErrorInfo;
} else {
    //echo 'E-mail sent';
}
unset($_SESSION['bill_period']);
$conn->close();

header("Location:location"); //going to the home page

?> 

1 Ответ

0 голосов
/ 04 января 2019

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

Почему код выполняется дважды? На это нельзя ответить с помощью информации в вашем вопросе. Мы можем только догадываться.

Вероятной возможностью является дублирование запроса на вставку либо со стороны пользователя (выполнение обновления, потому что это занимает слишком много времени, либо из-за перезапуска Chrome и повторного открытия вкладок, либо по какой-то другой причине), либо со стороны балансировщика нагрузки (повторение неудачных запросов ) или по вызову AJAX или что-то еще.

Или, возможно, ваш рабочий процесс заставляет нескольких людей выполнять одно и то же действие. Например, если несколько человек выбирают «верхний счет», смотрят на него и затем отмечают его как завершенный, вам нужно сделать что-то в приложении, чтобы убедиться, что они не все работают с одинаковыми записями примерно в одно и то же время.

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

Ваш подход к исправлению будет зависеть, скорее всего, от причины и вашей бизнес-логики. Вы можете использовать дополнительные таблицы, чтобы «проверить» идентификаторы счетов для определенных пользователей, чтобы убедиться, что один и тот же счет не будет вставлен дважды. Вы можете присвоить запросам на вставку идентификатор, так что отправка одной и той же вставки дважды может быть эффективно дедуплицирована (эта последняя реализована практически при каждом процессе оформления корзины в электронной торговле, в противном случае обновление снова покупает ваши вещи или перезапускает незавершенный процесс оплаты!).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...