가격 재 색인으로 인해 체크 아웃 중에 DB 교착 상태가 발생 함


47

제품 가격 재색 인화 프로세스로 인해 체크 아웃 프로세스에서 교착 상태 예외가 발생한다고 생각되는 문제가 발생했습니다.

결제 과정 에서이 예외가 발생했습니다.

주문 변환 예외 : SQLSTATE [40001] : 직렬화 실패 : 1213 잠금을 시도 할 때 교착 상태가 발견되었습니다. 거래를 다시 시작하십시오

불행히도 예외가 포착 된 위치 때문에 전체 스택 추적이 없지만 INNODB 상태를 확인하면 교착 상태를 추적 할 수있었습니다.

SELECT `si`.*, `p`.`type_id` FROM `cataloginventory_stock_item` AS `si` 
INNER JOIN `catalog_product_entity` AS `p` ON p.entity_id=si.product_id     
WHERE (stock_id=1) 
AND (product_id IN(47447, 56678)) FOR UPDATE

*** (1) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 0 page no 329624 n bits 352 index 
`PRIMARY` of table `xxxx`.`catalog_product_entity` 

SQL 요청 테이블 잠금은 Mage_CatalogInventory_Model_Stock::registerProductsSale()감소시키기 위해 현재 인벤토리 수를 가져 오려고 할 때 생성됩니다 .

교착 상태가 발생했을 때 제품 가격 재색 인 프로세스가 실행 중이며 catalog_product_entity table교착 상태를 유발 한 읽기 잠금이 있다고 가정합니다 . 교착 상태를 올바르게 이해하면 읽기 잠금으로 인해 교착 상태가 발생하지만 사이트에 ~ 50,000 개의 제품이 있으므로 제품 가격 재 색인이 일정 시간 동안 잠금을 유지합니다.

불행히도, 결제 코드 흐름의이 시점까지 고객의 신용 카드에 요금이 청구되었으며 (맞춤 결제 모듈을 통해) 해당 주문 객체를 생성하지 못했습니다.

내 질문은 :

  • 맞춤 결제 모듈 로직에 결함이 있습니까? 즉, 결제 수단 (신용 카드)으로 청구하기 전에 Magento가 견적을 주문 예외로 무료로 변환 할 수 있도록 허용 된 흐름이 있습니까?

편집 : 지불 모듈 논리는 실제로이 교착 상태가 발생한 장소 이후에 (이반의 대답에 따라) $ paymentmethod-> authorize ()에 대한 호출이 발생하기 때문에 실제로 잘못되었습니다. 그러나 거래는 여전히 교착 상태에 의해 차단됩니다 (신용 카드에 오류가없는 경우에도).

  • 이 함수 호출 $stockInfo = $this->_getResource()->getProductsStock($this, array_keys($qtys), true);Mage_CatalogInventory_Model_Stock::registerProductsSale()잠금을 읽습니다. 잠금이 아닌 읽기로 만드는 것이 얼마나 위험합니까?

  • 웹에서 답변을 검색 할 때 사이트가 뜨거울 때 두 곳에서 전체 재 인덱싱을 실행하지 말 것을 제안했습니다. 좋은 해결책처럼 보이지는 않습니다. 인덱싱 문제로 인해 테이블 ​​교착 상태 및 잠금 경합이 Magento에서 알려진 문제입니까? 해결 방법이 있습니까?

편집 : 여기에 남아있는 질문은 세 번째 질문의 것 같습니다. 다시 색인화하면 테이블 교착 상태가 발생합니다. 이에 대한 해결 방법을 찾고 있습니다.

편집 : 교착 상태는 그 자체로 문제가 아니라 그에 대한 응답이 초점이되어야한다는 개념은 많은 의미가 있습니다. 교착 상태 예외를 포착하고 요청을 다시 발행하기 위해 코드에서 포인트를 찾기 위해 추가로 조사합니다. Zend Framework DB 어댑터 수준에서이 작업을 수행하는 것이 한 가지 방법이지만 유지 관리를 쉽게하기 위해 Magento 코드에서이 작업을 수행하는 방법도 찾고 있습니다.

