배후 조종자: Laravel에서 데이터베이스 사무의 작업 방식

다음은 under the hood 시리즈입니다. 이번에는 Laravel의 데이터베이스 업무에 관한 것입니다.나는 Laravel의 업무를 어떻게 사용하는지에 관한 모든 내용을 중복하지 않을 것이다.주제에 익숙하지 않은 경우 문서here에서 모든 컨텐트를 찾을 수 있습니다.우리가 지금 주목하고 있는 것은 이러한 실현이 백그라운드에서 어떻게 작동하는지, 최근에 무엇이 우리를 골치 아프게 하는지, 그리고 그것을 어떻게 피하는지이다.자, 시작합시다.

거래 폐쇄
익숙한 것처럼 DB facade의 트랜잭션 방법에서 클립을 사용할 수 있습니다. 다음과 같습니다.
DB::transaction(function () {
    DB::update('update users set votes = 1');
    DB::delete('delete from posts');
});
이 방법을 사용하면, 업무 시작, 제출, 스크롤을 걱정할 필요가 없습니다. (문제가 발생하면) 이 모든 것을 자동으로 완성합니다.
트랜잭션과 관련된 방법은 Illumb/Database/Concerns/ManageTransactions에 있습니다.php 기능.두 번째 파라미터를 DB::transaction 방법에 전달할 수 있습니다. 이 파라미터는 이상이나 잠금이 발생했을 때 다시 업무를 시도하는 횟수입니다.코드 순환은 주어진 시도 횟수를 통해 새로운 업무를 시작합니다.
public function transaction(Closure $callback, $attempts = 1)
{
    for ($currentAttempt = 1; $currentAttempt <= $attempts; $currentAttempt++) {
        $this->beginTransaction();
리셋 함수를 실행하려고 시도합니다. 리셋에 이상이 생기면 스크롤합니다.정의된 시도 횟수에 도달할 때까지 다시 시도합니다.
try {
    $callbackResult = $callback($this);
}

// If we catch an exception we'll rollback this transaction and try again if we
// are not out of attempts. If we are out of attempts we will just throw the
// exception back out and let the developer handle an uncaught exceptions.
catch (Throwable $e) {
    $this->handleTransactionException(
        $e, $currentAttempt, $attempts
    );

    continue;
}
만약 모든 것이 순조롭게 진행된다면, 거래가 완료되면,
try {
    if ($this->transactions == 1) {
        $this->getPdo()->commit();

        optional($this->transactionsManager)->commit($this->getName());
    }

    $this->transactions = max(0, $this->transactions - 1);
} catch (Throwable $e) {
    $this->handleCommitTransactionException(
        $e, $currentAttempt, $attempts
    );

    continue;
}
콜백 결과를 반환하고 제출한 이벤트를 트리거하려면 다음과 같이 하십시오.
$this->fireConnectionEvent('committed');

return $callbackResult;
클립을 사용하는 것은 Laravel에서 업무를 사용하는 가장 간단하고 간단한 방법이다.

수동 트랜잭션
Laravel을 통해 거래를 제어할 수 있습니다.이것이 바로 최근에 우리에게 폐를 끼친 원인이다.우리는 곧 이 점을 이야기할 것이다. 그러나 우선 그것이 어떻게 작동하는지 보자.
  • DB:begintransaction()을 사용하여 트랜잭션을 시작합니다.
  • 모든 것이 순조롭다면 DB::commit()로 제출;
  • 또는 DB::rollback()을 사용하여 다른 방식으로 롤백합니다.
  • 그것은 아주 잘 작동하지만, 만약 당신이 끼워 넣는 일을 사용하고 싶다면.예를 들어, MySQL은 nested transactions를 지원하지 않지만 InnoDB 엔진은 savepoints을 지원합니다.
    Laravel은 이 기능을 사용합니다(지원되는 경우).따라서 DB::begintransaction () 을 호출할 때, 업무의 플러그인 단계를 계산합니다.이 플러그인 단계에 따라 새 사무를 만들거나 저장점을 만듭니다.
    if ($this->transactions == 0) {
        $this->reconnectIfMissingConnection();
    
        try {
            $this->getPdo()->beginTransaction();
        } catch (Throwable $e) {
            $this->handleBeginTransactionException($e);
        }
    } elseif ($this->transactions >= 1 && $this->queryGrammar->supportsSavepoints()) {
        $this->createSavepoint();
    }
    
    중첩 레벨이 한 개가 아닌 경우 DB::rollBack () 를 호출하면 계수기를 줄이고 이전 저장소로 굴러갑니다.
    protected function performRollBack($toLevel)
    {
        if ($toLevel == 0) {
            $this->getPdo()->rollBack();
        } elseif ($this->queryGrammar->supportsSavepoints()) {
            $this->getPdo()->exec(
                $this->queryGrammar->compileSavepointRollBack('trans'.($toLevel + 1))
            );
        }
    }
    
    다음은 까다로운 부분입니다. 계수기가 1일 때 DB::commit () 가 데이터베이스에 제출됩니다.그렇지 않으면 카운터를 줄이고 제출한 이벤트만 터치합니다.
    public function commit()
    {
        if ($this->transactions == 1) {
            $this->getPdo()->commit();
    
            optional($this->transactionsManager)->commit($this->getName());
        }
    
        $this->transactions = max(0, $this->transactions - 1);
    
        $this->fireConnectionEvent('committed');
    }
    
    분명히 이것은 좋지만, 모든 시작된 업무에 대해commit이나 롤백을 호출해야 합니다.특히 순환 중에 진행될 때

    문제가 생긴 예
    다음은 하나의 예이다. 우리의 예에서 무엇이 두통을 일으켰는가(이것은 위조 코드이지만 본질은 같다).
    $products = Products::all()
    foreach($products as $product){
        try { 
            DB::beginTransaction();
    
            if ($product->isExpensiveEnough()) {
                continue;
            }
    
            $product->price += 100;
            $product->save();
            DB::commit();
        }
        catch(Exception $e){
            DB::rollback();    
        }
    }
    
    그래서 기본적으로 우리는 제품을 순환시키고 일부 조건에 따라 속성을 바꾸고 싶다.이 위조 코드에서, 우리는 가격을 올리고 싶습니다. 왜냐하면 이 제품들은 비싸지 않기 때문입니다. -)
    그러면 위의 예에는 어떤 문제가 있습니까?만일 우리가 제품을 갱신하고 모든 것이 순조롭다면 우리는 하나의 사무를 시작하여 모델을 갱신하고 제출할 것이다.이상이 발생했을 때, 그것은 심지어 정상적으로 작동할 수 있고, 우리는 스크롤을 실행할 수 있다.
    그러나 조건이 충족되지 않을 때, 우리는 다음 교체에서 새로운 업무를 계속 만들 것입니다.거래 카운터가 2로 증가할 것이다.아래의 제출은 데이터베이스에 실제 제출을 하지 않고 계수기만 줄일 뿐이다.다음 교체는 카운터를 2등으로 늘리는 새로운 작업을 시작합니다. 비긴 트랜잭션 () 과commit ()/rollback () 방법의 수량이 동기화되지 않으면 다음 교체는 기록을 업데이트하지 않습니다.
    몇 시간 동안 실행된 명령이었고, 업데이트해야 할 내용이 전혀 업데이트되지 않았을 때, 이것은 웃기지 않았다.
    그래서 우리가 여기서 배운 교훈은 매우 간단하다.모든 열린 업무는 제출하거나 스크롤을 통해 끝내야 한다는 것을 항상 기억하십시오.특히 순환 중에는
    게시물Under the hood: How database transactions work in Laravel이 가장 먼저Daniel Werner에 올라왔다.

    좋은 웹페이지 즐겨찾기