You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Magento 2.3.4产品自定义字段保存时数据库死锁问题求助

Magento 2.3.4 产品自定义字段保存时数据库死锁问题解决

问题场景

为Magento 2.3.4产品添加计算器功能的自定义字段,已通过/app/code/Vendor/ProductCalculator/view/adminhtml/ui_component/product_form.xml在产品编辑表单中添加status和value字段,通过adminhtml/events.xml绑定catalog_product_save_after事件,创建HandleSaveProduct Observer保存数据到数据库,但保存产品时页面卡顿,出现数据库死锁,需重启MySQL服务器。

相关代码

1. product_form.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
    <fieldset name="magenest">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="label" xsi:type="string">Shrink Film Width Calculator</item>
                <item name="collapsible" xsi:type="boolean">true</item>
                <item name="dataScope" xsi:type="string">data.magenest</item>
                <item name="sortOrder" xsi:type="number">10</item>
            </item>
        </argument>
        <field name="status">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="label" xsi:type="string" translate="true">Status</item>
                    <item name="dataScope" xsi:type="string">status</item>
                    <item name="sortOrder" xsi:type="number">10</item>
                    <item name="componentType" xsi:type="string">field</item>
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="formElement" xsi:type="string">select</item>
                    <item name="options" xsi:type="array">
                        <item name="0" xsi:type="array">
                            <item name="value" xsi:type="number">0</item>
                            <item name="label" xsi:type="string">Inactive</item>
                        </item>
                        <item name="1" xsi:type="array">
                            <item name="value" xsi:type="number">1</item>
                            <item name="label" xsi:type="string">Active</item>
                        </item>
                    </item>
                </item>
            </argument>
        </field>
        <field name="value">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="label" xsi:type="string" translate="true">Value</item>
                    <item name="dataScope" xsi:type="string">value</item>
                    <item name="sortOrder" xsi:type="number">20</item>
                    <item name="componentType" xsi:type="string">field</item>
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="formElement" xsi:type="string">input</item>
                </item>
            </argument>
        </field>
    </fieldset>
</form>

2. events.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="catalog_product_save_after">
        <observer name="Vendor_ProductCalculator::save_custom_data"
                  instance="Vendor\ProductCalculator\Observer\HandleSaveProduct" />
    </event>
</config>

3. HandleSaveProduct.php

<?php

namespace Vendor\ProductCalculator\Observer;

use Magento\Framework\Event\ObserverInterface;

class HandleSaveProduct implements ObserverInterface
{

    /**
     * Custom factory
     *
     * @var \Vendor\ModuleName\Model\CustomProductsFactory
     */
    protected $_CustomProductsFactory;

    /**
     * Http Request
     *
     * @var \Magento\Framework\App\Request\Http
     */
    protected $request;
    
    
    /**
     * @param \Magento\Customer\Model\ResourceModel\Group\Collection $customerGroup
     * @param \Magento\Framework\App\Request\Http $request
     * @param array $data
     */
    public function __construct(
        \Magento\Catalog\Model\ProductFactory $customProductsFactory,
        \Magento\Framework\App\Request\Http $request,
        array $data = []
    ) {
        $this->_CustomProductsFactory = $customProductsFactory;
        $this->request = $request;
    }
    
    
    /**
     *
     *  @param \Magento\Framework\Event\Observer $observer
     */
    public function execute(\Magento\Framework\Event\Observer $observer)
    {
        $product = $observer->getProduct();
        
        if ((!empty($product))) {
            $productId = $product->getId();
            $requestId = $this->request->getParam('id');
            $productName =  $product->getName();
            $customFieldSetData = serialize($product->getMagenest());
            if ($productId) {
                $this->saveCustomProductData($requestId,$productId,$customFieldSetData);
            }
        }
    }
    

