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(); } }
问题分析与修复方案
核心问题原因
- 事件选择错误:
catalog_product_save_after触发时,产品保存的事务尚未提交,此时操作关联数据会和产品表的事务锁产生冲突,引发死锁。 - 依赖注入错误:构造函数中注入的是
Magento\Catalog\Model\ProductFactory(产品模型工厂),但业务逻辑需要的是自定义模型的工厂,导致错误加载产品模型而非自定义数据表模型,加剧锁冲突。 - 数据操作方式低效:先
load再save的方式会增加数据库锁持有时间,容易引发锁竞争。 - 序列化方式不安全:
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`);
验证修复效果
- 清理Magento缓存:
bin/magento cache:clean - 重新保存产品,检查页面卡顿情况,查看数据库日志确认死锁问题是否消失。
内容的提问来源于stack exchange,提问作者Akhilesh
相关产品推荐
相关产品推荐

