您的前端可能闪电般快速,但如果您的 管理网格加载时间超过 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 的商店,管理网格需要架构更改:
- 管理查询的专用读副本 — 将管理读取拆分到单独的 MySQL 实例
- Elasticsearch 支持的产品网格 — 用 ES 替换 MySQL 产品网格以实现即时过滤
- 自定义微服务网格 — 将繁重的网格卸载到具有自己索引存储的 Node.js/Python 服务
- 管理专用 CDN — 将渲染的网格 HTML 缓存 30-60 秒(对于内部工具可接受)
总结
管理网格性能是生产力的倍增器。小优化会累积效果:
- 为最常用的网格过滤器组合添加复合索引
- 将默认
pageSize从 200 减少到 20-50 - 默认隐藏昂贵的列(缩略图、地址)
- 为产品/客户网格启用平面目录表
- 分块批量操作以避免 PHP 超时
- 为稳定、读密集型网格缓存集合结果
- 禁用未使用的功能(内联编辑、拖放、实时计数)
您的运营团队会感谢您——您的服务器也会感谢您。
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.