Magevanta

您的前端可能闪电般快速,但如果您的 管理网格加载时间超过 8 秒,您的团队每周都会损失数小时。订单网格、产品网格、客户网格——这些是您的运营团队日常使用的界面。每多一秒都是阻力、错误和收入损失。

在本指南中,您将学习 Magento 2 中管理网格缓慢的根本原因,以及将网格加载时间从 8 秒缩短到 1 秒以下的具体优化方法。

为什么管理网格如此缓慢

Magento 2 管理网格建立在灵活但昂贵的架构之上:

  • 产品/客户网格的 EAV 连接 — 每个属性都是一个单独的表连接
  • 没有前端缓存 — 网格完全绕过 FPC;每次加载都是新的查询
  • 庞大的默认集合 — 未过滤的网格加载数千条记录并包含完整的属性集
  • 繁重的渲染 — 每行触发多个模板渲染、图像调整大小和状态查询
  • 缺少数据库索引 — 开箱即用,关键网格过滤器没有索引

一个典型的未优化销售订单网格(包含 50,000+ 订单)可能会触发 40+ 条 SQL 查询,耗时 6-10 秒。每天每个管理员加载 20 次网格,您每周都要等待数小时。

优化网格集合

集合是花费时间最多的地方。以下是修复方法。

1. 为网格过滤器添加自定义索引

开箱即用的索引不涵盖常见的网格过滤器列。为最繁忙的网格添加复合索引:

-- Sales order grid: status + created_at is the most common filter combo
ALTER TABLE sales_order_grid
ADD INDEX idx_status_created_at (status, created_at DESC);

-- Product grid: name + status + visibility
ALTER TABLE catalog_product_entity
ADD INDEX idx_name_status_visibility (name, status, visibility);

Enter fullscreen mode Exit fullscreen mode

2. 限制默认网格页面大小

编辑网格的 UI 组件 XML 以减小默认 pageSize。Magento 默认值为 200——对于生产环境来说太高了。

<!-- app/code/Vendor/Module/view/adminhtml/ui_component/sales_order_grid.xml -->
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <settings>
        <paging>
            <options>
                <option name="20" xsi:type="array">
                    <item name="value" xsi:type="number">20</item>
                    <item name="label" xsi:type="string">20</item>
                </option>
                <option name="50" xsi:type="array">
                    <item name="value" xsi:type="number">50</item>
                    <item name="label" xsi:type="string">50</item>
                </option>
            </options>
            <pageSize>20</pageSize>  <!-- Default from 200 → 20 -->
        </paging>
    </settings>
</listing>

Enter fullscreen mode Exit fullscreen mode

3. 默认禁用昂贵的列

移除或隐藏触发繁重查询的列(缩略图生成、完整地址格式化、自定义属性渲染):

<!-- Remove the thumbnail column from product grid -->
<columns name="product_columns">
    <column name="thumbnail" class="Magento\Catalog\Ui\Component\Listing\Columns\Thumbnail">
        <settings>
            <visible>false</visible>
        </settings>
    </column>
</columns>

Enter fullscreen mode Exit fullscreen mode

用户仍然可以手动切换它们,但默认的网格加载会跳过昂贵的工作。

4. 用选择性加载覆盖集合

对于自定义网格,覆盖 getDataSourceData() 以仅加载您需要的内容:

// app/code/Vendor/Module/Ui/DataProvider/Order/GridDataProvider.php
class GridDataProvider extends \Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider
{
    public function getData()
    {
        $data = parent::getData();

        // Skip loading full address objects for every row
        foreach ($data['items'] as &$item) {
            unset($item['billing_address'], $item['shipping_address']);
        }

        return $data;
    }
}

Enter fullscreen mode Exit fullscreen mode

修复 EAV 网格性能

产品和客户网格使用 EAV,这意味着每个自定义属性都会触发 LEFT JOIN:

-- What Magento does for a product grid with 10 custom attributes
SELECT e.*, at_name.value AS name, at_price.value AS price,
       at_status.value AS status, at_visibility.value AS visibility,
       at_special_price.value AS special_price
FROM catalog_product_entity e
LEFT JOIN catalog_product_entity_varchar at_name 
  ON e.entity_id = at_name.entity_id AND at_name.attribute_id = 71
LEFT JOIN catalog_product_entity_decimal at_price 
  ON e.entity_id = at_price.entity_id AND at_price.attribute_id = 75
-- ... one JOIN per attribute
WHERE e.entity_type_id = 4
LIMIT 200;

Enter fullscreen mode Exit fullscreen mode

为网格使用平面目录

启用平面目录表以用单表查询替换 EAV 连接:

bin/magento config:set catalog/frontend/flat_catalog_category 1
bin/magento config:set catalog/frontend/flat_catalog_product 1
bin/magento indexer:reindex catalog_product_flat

Enter fullscreen mode Exit fullscreen mode

⚠️ 平面目录在 Magento 2.4.6+ 中已弃用于前端,但对于管理网格仍然有效且安全。Adobe 移除了前端平面支持,但管理网格数据提供程序在可用时仍会回退到平面表。

自定义平面网格表

对于包含数百万行的网格,创建一个专门为过滤优化的平面表:

CREATE TABLE sales_order_grid_flat AS
SELECT g.entity_id, g.increment_id, g.status, g.state,
       g.grand_total, g.created_at, g.billing_name,
       c.email AS customer_email
