WooCommerce批量导入大量产品时内存溢出错误的排查与解决方案求助
WooCommerce批量导入大量产品时内存溢出错误的排查与解决方案求助
大家好,我开发了一个集成WooCommerce的WordPress插件,核心功能之一是从外部数据库导入产品。目前遇到一个棘手的问题:当导入产品数量在900以内时一切正常,但超过这个阈值就会触发内存溢出错误。查看debug.log时,错误只指向了“random”函数,完全没法定位真正的问题根源。
我已经尝试了以下几种方案,但都没能解决问题:
- 环境版本更新:确保WordPress、WooCommerce和PHP都使用了最新版本(PHP 8)
- 解耦导入流程:改用Scheduled Actions把导入过程和页面加载分开,避免页面超时,但内存溢出的问题依然存在
- 禁用缓存添加:调用
wp_suspend_cache_addition(true),本来猜测是WordPress/WooCommerce在缓存不必要的产品数据占用内存,但没看到明显效果 - 内存监控调试:添加了内存监控的代码,只能看到内存缓慢增长直到崩溃,还是找不到具体的泄漏点
我的主要限制是:这是一个WordPress/WooCommerce插件,不能假设拥有服务器的直接操作权限;另外,我不想直接操作数据库,必须使用WP/WC的官方函数,毕竟要保证数据安全和兼容性。
我现在有几个疑问想请教大家:
- WordPress和WooCommerce处理这类批量数据导入时,内存管理机制是怎样的?
- 我看了代码,WooCommerce最终会调用
wp_update_post()或wp_insert_post(),底层是$wpdb->update()或$wpdb->insert(),这部分似乎没有缓存操作,那内存泄漏可能出现在哪里?
另外,我怀疑内存泄漏可能出在“检查产品是否已存在”的逻辑里:原本用WP_Query来查询,后来换成get_posts()确实减少了一些内存占用,但问题还是没解决。
编辑1:
我试过用PHP的Scheduled Actions分批执行导入流程,但还是会内存溢出;不过如果用JS通过AJAX分批调用,就能正常处理所有产品。也就是说,浏览器端触发的导入没问题,但后端自动化执行的就不行。
编辑2:
下面是我精简后的核心代码(去掉了大量错误检查和无关变量,保留关键逻辑),如果有不清楚的地方可以告诉我:
function my_update_product_listings() { // 返回包含简单产品、可变产品和变体的数组 $products = my_apiCall( "GET", "API/URL/Sanitized/For/StackOverflow" ); if ( is_wp_error( $products ) ) { return array( 'message' => "Product Update Error" ); } // 更新/插入产品,返回插入、更新数量和错误SKU $updated = my_update_products( $products ); // 过滤掉可变产品和出错的产品,只保留简单产品和变体用于价格/库存更新 $end_products = my_only_end_products($products, $updated); // 触发价格和库存更新动作 do_action( 'post_update_end_products_actions_filter', $end_products); return "complete"; } function my_single_products( $products ) { if ( !$updated ) { $updated = array('new' => 0, 'existing' => 0, 'errors' => array()); } foreach ( $products as $prod ) { $prod_sku = $prod['sku']; $parent = $prod['parent']; $object_term = $parent ? 'variable' : 'simple'; $variation = $prod['parent_sku'] && !$parent; try { $existing = my_is_already_product( $prod_sku, $variation ); } catch ( Exception $e ) { $updated["errors"][] = $prod_sku; continue; } $meta_input = array( '_visibility' => 'visible', '_manage_stock' => 'yes', '_backorders' => 'no', '_downloadable' => 'no', '_featured' => 'no', '_virtual' => 'no', '_sku' => $prod_sku, ); if ( $parent ) { $meta_input['_manage_stock'] = "no"; } $post_info = array( 'post_type' => "product", 'meta_input' => $meta_input, 'post_title' => $prod['description'], ); if ( $existing ) { $post_info['ID'] = $existing; $post_id = wp_update_post( $post_info, true ); } else { $post_info['post_status'] = 'draft'; $post_info['post_name'] = "p_" . $prod_sku; $post_id = wp_insert_post( $post_info, true ); } if ( is_wp_error( $post_id ) ) { $updated['errors'][] = $prod_sku; } else { wp_set_object_terms( $post_id, $object_term, 'product_type' ); $product = wc_get_product( $post_id ); $attributes = array(); foreach ( $prod as $key => $value ) { // 处理以"SPEC"开头的属性字段 if ( str_starts_with( $key, "SPEC" ) && $value != null ) { $attributes['pa_' . strtolower( $key )] = array( 'name' => 'pa_' . strtolower( $key ), 'value' => strtolower( urldecode( $value ) ), 'is_visible' => 1 ); } } if ( $attributes ) { $product->update_meta_data( '_product_attributes', $attributes ); } if ( $existing ) { $updated['existing']++; } else { $updated['new']++; } $product->update_meta_data( 'my_custom_synced_meta_field', 1 ); $product->save(); } } return $updated; } function my_is_already_product( $sku, $variation = false ) { $meta_query = array( 'relation' => 'AND', array( 'key' => '_sku', 'value' => $sku ), array( 'key' => 'my_custom_synced_meta_field', 'value' => 1 ) ); $gotten_posts = get_posts(array( 'post_type' => $variation ? 'product_variation' : 'product', 'post_status' => 'any', 'meta_query' => $meta_query )); if ( count($gotten_posts) === 0 ) { return false; } else if ( count($gotten_posts) === 1 ) { return $gotten_posts[0]->ID; } else if ( count($gotten_posts) > 1 ) { throw new Exception( "Multiple products found with same SKU: ". $sku ); } } function my_update_product_price( $products_prices ) { $price_list = get_option( "my_price_list" ); $sale_price_list = get_option( "my_sale_price_list" ); $priceField = 'price'; foreach ( $products_prices as $prices ) { if ( !isset( $prices['prices'] ) ) { continue; } $product = my_get_product_by_SKU( $prices['sku'] ); if ( $product ) { foreach ( $prices['prices'] as $pricePoint ) { if ( $pricePoint['priceList'] === $price_list ) { $product->set_regular_price( $pricePoint['price'] ); } if ( $pricePoint['priceList'] === $sale_price_list ) { $product->set_sale_price( $pricePoint['price'] ); } } $product->save(); } } } function my_get_product_by_SKU( $sku, $variable = false ) { try { $id = my_is_already_product( $sku ); if ( $id ) { if ( $variable ) { $product = new WC_Product_Variable( $id ); } else { $product = new WC_Product( $id ); } } else { $id = my_is_already_product( $sku, true ); if ( $id ) { $product = new WC_Product_Variation( $id ); } else { return false; } } return $product; } catch ( Exception $e ) { error_log( "Get Product by SKU Error: " . $e->getMessage() ); return false; } }
备注:内容来源于stack exchange,提问作者Abe
相关产品推荐
相关产品推荐