    public function saveCustomProductData($requestId,$productId,$customFieldSetData)
    {
        
        $model = $this->_CustomProductsFactory->create();
        
        if (empty($requestId)) {
            $data = ['magenest'=>$customFieldSetData,'product_id'=>$productId];
        } else {
            //load model
            $model->load($productId, 'product_id');
            $data = ['magenest'=>$customFieldSetData,'product_id'=>$productId];
        }
        
        $model->addData($data);
        
        $model->save();
    }
} 

问题分析与修复方案

核心问题原因

  1. 事件选择错误:catalog_product_save_after触发时,产品保存的事务尚未提交,此时操作关联数据会和产品表的事务锁产生冲突,引发死锁。
  2. 依赖注入错误:构造函数中注入的是Magento\Catalog\Model\ProductFactory(产品模型工厂),但业务逻辑需要的是自定义模型的工厂,导致错误加载产品模型而非自定义数据表模型,加剧锁冲突。
  3. 数据操作方式低效:先load再save的方式会增加数据库锁持有时间,容易引发锁竞争。
  4. 序列化方式不安全:serialize函数存在安全风险,Magento推荐使用JSON格式存储数组数据。

具体修复步骤

1. 替换事件为catalog_product_save_commit_after

修改adminhtml/events.xml,将事件改为事务提交后触发的catalog_product_save_commit_after,避免事务锁冲突:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="catalog_product_save_commit_after">
        <observer name="Vendor_ProductCalculator::save_custom_data"
                  instance="Vendor\ProductCalculator\Observer\HandleSaveProduct" />
    </event>
</config>

2. 修复Observer的依赖注入

将构造函数中的产品工厂替换为自定义模型工厂(假设自定义模型为Vendor\ProductCalculator\Model\CustomProducts),修正注释和注入逻辑:

<?php

namespace Vendor\ProductCalculator\Observer;

use Magento\Framework\Event\ObserverInterface;

class HandleSaveProduct implements ObserverInterface
{
    /**
     * Custom Products Factory
     *
     * @var \Vendor\ProductCalculator\Model\CustomProductsFactory
     */
    protected $_customProductsFactory;

    /**
     * Http Request
     *
     * @var \Magento\Framework\App\Request\Http
     */
    protected $request;
    
    /**
     * @param \Vendor\ProductCalculator\Model\CustomProductsFactory $customProductsFactory
     * @param \Magento\Framework\App\Request\Http $request
     * @param array $data
     */
    public function __construct(
        \Vendor\ProductCalculator\Model\CustomProductsFactory $customProductsFactory,
        \Magento\Framework\App\Request\Http $request,
        array $data = []
    ) {
        $this->_customProductsFactory = $customProductsFactory;
        $this->request = $request;
    }

3. 优化数据保存逻辑,替换序列化方式

修改execute和saveCustomProductData方法,用json_encode替代serialize,简化逻辑减少锁持有时间:

public function execute(\Magento\Framework\Event\Observer $observer)
    {
        $product = $observer->getProduct();
        
        if ($product && $product->getId()) {
            $productId = $product->getId();
            $magenestData = $product->getMagenest();
            $customFieldSetData = is_array($magenestData) ? json_encode($magenestData) : '';
            $this->saveCustomProductData($productId, $customFieldSetData);
        }
    }
    

    public function saveCustomProductData($productId, $customFieldSetData)
    {
        $model = $this->_customProductsFactory->create();
        $model->load($productId, 'product_id');
        
        $data = [
            'magenest' => $customFieldSetData,
            'product_id' => $productId
        ];
        
        $model->addData($data);
        $model->save();
    }

4. 优化自定义数据表索引

为自定义数据表的product_id字段添加唯一索引,提升查询更新效率,避免重复数据:

ALTER TABLE `vendor_productcalculator_customproducts` ADD UNIQUE INDEX `IDX_PRODUCT_ID` (`product_id`);

验证修复效果

  1. 清理Magento缓存:bin/magento cache:clean
  2. 重新保存产品,检查页面卡顿情况,查看数据库日志确认死锁问题是否消失。

内容的提问来源于stack exchange,提问作者Akhilesh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 14:50:28