이 스레드에는 흥미로운 교착 상태가 있습니다 . http://www.magentocommerce.com/boards/viewthread/31666/P0/ 관련 교착 상태를 해결하는 것으로 보입니다 (단, 구체적으로는 아님).

편집 : 분명히 교착 상태는 CE 1.8 Alpha에서 어느 정도 해결되었습니다. 이 버전이 알파 버전이 될 때까지 해결 방법을 찾고 있습니다.


최근 비슷한 문제가 발생했습니다. 어떤 결제 확장 프로그램을 사용하고 있습니까?
Peter O'Callaghan

사용자 지정 코드 확장
Roscius

1
@kalenjordan 1.13의 인덱싱 개선과 philwinkle과 같은 재시도 체계는 저에게 문제를 크게 완화 시켰습니다.
Roscius

1
@Roscius는 대략 얼마나 많이 완화 했습니까? DB 연결 장애 (연결 시간 초과, 잠금 대기 시간 초과, 교착 상태)가 주문의 약 0.2 %에 영향을 미치는 것으로 나타났습니다. 매우 드물지만 실제로 완전히 해결하고 싶습니다.
kalenjordan

답변:


16

귀하의 지불 방법이 지불을 잘못 처리하고있을 가능성이 큽니다.

마 젠토 주문 저장 프로세스는 매우 간단합니다.

  • 가격 및 제품 정보를 포함하여 견적 품목에서 주문 품목으로 이전해야 할 모든 데이터를 준비한 후 가격 검색을 호출하지 않습니다.
  • 주문 제출 이벤트 전에 호출 checkout_type_onepage_save_order하고sales_model_service_quote_submit_before
    • Mage_CatalogInventory_Model_Stock::registerProductsSale() 이 이벤트 관찰자에서 호출됩니다
  • DB 거래 시작
  • 호출 $order->place()호출하여 지불을 처리하는 방법 $paymentMethod->authorize(), $paymentMethod->capture()또는 $paymentMethod->initialize()그 논리에 따라 달라집니다.
  • 처리 된 순서를 DB 테이블에 저장하는 $ order-> save () 메소드를 호출하십시오 sales_flat_order_*.
  • DB 트랜잭션 커밋 (이 단계에서 DB는 인벤토리 테이블에 대한 잠금을 해제 함)

보시다시피, 지불 방법은 재고 잠금 및 제품 가격 또는 제품 정보 읽기 전에 돈을 청구합니다.

지불 방법에 대한 API 호출이 수행 된 후 지불 방법이 구현 된 경우에만 가격으로 제품 자체를로드하는 것이 가능합니다.

이것이 문제를 디버깅하는 데 도움이되기를 바랍니다.

재 인덱싱의 경우 결제 방법에이 문제가없는 경우 안전해야합니다. 잠금에 의존하는 읽기 작업은 돈이 청구되기 전에 수행되기 때문에.


1
감사합니다. 맞춤 결제 모듈 로직이 약간 벗어난 것 같습니다. 그러나 인덱싱 프로세스는 예외를 발생시켜 체크 아웃을 차단하는 것처럼 보입니다 registerProductsSale()(맞춤 결제 모듈을 수정하면 고객의 카드 충전 문제를 해결할 수 있음).
Roscius

8

이것은 사용자 지정 확장이므로 핵심 파일을 편집하지 않고 저장을 다시 시도하는 사용자 지정 해결 방법 (읽기 : 핵)을 찾을 수 있습니다.

도우미 클래스에 추가 된 다음 두 가지 방법으로 모든 교착 상태 문제를 해결했습니다. 전화하는 대신 $product->save()지금 전화 Mage::helper('mymodule')->saferSave($product):

/**
 * Save with a queued retry upon deadlock, set isolation level
 * @param  stdClass $obj object must have a pre-defined save() method
 * @return n/a      
 */
