【问题标题】:Issue with maintaining a MySQL WooCommerce Customer Table维护 MySQL WooCommerce 客户表的问题
【发布时间】:2021-05-07 17:14:01
【问题描述】:

好吧,恐怕我无法发布最低限度的可重现示例,为此我深表歉意。但是,这里什么都没有。

我们提供每周准备的膳食服务。我以多种方式跟踪订单量。以下是相关表的结构:

然后我以多种方式利用突出显示的字段,例如,如果客户从一个多月前的先前订单返回 (last_order_w - prev_order_w > 4),则向送货司机指示。

最近我注意到数据并没有持续正确更新。在过去的 3 周里,我会说这是 5% 的发生率。如果它更一致,我会对我追查问题的能力更有信心,但我什至不知道如何引发它,因为我只是在事后才真正注意到它。

应该导致更新的代码如下:

<?php   
    //retrieve and iterate over IDs of orders placed since last synchronization.
    $newOrders=array_map('reset',$dbh->query("select id from wp_posts where id > (select max(synced) from fitaf_weeks) and post_type='shop_order' and post_status='wc-processing'")->fetchAll(PDO::FETCH_NUM)); 
    foreach($newOrders as $no){
    
        //retrieve the metadata for the current order
        $newMetas=array_map('reset',$dbh->query("select meta_key,meta_value from wp_postmeta where post_id=$no")->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_UNIQUE));
        
        //check if the current order is associated with an existing customer
        $exist=$dbh->query("select * from fitaf_customers where id=".$newMetas['_customer_user'])->fetch();
        
        //if not, gather the information we want to store from this post
        $noExist=[$newMetas['_customer_user'],$newMetas['_shipping_first_name'],$newMetas['_shipping_last_name'],$newMetas['_shipping_address_1'],(strlen($newMetas['_shipping_address_2'])==0?NULL:$newMetas['_shipping_address_2']),$newMetas['_shipping_city'],$newMetas['_shipping_state'],$newMetas['_shipping_postcode'],$phone,$newMetas['_billing_email'],1,1,$no,$newMetas['_paid_date'],$week[3],$newMetas['_order_total']];
        if($exist){
        
            //if we found a record in the customer table, retrieve the data we want to modify
            $oldO=$dbh->query("select last_order_id,last_order,last_order_w,lo,num_orders from fitaf_customers where id=".$newMetas['_customer_user'])->fetch(PDO::FETCH_GROUP|PDO::FETCH_ASSOC|PDO::FETCH_UNIQUE);
            
            //make changes to the retrieved data, and make sure we are storing the most recently used delivery address and prepare the data points for the update command
            $exists=[$phone,$newMetas['_shipping_first_name'],$newMetas['_shipping_last_name'],$newMetas['_shipping_postcode'],$newMetas['_shipping_address_1'],(strlen($newMetas['_shipping_address_2'])==0?NULL:$newMetas['_shipping_address_2']),$newMetas['_shipping_city'],$newMetas['_shipping_state'],$newMetas['_paid_date'],$no,$week[3],$oldO['last_order'],$oldO['last_order_id'],$oldO['last_order_w'],($oldO['num_orders']+1),($oldO['lo']+$newMetas['_order_total']),$newMetas['_customer_user']];
        }
        
        
        if(!$exist){
        
            //if the customer did not exist, perform an insert
            $dbh->prepare("insert into fitaf_customers(id,fname,lname,addr1,addr2,city,state,zip,phone,email,num_orders,num_weeks,last_order_id,last_order,last_order_w,lo) values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)")->execute($noExist);
        }
        else{
        
            //if the customer did exist, update their data
            $dbh->prepare("update fitaf_customers set phone=?,fname=?,lname=?,zip=?,addr1=?,addr2=?,city=?,`state`=?,last_order=?,last_order_id=?,last_order_w=?,prev_order=?,prev_order_id=?,prev_order_w=?,num_orders=?,lo=? where id=?")->execute($exists);
        }
    }
    
    //finally retrieve the most recent post ID and update the field we check against when the syncornization script runs
    $lastPlaced=$dbh->query('select max(id) from wp_posts where post_type="shop_order"')->fetch()[0];
    $updateSync=$dbh-> query("update fitaf_weeks set synced=$lastPlaced order by id desc limit 1");
?>

不幸的是,我没有任何相关的错误日志可以显示,但是,当我记录了这篇文章的代码时,我意识到了一个潜在的缺点。我应该利用从新帖子的初始查询中检索到的数据,而不是在执行此逻辑后选择最高的帖子 ID。但是,我的脚本上运行了计时器,并且这部分运行很长时间没有超过 3 秒。因此,每 5 分钟在 cron 上运行的脚本似乎不太可能出现这种意外重叠?

虽然我已做出更改以从 $newOrders 中弹出最高 ID,并希望它能解决问题,但我仍然很想知道是否有人对导致此逻辑在如此低的发生率下失败的原因有任何见解.

【问题讨论】:

    标签: php mysql woocommerce pdo


    【解决方案1】:

    您的问题似乎来自访问您的数据库的多个操作之间的竞争条件。

    首先,您的最后几行代码执行SELECT MAX(ID),然后使用该值更新某些内容。你不能那样做™。如果其他人在您认为相关的条目之后的任何时间向该 wp_posts 表添加一行,您将使用错误的 ID。我不太了解您的应用程序,无法推荐修复程序。但我知道这是一个严重且臭名昭著的问题。

    您还有另一个可能的竞争条件。你的逻辑是这样的:

    1. 选择一些东西。

    2. 根据您选择的内容做出决定。

    3. 根据该决定插入或更新。

    如果其他一些操作,由数据库的其他用户完成,在步骤 1 和步骤 3 之间进行干预,您的决定可能是错误的。

    您可以通过数据库事务解决此问题。 -&gt;beginTransaction() operation,好吧,开始交易。 -&gt;commit() operation concludes吧。并且,您用于第一步的 SELECT 操作should say SELECT ... FOR UPDATE

    【讨论】:

    • 感谢 O.,提供知识渊博的答复。我的第一反应是“我是数据库的唯一用户,为什么我需要交易?”在某种程度上,我仍然有这种感觉。我的 wp_posts 从生产数据库同步并存在于我的服务器上,一个镜像。无论如何,同步脚本每 5 分钟在 cron 上触发一次。那是唯一一次修改表,所以进一步考虑我不知道这可能是问题所在。同步发生,分析发生,中间没有人接触数据。
    • 我想我会收回的。同步脚本也可以从访问 URL 的用户触发。所以我想你是对的,它可能发生在 cron 触发并且我同时拉出 GUI 版本时。尽管我确实认为您的建议是有根据的,并且我应该将交易添加到我的工具箱中,但我不确定我现在是否有时间学习和实施它们。所以我的新方法不是重新查询以获取MAX(ID),而是从$newOrders 数组中弹出最高ID。有了关于我的设置的附加信息,你认为这对现在来说是一个足够的创可贴吗?
    • 是的。在您要查找的 INSERT 之后,从直接从 -&gt;lastInsertId() value 获取的某个地方获取 ID 是完全正确的做法。 尽管如此, 如果您正在处理其他人的钱,您最终需要正确地进行此交易。
    猜你喜欢
    • 1970-01-01
    • 2010-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多