성능 : 모든 제품 유형의 list.phtml 제품 목록에 재고 재고 레벨 추가


12

TL; DR , 요구 사항은 Magento의 프레임 워크를 준수하는 성능을 염두에두고 추가 쿼리 / 메모리를 거의 사용하지 않고 카테고리 제품 목록 페이지에 재고 레벨의 재고를 표시해야합니다.


확장 성을위한 사전로드에 관한 Vinai Kopp의 기사를 읽은 후 .

성능 향상을 위해 추가 쿼리 /로드가 적은 카테고리 제품 목록 페이지 ( list.phtml ) 에 재고 수준을 포함시키는 가장 좋은 방법은 무엇입니까 ?

몇 가지 접근 방식을 알고 있습니다.

afterLoad ()media_gallery추가 쿼리없이 포함과 함께 잘 작동하는 것처럼 보이지만인벤토리와 동일한 접근 방식을 구현하지 못했습니다.

$attributes = $_product->getTypeInstance(true)->getSetAttributes($_product);
$media_gallery = $attributes['media_gallery'];
$backend = $media_gallery->getBackend();
$backend->afterLoad($_product);

product_id키를 사용 하여 필요한 데이터를 수집과 병렬로 수집하도록 SQL 에 지시하십시오 . 그러나 프레임 워크를 통해 더 많은 수단을 찾고 있습니다.

현재 나는 단순히 다음을 stock_item통해 객체를 로드하고 있습니다 :

$_product->load('stock_item')->getTotalQty(); 어느 것이 효과적이지만 컬렉션에있는 모든 제품의 재고 총계를 얻기 위해 더 많은 쿼리가 추가 된 것을 알았습니다.

...

__EAV_LOAD_MODEL__ (Mage_Catalog_Model_Product, id: stock_item, attributes: NULL)
__EAV_LOAD_MODEL__ (Mage_Catalog_Model_Product, id: stock_item, attributes: NULL)
__EAV_LOAD_MODEL__ (Mage_Catalog_Model_Product, id: stock_item, attributes: NULL)

...

이상하게도 이것은 작동합니다. 마술은 Mage_Eav_Model_Entity_Abstract-> load ($ object, $ entityId, $ attributes)에서 발생합니다. $ attributes가 비어 있으면 loadAllAttribute ($ object)를 호출합니다. 따라서 $ product-> load ( 'blah')는 'media_gallery'를 포함한 모든 누락 된 속성을로드합니다 – William Tran Nov 19 '14 at 4:45

이미로드 된 컬렉션에 필요한 값을 추가하십시오.

필요한 데이터를 레이어 / 필터의 최상위 프로덕션 컬렉션에 추가하는 명백한 간단한 방법이 최선의 방법 인 것 같습니다.

나는 통지를 관찰자의 한 addInventoryDataToCollection () 에서 Mage_CatalogInventory_Model_Observer 그것과 같은 소리는 달성하지만, 호환 될 것 같지 않습니다 사용자 정의 모듈 관찰자에게 방법을 추가하는 것입니다.

<events>
    <catalog_product_collection_load_after>
        <observers>
            <inventory>
                <class>cataloginventory/observer</class>
                <method>addInventoryDataToCollection</method>
            </inventory>
        </observers>
    </catalog_product_collection_load_after>
</events>

결과 :

경고 : 71 행의 /app/code/core/Mage/CatalogInventory/Model/Resource/Stock/Item/Collection.php에서 foreach ()에 잘못된 인수가 제공되었습니다.


1
좋은 질문 부머
Amit Bera

답변:


4

여기서 실제 문제는 사전로드가 아니라 정확성입니다. 제품 컬렉션의 재고 수량을 얻는 것이 비교적 쉽습니다.

$products = Mage::getModel('catalog/product')->getCollection()
    ->addCategoryFilter($_category);
$stockCollection = Mage::getModel('cataloginventory/stock_item')
    ->getCollection()
    ->addProductsFilter($products);

이제 두 개의 쿼리로 필요한 모든 정보를 얻을 수 있습니다. 그것들은 서로 연관시키기가 어렵습니다. 연관 배열을 사용 'product_id' => 'stock'하고 getter를 작성 하여 해결할 수 있습니다 . 또한 addProductsFilter를 최적화 할 수 있습니다.