public function saferSave($obj)
{

    // Deadlock Workaround
    $adapter = Mage::getModel('core/resource')->getConnection('core_write');
    // Commit any existing transactions (use with caution!)
    if ($adapter->getTransactionLevel > 0) {
        $adapter->commit();
    }
    $adapter->query('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE');

    //begin a retry loop that will recycle should a deadlock pop up
    $tries = 0;
        do {
            $retry = false;
            try {
                $obj->save();
            } catch (Exception $e) {
                if ($tries < 10 and $e->getMessage()=='SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction') {
                    $retry = true;
                } else {
                    //we tried at least 10 times, go ahead and throw exception
                    throw new Zend_Db_Statement_Exception($e->getMessage());
                }
                sleep($this->getDelay());
                $tries++;
            }
        } while ($retry);

    //free resources
    unset($adapter);
    return;
}

public function getDelay($tries){
    return (int) pow(2, $tries);
}

교착 상태가 발생하면 재 시도를 대기시키고 재 시도에 대한 기하 급수적으로 시간 초과를 설정합니다. 또한 트랜잭션 격리 수준을 설정합니다. MySQL의 트랜잭션 격리 수준에 대한 자세한 내용은 SO 및 DBA.SE에 대한 많은 정보가 있습니다.

FWIW, 이후 교착 상태가 발생하지 않았습니다.


1
@Mage :: getModel ( 'core / resource') @는 새로운 연결을 만들어야합니다. 현재 트랜잭션 격리 수준을 변경하는 방법을 이해하지 못합니다.
giftnuss

@giftnuss 공정합니다. 반드시 싱글 톤이어야합니다. GitHub의 내 교착 모듈 이상에 기여 자유롭게
philwinkle

이 남자에게 감사합니다. EE 1.13 업그레이드가 내 문제를 해결할 것인지 또는 이것도 조사해야하는지 파악하려고합니다. 1.13은 비동기 적으로 색인을 생성한다는 것을 알고 있지만 동일한 기본 쿼리가 관련된 경우 비동기만으로 교착 상태가 발생하지 않도록하는 방법을 이해하기가 어렵습니다.
kalenjordan

1
@kalenjordan은 1.8 / 1.13의 비동기 및 업데이트 된 varien db 변경 조합으로 교착 상태 가능성을 줄입니다.
philwinkle

난 당신이 통과하는 것을 잊었다 생각 $tries이 함수에sleep($this->getDelay());
타히르 야신을

3

Magento 포럼에서 Zend 라이브러리 파일 편집에 대해 이야기합니다. lib / Zend / Db / Statement / Pdo.php

원래 _execute 함수 :

public function _execute(array $params = null)
    {
        // begin changes
        $tries = 0;
        do {
            $retry = false;
            try {
                if ($params !== null) {
                    return $this->_stmt->execute($params);
                } else {
                    return $this->_stmt->execute();
                }
            } catch (PDOException $e) {
                #require_once 'Zend/Db/Statement/Exception.php';
                if ($tries < 10 and $e->getMessage()=='SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction') {
                    $retry = true;
                } else {
                    throw new Zend_Db_Statement_Exception($e->getMessage());
                }
                $tries++;
            }
        } while ($retry);
        // end changes
    }

수정 후 :

public function _execute(array $params = null)
    {
        $tries = 0;
        do {
            $retry = false;
            try {
                $this->clear_result();
                $result = $this->getConnection()->query($sql);
                $this->clear_result();
            }
            catch (Exception $e) {
                if ($tries < 10 and $e->getMessage()=='SQLSTATE[HY000]: General error: 1205 Lock wait timeout exceeded; try restarting transaction') {
                    $retry = true;
                } else {
                    throw $e;
                }
                $tries++;
            }
        } while ($retry);

        return $result;
    }

보시다시피 변경된 유일한 것은 $ tries가 루프 외부로 이동되었다는 것입니다.

항상 그렇듯이 개발 / 테스트 환경에서이 기능을 사용해보고 프로덕션 환경에이 수정 프로그램을 즉시 배포하지 않는 것이 좋습니다.


