使用 AJAX 的 SQL 表更新频率(以秒为单位)

发布于 2025-01-10 22:53:30 字数 7880 浏览 2 评论 0原文

我有一个不同的用户可以更新数据的表单。我已经使用 AJAX 实现了写保护脚本,以避免在多个用户在表单中工作时发生冲突。因为我不太了解;)我已经通过这种方式解决了这个问题:

如果用户打开表单,相关的 user_id 和时间戳将在我的 SQL 表中更新为特定的表单 ID。使用 AJAX,我每 3 秒检查一次该用户是否仍在表单上并更新时间戳。如果另一个用户并行打开表单,我会检查此时间戳是否早于 10 秒。如果这是真的,我每 3 秒用新的 user_id 和时间戳更新 SQL 表。如果第一个用户的时间戳不早于 10 秒,我只需通过添加具有高 z-index 的覆盖层来锁定表单。因此用户可以看到信息但不能更改它。我还显示了此表单当前已锁定的信息。

这听起来很简单,而且正在工作......或多或少......

我的问题来了:

似乎由于对我来说一些未知的原因,SQL 表不会每 3 秒更新一次。 (编辑:)有时我可以正常工作更长的时间。但有时(但不是很常见)SQL 更新未在 9 秒内启动(这是 3 次尝试)。我在下面添加了我的脚本,可能我的例程并不适合我的需求?如果您能帮助我优化它以避免更新延迟(希克斯),我会很高兴。

我在当地工作。使用 XAMPP 控制面板 v3.2.4。 PHP 版本 7.4.6

我的前端页面中的代码:

<script type="text/javascript">
 var auto_refresh = setInterval(
     function() {
         $('#login')
             .load('ajax-audit_execute_login.php?audit_id=<?= $audit_id ?>&user_id=<?= $user['id'] ?>');
     }, 3000); // refresh every 3000 milliseconds
</script>