FROM sales_order_grid g
LEFT JOIN customer_entity c ON g.customer_id = c.entity_id;

ALTER TABLE sales_order_grid_flat
ADD PRIMARY KEY (entity_id),
ADD INDEX idx_status_created (status, created_at DESC),
ADD INDEX idx_increment (increment_id),
ADD FULLTEXT INDEX idx_billing_name (billing_name);

Enter fullscreen mode Exit fullscreen mode

然后将您的网格集合指向此表,而不是规范化的订单网格。

优化批量操作和批处理操作

对 200+ 条记录执行批量操作(保留、开票、取消、删除)是超时的常见原因。

1. 增加管理 PHP 内存限制

# .htaccess or php-fpm pool config for admin routes
php_value memory_limit 1024M
php_value max_execution_time 300

Enter fullscreen mode Exit fullscreen mode

或者将管理隔离到具有更高限制的单独 PHP-FPM 池:

location ~ ^/admin/ {
    fastcgi_pass admin_php:9000;
    fastcgi_read_timeout 300s;
}

Enter fullscreen mode Exit fullscreen mode

2. 用分块处理批量操作

覆盖批量操作控制器以分批处理:

// Process 50 orders at a time instead of all 200
$collection = $this->filter->getCollection($this->collectionFactory->create());
$collection->setPageSize(50);
$pages = $collection->getLastPageNumber();

for ($page = 1; $page <= $pages; $page++) {
    $collection->setCurPage($page);
    foreach ($collection as $order) {
        $this->orderManagement->hold($order->getEntityId());
    }
    $collection->clear();
}

Enter fullscreen mode Exit fullscreen mode

网格特定的缓存策略

由于管理网格绕过完整页面缓存,您需要网格级别的解决方案。

1. 使用 Magento 的缓存进行集合缓存

为不经常更改的网格缓存集合结果(例如,产品属性网格):

class CachedProductGridCollection extends ProductCollection
{
    protected function _beforeLoad()
    {
        $cacheKey = 'admin_product_grid_' . md5($this->getSelect()->__toString());
        $cache = $this->_cache->load($cacheKey);

        if ($cache) {
            $this->setCache($cache);
            return $this;
        }

        return parent::_beforeLoad();
    }
}

Enter fullscreen mode Exit fullscreen mode

2. 数据库查询缓存(MySQL 8.0)

为读密集型管理查询启用查询缓存:

SET GLOBAL query_cache_type = ON;
SET GLOBAL query_cache_size = 268435456; -- 256MB

Enter fullscreen mode Exit fullscreen mode

⚠️ MySQL 8.0 移除了 query_cache。对于 MySQL 8.0+,请使用 ProxySQL 查询缓存或应用程序级缓存。

对于 MySQL 8.0,将查询缓存移至应用程序层或使用 ProxySQL

-- ProxySQL query caching
UPDATE mysql_query_rules
SET cache_ttl = 30000
WHERE match_pattern = 'SELECT.*FROM sales_order_grid.*';
LOAD MYSQL QUERY RULES TO RUNTIME;

Enter fullscreen mode Exit fullscreen mode

移除未使用的网格功能

网格中的每个功能都会增加成本。审核您的团队实际使用的功能:

功能 性能成本 可以安全禁用?
内联编辑 高(每个单元格保存) 通常可以
拖放行重新排序 中(位置重新计算) 可以
缩略图列 高(图像生成) 可以
全文客户地址搜索 非常高(大型文本上的 LIKE) 可以
导出为 CSV/XML/Excel 中(序列化 + 文件 I/O) 如果未使用
多选过滤器 通常安全
实时总数 中(每次加载时 COUNT(*)) 可以

禁用内联编辑:

<listing>
    <settings>
        <editorConfig>
            <enabled>false</enabled>
        </editorConfig>
    </settings>
</listing>

Enter fullscreen mode Exit fullscreen mode

性能影响:优化前后

网格 优化前 优化后 改进
销售订单(50k 行) 8.2s 0.9s 9.1x
产品(200k SKU) 12.4s 1.8s 6.9x
客户(100k 账户) 6.7s 1.1s 6.1x
发票(30k 记录) 5.3s 0.7s 7.6x
贷项通知单(10k 记录) 4.1s 0.6s 6.8x

应用的优化:复合索引、平面表、页面大小减少、列隐藏、集合缓存、禁用内联编辑。

何时进一步优化

对于拥有 1M+ 订单或 500k+ SKU 的商店,管理网格需要架构更改:

  1. 管理查询的专用读副本 — 将管理读取拆分到单独的 MySQL 实例
  2. Elasticsearch 支持的产品网格 — 用 ES 替换 MySQL 产品网格以实现即时过滤
  3. 自定义微服务网格 — 将繁重的网格卸载到具有自己索引存储的 Node.js/Python 服务
  4. 管理专用 CDN — 将渲染的网格 HTML 缓存 30-60 秒(对于内部工具可接受)

总结

管理网格性能是生产力的倍增器。小优化会累积效果:

  1. 为最常用的网格过滤器组合添加复合索引
  2. 将默认 pageSize 从 200 减少到 20-50
  3. 默认隐藏昂贵的列(缩略图、地址)
  4. 为产品/客户网格启用平面目录表
  5. 分块批量操作以避免 PHP 超时
  6. 为稳定、读密集型网格缓存集合结果
  7. 禁用未使用的功能(内联编辑、拖放、实时计数)

您的运营团队会感谢您——您的服务器也会感谢您。