2
기본 프레임 워크 파일 편집에 대해 걱정합니다. 재 시도가 대신 Magento 코드 수준에서 발생하는 것 같습니다.
Roscius

제안 된 수정 사항을 시도했으며 실제로이 특정 교착 상태로 인해 문제가 발생하지 않도록 방지했습니다. 우리는 또한 sales_flat_order_grid에 대한 저장을 교착 상태에 빠뜨 렸습니다.이 수정 사항을 적용하면 무결성 제약 조건 위반이 발생합니다.
Peter O'Callaghan

2

Magento 1.11 사이트에서도 이와 동일한 문제가 있으며 2012 년 11 월 12 일 이후 Magento가 포함 된 공개 티켓이 있습니다. 그들은 그것이 문제임을 확인했으며 패치를 만들고 있다고 가정합니다.

내 질문은 현재 가격을 다시 인덱싱해야하는 이유는 무엇입니까? 이것이 필요하다고 생각하지 않습니다.

#8 /var/www/html/app/code/core/Mage/CatalogInventory/Model/Observer.php(689): Mage_Catalog_Model_Resource_Product_Indexer_Price->reindexProductIds(Array)

1
제품이 품절되고 품절 된 제품이 카탈로그에 표시되지 않아야하는 경우, 가격 책정에 참여할 때 제품 콜렉션에서 제외시키는 가격 지수 레코드가 없다는 장점에 의해 숨겨져 있다고 생각합니다. .
davidalger

이것은 질문에 대답하지 않습니다. 원래 질문에 추가 정보를 추가하려는 것 같습니다. 아마도이 정보는 원래 질문에 대한 의견으로 더 좋을 것입니다.
Luke Mills

당신과 함께 해요, 킴 2011 년 11 월부터 같은 티켓을 열었습니다.
philwinkle

나는 이것이 기술적으로 대답이 아니라 하위 질문이라는 것을 알고 있지만이 질문을 중복으로 언급하는 질문에 대답합니다! 킴벌리 토마스와 davidalger는 "왜 가격을 재 색인 하는가?"라는 특정 답변에 대한지지를 얻었습니다. 내가 현재 인터넷 검색 중입니다! 감사!
cygnus digital

0

재색 인 중에 특정 전화를 걸 때 비슷한 교착 상태 문제가 발생했습니다. 우리에게는 고객이 장바구니에 무언가를 추가 할 때 주로 나타납니다. 실제 근본적인 문제를 해결하지는 못했지만 비동기 재 인덱싱을 구현하면 이전에보고 있던 모든 교착 상태 호출이 완전히 중지되었습니다. 근본적인 문제가 해결되고 EE / CE 버전으로 넘어갈 때까지 스톱 갭으로 작동해야합니다.


0

Philwinkle DeadlockRetry를 설치하는 것이 좋습니다. 데이터베이스에서 작동했습니다.

https://github.com/philwinkle/Philwinkle_DeadlockRetry

또한 웹 API에 부딪히는 외부 프로그램을 살펴 보는 것이 좋습니다. 우리는 제품의 수량을 업데이트하고 많은 교착 상태를 일으켰습니다. 우리는 이것을 다시 쓰고 데이터베이스로 직접 갔다.


1
리포지토리 는 더 이상 지원되지 않지만 다행히도 github.com/AOEpeople/Aoe_DbRetry 교체를 권장합니다 .
거위

-1

작년에 교착 상태 문제를 여러 번 만났는데 인덱싱 프로세스가 모든 리소스를 사용하기 때문에 서버의 메모리를 늘려서 간단히 정리했습니다.

당신은 또한 내가 기적을 사용한 비동기 재 색인 솔루션을해야합니다

보다 안정적인 시스템을 위해서는 백엔드를 프론트 엔드에서 분리하여 서로의 RAM을 먹지 않도록해야합니다.

내 경험상 소스 코드의 문제는 아닙니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.