public function addProductIdsFilter(array $productIds)
{
    if(empty($productIds) {
        $this->_setIsLoaded(true);
    }
    $this->addFieldToFilter('main_table.product_id', array('in' => $productIds));
    return $this;
}

그러면 유형 확인 및 어레이 복제가 절약됩니다.

문제는 이제 HTML 캐시 차단입니다. 이 범주 페이지는 제품에 포함 된 제품에서 재고가 갱신 될 때 제거해야합니다. 내가 아는 한, 재고 상태 변경 만으로 인해 제품이 포함 된 카테고리 페이지 (또는 더 정확하게는 가시성 변경)가 제거 되므로 표준이 아닙니다 . 따라서 최소한 cataloginventory_stock_item_before_save몇 가지 를 관찰하고 해당 카테고리 페이지에 대한 html 캐시 (및 FPC 캐시)를 제거해야합니다.


최종 요점은 추가 요청이 주식 데이터 검색을 요청한 이유입니다. 빠르게 움직이는 제품이 포함 된 범주가있는 경우 캐시하는 데 대부분의 시간이 플러시 / 무효화됩니다. 카테고리 페이지 캐시를 제거해야하는 유일한 경우는 해당 카테고리에서 제품이 제거 된 경우입니다. 사례별로 가장 효율적인 구현을 수행해야하는 마술 솔루션은 없습니다. 원하는 경우 주식 데이터를 캐시 할 수 있으므로 전체 페이지가 아닌 플러시 후에 다시 작성해야합니다.
john-jh

JavaScript 데이터를 사용하여 DOM을 업데이트하는 것과 동일한 방식으로 주식 데이터를 구현하는 경우 자체 캐시 키가있는 블록을 사용하여 주식 데이터를 작성하고 주식 데이터 만 무효화 할 수 있습니다. 이것이 내가 당신의 상황에 맞게하는 방법이며 ESI 기반 FPC를 사용하지 않고 요청 프로세서의 펀치 블록을 구멍을 낼 수있는 방법입니다. 라우터 비트뿐만 아니라 페이지 당 하나의 PHP 인터프리터 만 필요합니다. 어떤 이유로 든 기계에 압력이 가해지면 PHP 인터프리터가 가장 CPU 집약적 리소스입니다. 좋은 주말 프로젝트처럼 들리기 시작했다 ;-)
Melvyn

3
구현과 잠재적 문제 및 병목 현상에 대해 실제로 생각해야 할 부분에서 작업을 실제로 흥미롭게 만드는 유형의 항목입니다. 나는 단일 PHP 프로세스로 어디에서 왔는지 확실하게 알지 못하지만 현재로서는 문제가되지 않지만 확장에 따른 영향을 볼 수 있습니다. 페이지에 표시되는 재고 값은 중요한 기능이 아니므로로드 후 사용자에게 영향을주지 않으면 서 보조 리소스로로드합니다. 기본 기능이 아닌 제품 목록의 수치입니다.
john-jh

1
네, 지금 Redis에서 직접 가져 와서 PHP를 잘라내는 아이디어를 가지고 놀고 있습니다. Yichun Zhang의 오픈 Resty 에 대한 흥미로운 작업이 있습니다 .
Melvyn 2019

2

나는 이미 당신이 이미 받아들이고 의심 할 여지가없는 것을 addInventoryDataToCollection()보았지만, 당신이 얼마나 가까운 지 지적하고 싶지만 구성 파일을 잘못 인용 한 것처럼 보이거나 우리는 매우 다른 버전의 magento를 사용하고 있습니다. 내 사본 CatalogInventory/etc/config.xml에는 다른 방법이 있습니다.catalog_product_collection_load_after

 <catalog_product_collection_load_after>
    <observers>
        <inventory>
            <class>cataloginventory/observer</class>
            <method>addStockStatusToCollection</method>
        </inventory>
    </observers>
 </catalog_product_collection_load_after>

addInventoryDataToCollection() 에서 호출 <sales_quote_item_collection_products_after_load>

소스 addStockStatusToCollection()는 다음과 같습니다.

public function addStockStatusToCollection($observer)
{
    $productCollection = $observer->getEvent()->getCollection();
    if ($productCollection->hasFlag('require_stock_items')) {
        Mage::getModel('cataloginventory/stock')->addItemsToProducts($productCollection);
    } else {
        Mage::getModel('cataloginventory/stock_status')->addStockStatusToProducts($productCollection);
    }
    return $this;
}