<div id="login">
<?php
    // Check if someone is logged in into that form
    $statement = $pdo->prepare("SELECT login_at, login_by FROM audit WHERE id = :id");
    $result = $statement->execute(array(':id' => $audit_id));
    $login = $statement->fetch();

    // Calculate the tim ebetween the now and the latest login timestamp
    $ts2 = strtotime(date("Y-m-d H:i:s"));
    $ts1 = strtotime($login['login_at']);
    $seconds_diff = $ts2 - $ts1;

   // If its the same user who locked the form latest do just an update of the timestamp
    if ($login['login_by'] == $user['id']) {        
        $query = "UPDATE audit  
        SET login_at = :login_at, 
        login_by = :login_by  
        WHERE id = :audit_id";
        $pdoResult = $pdo->prepare($query);
        $pdoExec = $pdoResult->execute(array(":login_at" => date("Y-m-d H:i:s"), ':audit_id' => $audit_id, ':login_by' => $user['id']));  
        
        $locked = 0; // Form is not locked
        
    } else {
       // If its another user check if the timestamp is older then 10 seconds
        if ($seconds_diff > 10) {
            // The timestamp is older then 10 seconds. Update the SQL data for new user.
            $query = "UPDATE audit  
            SET login_at = :login_at, 
            login_by = :login_by  
            WHERE id = :audit_id";
            $pdoResult = $pdo->prepare($query);
            $pdoExec = $pdoResult->execute(array(":login_at" => date("Y-m-d H:i:s"), ':audit_id' => $audit_id, ':login_by' => $user['id']));
            
            $locked = 0; // Form is unlocked for the new user
            
        } else {
            // Timestamp is not older so the form is locked
            $statement = $pdo->prepare("SELECT users.vorname, users.nachname, companies.company_name 
                                        FROM users 
                                        JOIN companies ON users.cid = companies.cid 
                                        WHERE users.id = :id");
            $result = $statement->execute(array(':id' => $login['login_by']));
            $locked_by = $statement->fetch();

            $locked = 1; // Form is locked
        }
    }
    ?>

<?php if ($locked == 1) { ?>
  <style>
    div.fadeMe {
        opacity: 0.1;
        background: #000;
        width: 100%;
        height: 100%;
        z-index: 100;
        top: 0;
        left: 0;
        position: fixed;
    }
  </style>
<?php 
// If the form is locked adding a transparent overlay to avoid changes on the form.
// Add a alert and inform the user about the situation.
?>
<div class="fadeMe"></div>
  <div class="alert alert-danger mt-0 mb-0 rounded-0" role="alert">
         <p class="mb-0 text-center">
            <i class="fa-solid fa-lock mr-1"></i> 
            Die Bearbeitung dieses Fragebogens ist durch <?=$locked_by['vorname']?> <?=$locked_by['nachname']?> [<?=$locked_by['company_name']?>] gesperrt. </p>
   </div>
 <?php } else { ?>
 <?php } ?>
</div>

我在“后台”的AJAX文件中的代码: 这或多或少与前端相同。

<?php
$audit_id = $_GET['audit_id'];
$user_id = $_GET['user_id'];
?>

<div id="login">
<?php
    // Check if someone is logged in into that form
    $statement = $pdo->prepare("SELECT login_at, login_by FROM audit WHERE id = :id");
    $result = $statement->execute(array(':id' => $audit_id));
    $login = $statement->fetch();

    // Calculate the tim ebetween the now and the latest login timestamp
    $ts2 = strtotime(date("Y-m-d H:i:s"));
    $ts1 = strtotime($login['login_at']);
    $seconds_diff = $ts2 - $ts1;

   // If its the same user who locked the form latest do just an update of the timestamp
    if ($login['login_by'] == $user_id) {        
        $query = "UPDATE audit  
        SET login_at = :login_at, 
        login_by = :login_by  
        WHERE id = :audit_id";
        $pdoResult = $pdo->prepare($query);
        $pdoExec = $pdoResult->execute(array(":login_at" => date("Y-m-d H:i:s"), ':audit_id' => $audit_id, ':login_by' => $user_id));  
        
        $locked = 0; // Form is not locked
        
    } else {
       // If its another user check if the timestamp is older then 10 seconds
        if ($seconds_diff > 10) {
            // The timestamp is older then 10 seconds. Update the SQL data for new user.
            $query = "UPDATE audit  
            SET login_at = :login_at, 
            login_by = :login_by  
            WHERE id = :audit_id";
            $pdoResult = $pdo->prepare($query);
            $pdoExec = $pdoResult->execute(array(":login_at" => date("Y-m-d H:i:s"), ':audit_id' => $audit_id, ':login_by' => $user_id));
            
            $locked = 0; // Form is unlocked for the new user
            
        } else {
            // Timestamp is not older so the form is locked
            $statement = $pdo->prepare("SELECT users.vorname, users.nachname, companies.company_name 
                                        FROM users 
                                        JOIN companies ON users.cid = companies.cid 
                                        WHERE users.id = :id");
            $result = $statement->execute(array(':id' => $login['login_by']));
            $locked_by = $statement->fetch();

            $locked = 1; // Form is locked
        }
    }
    ?>

<?php if ($locked == 1) { ?>
  <style>
    div.fadeMe {
        opacity: 0.1;
        background: #000;
        width: 100%;
        height: 100%;
        z-index: 100;
        top: 0;
        left: 0;
        position: fixed;
    }
  </style>
<?php 
// If the form is locked adding a transparent overlay to avoid changes on the form.
// Add a alert and inform the user about the situation.
?>
<div class="fadeMe"></div>
  <div class="alert alert-danger mt-0 mb-0 rounded-0" role="alert">
         <p class="mb-0 text-center">
            <i class="fa-solid fa-lock mr-1"></i> 
            <?php 
            // just for development purpose only to see the reaction time on the SQL update
            // See every 3 seconds the updated time stamp and the calculated time difference in sseconds.
            echo $login['login_at'];
            echo " ";
            echo $seconds_diff 
            ?> 
            Die Bearbeitung dieses Fragebogens ist durch <?=$locked_by['vorname']?> <?=$locked_by['nachname']?> [<?=$locked_by['company_name']?>] gesperrt. </p>
   </div>
 <?php } else { ?>
 <?php } ?>
</div>

I have a form where different users can update data. I have implemented a write protection script with AJAX to avoid conflicts if more then one user is working in the form. As i do not know it better ;) i have solved this in that way:

If the user open form the related user_id and timestamp will be updated in my SQL table for the specific form ID. With AJAX i check every 3 seconds if this user is still on the form and do a update of the timestamp. If another user is opening the form in parallel, i check if this timestamp is older then 10 seconds. If this is true i update the SQL table with the new user_id and timestamp every 3 seconds. If the timestamp from the first user is not older then 10 seconds i just lock the form by adding a overlay with a high z-index. So the user can see the infomation but can not change it. I also show a information that this form is currently locked.

This sounds simple and is working ... more or less...

Here comes my problem:

It seems like that due to for me some unknown reason the SQL table does not gets updated every 3 seconds. (Edit:) I is working correctly sometimes for a longer time. But sometimes but not very often the SQL update is not initiated within 9 seconds (this is 3 tries). I added my script bellow, may be my routine is not optimal for my needs? Would be happy if you can help me to optimize it to avoid those update delays (hicks).

I am working local. Using the XAMPP Control Panel v3.2.4. PHP Version 7.4.6

Code in my frontend page:

<script type="text/javascript">
 var auto_refresh = setInterval(
     function() {
         $('#login')
             .load('ajax-audit_execute_login.php?audit_id=<?= $audit_id ?>&user_id=<?= $user['id'] ?>');
     }, 3000); // refresh every 3000 milliseconds
</script>

<div id="login">
<?php
    // Check if someone is logged in into that form
    $statement = $pdo->prepare("SELECT login_at, login_by FROM audit WHERE id = :id");
    $result = $statement->execute(array(':id' => $audit_id));
    $login = $statement->fetch();

    // Calculate the tim ebetween the now and the latest login timestamp
    $ts2 = strtotime(date("Y-m-d H:i:s"));
    $ts1 = strtotime($login['login_at']);
    $seconds_diff = $ts2 - $ts1;

   // If its the same user who locked the form latest do just an update of the timestamp
    if ($login['login_by'] == $user['id']) {        
        $query = "UPDATE audit  
        SET login_at = :login_at, 
        login_by = :login_by  
        WHERE id = :audit_id";
        $pdoResult = $pdo->prepare($query);
        $pdoExec = $pdoResult->execute(array(":login_at" => date("Y-m-d H:i:s"), ':audit_id' => $audit_id, ':login_by' => $user['id']));  
        
        $locked = 0; // Form is not locked
        
    } else {
       // If its another user check if the timestamp is older then 10 seconds
        if ($seconds_diff > 10) {
            // The timestamp is older then 10 seconds. Update the SQL data for new user.
            $query = "UPDATE audit  
            SET login_at = :login_at, 
            login_by = :login_by  
            WHERE id = :audit_id";
            $pdoResult = $pdo->prepare($query);
            $pdoExec = $pdoResult->execute(array(":login_at" => date("Y-m-d H:i:s"), ':audit_id' => $audit_id, ':login_by' => $user['id']));
            
            $locked = 0; // Form is unlocked for the new user
            
        } else {
            // Timestamp is not older so the form is locked
            $statement = $pdo->prepare("SELECT users.vorname, users.nachname, companies.company_name 
                                        FROM users 
                                        JOIN companies ON users.cid = companies.cid 
                                        WHERE users.id = :id");
            $result = $statement->execute(array(':id' => $login['login_by']));
            $locked_by = $statement->fetch();

            $locked = 1; // Form is locked
        }
    }
    ?>

<?php if ($locked == 1) { ?>
  <style>
    div.fadeMe {
        opacity: 0.1;
        background: #000;
        width: 100%;
        height: 100%;
        z-index: 100;
        top: 0;
        left: 0;
        position: fixed;
    }
  </style>
<?php 
// If the form is locked adding a transparent overlay to avoid changes on the form.
// Add a alert and inform the user about the situation.
?>
<div class="fadeMe"></div>
  <div class="alert alert-danger mt-0 mb-0 rounded-0" role="alert">
         <p class="mb-0 text-center">
            <i class="fa-solid fa-lock mr-1"></i> 
            Die Bearbeitung dieses Fragebogens ist durch <?=$locked_by['vorname']?> <?=$locked_by['nachname']?> [<?=$locked_by['company_name']?>] gesperrt. </p>
   </div>
 <?php } else { ?>
 <?php } ?>
</div>

My code in the AJAX file in "background":
Which is more or less the same as in the front end.

<?php
$audit_id = $_GET['audit_id'];
$user_id = $_GET['user_id'];
?>

<div id="login">
<?php
    // Check if someone is logged in into that form
    $statement = $pdo->prepare("SELECT login_at, login_by FROM audit WHERE id = :id");
    $result = $statement->execute(array(':id' => $audit_id));
    $login = $statement->fetch();

    // Calculate the tim ebetween the now and the latest login timestamp
    $ts2 = strtotime(date("Y-m-d H:i:s"));
    $ts1 = strtotime($login['login_at']);
    $seconds_diff = $ts2 - $ts1;

   // If its the same user who locked the form latest do just an update of the timestamp
    if ($login['login_by'] == $user_id) {        
        $query = "UPDATE audit  
        SET login_at = :login_at, 
        login_by = :login_by  
        WHERE id = :audit_id";
        $pdoResult = $pdo->prepare($query);
        $pdoExec = $pdoResult->execute(array(":login_at" => date("Y-m-d H:i:s"), ':audit_id' => $audit_id, ':login_by' => $user_id));  
        
        $locked = 0; // Form is not locked
        
    } else {
       // If its another user check if the timestamp is older then 10 seconds
        if ($seconds_diff > 10) {
            // The timestamp is older then 10 seconds. Update the SQL data for new user.
            $query = "UPDATE audit  
            SET login_at = :login_at, 
            login_by = :login_by  
            WHERE id = :audit_id";
            $pdoResult = $pdo->prepare($query);
            $pdoExec = $pdoResult->execute(array(":login_at" => date("Y-m-d H:i:s"), ':audit_id' => $audit_id, ':login_by' => $user_id));
            
            $locked = 0; // Form is unlocked for the new user
            
        } else {
            // Timestamp is not older so the form is locked
            $statement = $pdo->prepare("SELECT users.vorname, users.nachname, companies.company_name 
                                        FROM users 
                                        JOIN companies ON users.cid = companies.cid 
                                        WHERE users.id = :id");
            $result = $statement->execute(array(':id' => $login['login_by']));
            $locked_by = $statement->fetch();

            $locked = 1; // Form is locked
        }
    }
    ?>

<?php if ($locked == 1) { ?>
  <style>
    div.fadeMe {
        opacity: 0.1;
        background: #000;
        width: 100%;
        height: 100%;
        z-index: 100;
        top: 0;
        left: 0;
        position: fixed;
    }
  </style>
<?php 
// If the form is locked adding a transparent overlay to avoid changes on the form.
// Add a alert and inform the user about the situation.
?>
<div class="fadeMe"></div>
  <div class="alert alert-danger mt-0 mb-0 rounded-0" role="alert">
         <p class="mb-0 text-center">
            <i class="fa-solid fa-lock mr-1"></i> 
            <?php 
            // just for development purpose only to see the reaction time on the SQL update
            // See every 3 seconds the updated time stamp and the calculated time difference in sseconds.
            echo $login['login_at'];
            echo " ";
            echo $seconds_diff 
            ?> 
            Die Bearbeitung dieses Fragebogens ist durch <?=$locked_by['vorname']?> <?=$locked_by['nachname']?> [<?=$locked_by['company_name']?>] gesperrt. </p>
   </div>
 <?php } else { ?>
 <?php } ?>
</div>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

半城柳色半声笛 2025-01-17 22:53:30

好吧,这里有很多问题。

  1. 如果您打算进行任何类型的持续验证,则不会在每个请求中返回整个 HTML 正文,而是返回一个小的 JSON 回复,并使用它通过 JS 控制 HTML。一般来说,将 HTML \ JS 与 API \ 验证层完全分开是一个很好的做法。

  2. 您根本不应该在前端管理数据锁。一般来说,信任客户端来维护数据完整性是一个很大的禁忌。看来这应该作为某种批处理队列来处理。您永远不应该通过锁定和解锁表单客户端来“重新创建该效果”。

  3. 您的“静默”表单密钥(或任何验证内容)应通过会话或 OAUTH 处理,而不是 GET 参数。

也就是说,该问题很可能是由表锁定引起的竞争条件造成的。如果 2 个用户都尝试锁定 \ 访问具有间隙重叠的记录,则用户 2 必须等待,直到用户 1 在该范围上的锁定释放。

这反过来又使得 PHP 在等待表锁释放时“默默地”阻止请求。如果您没有使用上述查询正确索引您的用户\时间戳列,它可能会在等待时锁定所有其他用户更新查询,具体取决于您设置索引的方式...这可能会导致您的计时器创建回调地狱,因为每个用户在登录的整个过程中都在运行它。

您应该专门记录数据库响应时间,以确定这是否是一个问题,但无论如何,都应该解决一些架构问题。

Ok so there's a looooooot of issues here.

  1. If you're gunna do any sort of ongoing validation, you don't return the entire HTML body every request, you return a small JSON reply and use that to control the HTML via JS. Generally it's a good practice to separate your HTML \ JS from your API \ validation layer altogether.

  2. You shouldn't be managing data locks on the front end at all. Trusting the client to maintain data integrity is a big no-no in general. It seems like this should be handled as some sort of batched queue. You should never "recreate that effect" via locking and unlocking forms client side.

  3. Your "silent" form key (or any validation stuff) should be handled via sessions or OAUTH, not GET params.

That said the issue is most likely a result of a race condition cause by table locking. If 2 users are both trying to lock \ access records which have a gap overlap user 2 has to wait until user 1's lock releases on that range.

This in turn makes the PHP "silently" block requests while they wait on the table locks to release. If you didn't properly index your user \ timestamp columns with the above queries, it could put a lock on all the other users update queries while it waits depending on how you set up your indexes... Which can then lead to your timer creating callback hell since every user is running it the whole time their logged in.

You should log your DB response time specifically to pin down if this is an issue, but regardless there's some architectural issues that should be addressed regardless.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文