require_stock_items컬렉션이로드되기 전에 컬렉션에 플래그 를 설정했을 수도 있습니다 ( 아마도 범주 목록 뒤에있는 블록으로는 쉽지 않을 수도 있음). 또는 Mage::getModel('cataloginventory/stock')->addItemsToProducts($productCollection)컬렉션이 이미로드 된 후에 컬렉션에서 수동으로 호출 할 수 있습니다. addItemsToProducts()모든 StockItem을 가져와 ProductCollection에 첨부합니다.

public function addItemsToProducts($productCollection)
{
    $items = $this->getItemCollection()
        ->addProductsFilter($productCollection)
        ->joinStockStatus($productCollection->getStoreId())
        ->load();
    $stockItems = array();
    foreach ($items as $item) {
        $stockItems[$item->getProductId()] = $item;
    }
    foreach ($productCollection as $product) {
        if (isset($stockItems[$product->getId()])) {
            $stockItems[$product->getId()]->assignProduct($product);
        }
    }
    return $this;
}

1

바니시 또는 FPC를 전혀 사용하고 있습니까? 아니면 향후 계획을 세우고 있습니까?

우리는 제품 목록에 필요한 홀 펀치 / ESI 요청의 양에 따라 캐싱을 배치 할 가치가 거의 없었으므로 다른 접근 방식을 선택했습니다.

우리는 하나의 웹 사이트에서 AJAX 요청을 사용자 정의 컨트롤러에 사용하여 제품의 재고 데이터를 검색하고 자바 스크립트가 DOM 업데이트를 처리하는 솔루션을 구현했습니다. 재고 데이터에 대한 추가 요청은 ~ 100ms가 걸리며 전체 (표시 가능한) 페이지로드 시간에 전혀 영향을 미치지 않습니다. 프라임 FPC가 페이지 요청을 100ms 미만으로 떨어 뜨리면 제품 목록에 재고 데이터를 표시하기위한 성능 오버 헤드가 낮은 빠른 사이트가 하나 있습니다.

템플릿을 현명하게 수행하기 위해서는 각 제품 래퍼 HTML에 productId를 추가하기 만하면 자바 스크립트가 각 제품에 적용 할 재고 데이터를 알 수 있습니다.

실제 주식 데이터에 대한 추가 캐싱 기술을 살펴보면 각 요청에서 데이터베이스를 초기화하거나 적중하지 않아도 100ms 미만으로 떨어질 수 있습니다.

이것이 당신이 찾고있는 라인을 따르지 않는다면 죄송하지만 우리는 그것이 요구 사항에 대한 확장 성과 성능을위한 최선의 접근 방식이라는 것을 알았습니다.


2
혼돈 원숭이를 배치 하고 캐시 스토리지가 넘어지면 어떻게되는지 확인하십시오. 이 질문은 특히 캐시에 의존하지 않는다고 언급합니다. FPC가 BuiltIn / MomsBasement 템플릿을 너무 많이 수정하지 않을 것이라고 클라이언트를 설득시키는 것은 이와 같은 답변입니다.
Melvyn

성능과 솔루션으로 FPC / Varnish를 제안한다고 생각하면 실제로 내 대답을 오해합니다. FPC / Vanish를 사용 중이거나 고려중인 경우 홀 펀치 / ESI 요청을 줄이기 위해이 접근 방식을 조사해야한다고 언급했습니다. 우리는 leaveraging 캐시없이 필요한 재고 데이터를 100ms 미만으로 얻습니다.
john-jh

그리고 ESI를 사용하면 같은 시간에 같은 데이터를 얻을 수 있습니다. ESI와의 접근 방식은 근본적으로 다릅니다. 따라서 캐시가 없으면 짧은 시간 내에 동일한 데이터를 계속 얻을 수 있습니다. JSON을 다시 보내기 위해 또 다른 라우터를 거치지 않고 DOM 등을 업데이트하는 주식 배열을 JavaScript로 작성합니다. 원하는 경우 JSON을 넣어 라우터를 잘라냅니다.
Melvyn

1
캐싱이 사용되지만 성능 최적화에 대한 문제가 더 있습니다. 일부 배경 : magento.stackexchange.com/questions/13957/… (캐시가 거의 없음) 답장을 보내 주셔서 감사합니다. 입력에 감사드립니다!
B00MER

M2에서이를 수행하는 "모범 사례"방법이 있는지 확실하지 않지만 솔루션은 Magento 1.x 이후로 항상 수행 한 방법입니다.
thdoan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.