MySQL에서 트리거와 저장 프로 시저의 성능


11

DBA.StackExchange에 대한 게시물 ( 레코드에서 개정 번호를 유지하는 트리거의 모범 사례는 무엇입니까? )은 MySQL의 성능과 관련하여 흥미로운 질문을 제기했습니다.

컨텍스트는 업데이트되는 각 행에 대해 테이블에 레코드를 삽입하려는 것입니다. 행이 업데이트되기 전에 이전 값을 저장 한 다음 열 중 하나 ( "버전"열)를 늘리려 고합니다.

트리거 내 에서이 작업을 수행하면 잘 작동합니다. MySQL의 경우 트리거는 행 단위 이므로 쉬운 솔루션입니다. 현재 테이블에있는 데이터를 선택하고 로깅 테이블에 삽입 한 후 새 데이터의 "버전"열을 업데이트하십시오.

그러나이 논리를 저장 프로 시저로 옮길 수 있습니다. 그렇게하면 삽입을 수행 한 다음 테이블에서 "버전"열을 증가시킵니다. 모든 것이 기반으로 설정됩니다.

따라서이 삽입을 수행 할 때 세트 기반 저장 프로 시저 접근 방식 또는 트리거 기반 접근 방식을 사용하는 것이 더 성능이 좋습니까?

이 질문은 행 단위 트리거가 있기 때문에 MySQL에 관한 것이지만 다른 행 단위 트리거 DBMS에 적용될 수 있습니다.


1
버전 관리 논리를 저장 프로 시저로 푸시하는 것과 관련하여 명심해야 할 한 가지는 누군가가 어떻게 감사 메커니즘을 우회하여 직접 테이블에 쓸 때 얼마나 험난 할 것입니까?
— billinkc

동의한다. 그러나 규모의 다른 쪽 끝에서 특정 상황에서 의도적 으로이 로깅을 우회하려는 경우가 있습니다. 물론, 그것은 완전히 다른 질문 입니다. 성능에 미치는 영향이 궁금합니다.
— Richard

답변:


7

간단하게하기 위해 트리거는 모든 종류의 데이터베이스 변경 사항 추적을 구현하는 방법입니다. 그러나 트리거를 사용할 때 후드에서 어떤 일이 발생하는지 알고 있어야합니다.

"Trigger Overhead"헤드 아래의 256 페이지의 MySQL Stored Procedure Programming 에 따르면 다음과 같이 말합니다.

필요에 따라 트리거가 적용되는 DML 문에 오버 헤드가 추가된다는 점을 기억해야합니다. 실제 오버 헤드 양은 트리거의 특성에 따라 달라 지지만 모든 MySQL 트리거가 각 행에 대해 실행될 때 많은 행을 처리하는 명령문에 대해 오버 헤드가 빠르게 누적 될 수 있습니다. 따라서 값 비싼 SQL 문 또는 절차 코드를 트리거에 배치하지 마십시오.

트리거 오버 헤드에 대한 자세한 설명은 529-531 페이지에 나와 있습니다. 이 섹션의 결론은 다음과 같습니다.

여기서 교훈은 다음과 같습니다. 트리거 코드는 DML 문의 영향을받는 모든 행마다 한 번씩 실행되므로 트리거는 DML 성능에서 가장 중요한 요소가 될 수 있습니다. 트리거 본문 내부의 코드는 가능한 한 가벼워 야하며, 특히 트리거의 모든 SQL 문은 가능할 때마다 인덱스에서 지원해야합니다.

트리거를 사용할 때이 책에 언급되지 않은 또 다른 요소는 감사 로깅과 관련하여 데이터에 로그인하는 내용을 알고 있어야합니다. MyISAM 테이블에 로그인하도록 선택하면 MyISAM 테이블에 대한 각 INSERT는 INSERT 중에 전체 테이블 잠금을 생성합니다. 트래픽이 많고 트랜잭션이 많은 환경에서는 심각한 병목 현상이 발생할 수 있습니다. 또한 트리거가 InnoDB 테이블에 대해 트리거되고 트리거 내에서 MyISAM의 변경 사항을 기록하는 경우 롤백 할 수없는 ACID 준수 (예 : 블록 트랜잭션을 자동 커밋으로 축소)를 비밀리에 비활성화합니다.

InnoDB 테이블에서 트리거를 사용하고 변경 사항을 로깅하는 경우

  • 로그인 한 테이블도 InnoDB입니다
  • 자동 커밋을 해제했습니다
  • START TRANSACTION ... COMMIT / ROLLBACK 블록을 완전히 설정했습니다.

이러한 방식으로 감사 로그는 기본 테이블과 마찬가지로 COMMIT / ROLLBACK의 이점을 얻을 수 있습니다.

저장 프로 시저 사용과 관련하여 추적중인 테이블에 대해 DML의 모든 지점에서 저장 프로 시저를 힘들게 호출해야합니다. 수만 줄의 응용 프로그램 코드에서 로깅 변경 사항을 쉽게 놓칠 수 있습니다. 이러한 코드를 트리거에 배치하면 모든 DML 문을 찾을 필요가 없습니다.

경고

트리거가 얼마나 복잡한 지에 따라 여전히 병목 현상이 발생할 수 있습니다. 감사 로깅의 병목 현상을 줄이려면 할 수있는 일이 있습니다. 그러나 약간의 인프라 변경이 필요합니다.

상용 하드웨어를 사용하여 두 개의 DB 서버 추가

감사 로깅으로 인해 주 데이터베이스 (MD)의 쓰기 I / O를 줄이기 위해 서버가 작동합니다. 이를 달성하는 방법은 다음과 같습니다.

단계 01) 주 데이터베이스에서 이진 로깅을 설정하십시오.

단계 02) 저렴한 서버를 사용하여 바이너리 로깅이 활성화 된 MySQL (MD와 동일한 버전)을 설정하십시오. 이것은 DM이 될 것입니다. MD에서 DM으로 복제를 설정합니다.

단계 03) 두 번째 저렴한 서버를 사용하여 이진 로깅을 비활성화하고 MySQL (MD와 동일한 버전)을 설정합니다. --replicate-do-table 을 사용하도록 각 감사 테이블을 설정 하십시오 . 이것은 AU입니다. DM에서 AU로 복제를 설정합니다.

단계 04) mysql은 MD에서 테이블 구조를 덤프하여 DM과 AU에로드합니다.

5 단계) MDH의 모든 감사 테이블을 BLACKHOLE 스토리지 엔진을 사용하도록 변환

단계 06) BLACKHOLE 스토리지 엔진을 사용하도록 DM 및 AU의 모든 테이블을 변환하십시오.

07 단계) MyISAM 스토리지 엔진을 사용하도록 AU의 모든 감사 테이블 변환

완료되면

  • DM은 MD에서 복제하고 바이너리 로그에만 내용을 기록합니다.
  • 함께 --replicate-할 테이블의 모든 감사 테이블에 필터, AU는 DM에서 복제합니다

이것이하는 일은 감사 정보를 별도의 DB 서버에 저장하고 MD가 일반적으로하는 쓰기 I / O 저하를 줄입니다.


엄청난 대답 +++ 1
— b_dubb

1

다음은이 업데이트를 대량으로 수행하는 방법입니다.

이 예의 경우

  • table_A에는 PRIMARY KEY id가 있습니다
  • ID를 PRIMARY KEY로 사용하여 table_A_Keys2Update라는 테이블을 작성합니다.
  • 업데이트해야 할 table_A의 id로 table_A_Keys2Update를 채 웁니다.

table_A_Keys2Update를 작성하려면 다음을 수행하십시오.

CREATE TABLE table_A_Keys2Update SELECT id FROM table_A;
ALTER TABLE table_A_Keys2Update ADD PRIMARY KEY (id);

개정 번호를 증분해야하는 ID로 table_A_Keys2Update를 채운 후 다음 UPDATE JOIN을 수행하여 id가 table_A 및 table_A_Keys2Update 모두에있는 모든 행의 개정 번호를 증가 시키십시오.

UPDATE table_A A INNER JOIN table_A_Keys2Update B USING (id)
SET A.revision = A.revision + 1;

이 한 줄 쿼리는 트리거와 저장 프로 시저를 대체 할 수 있습니다.

선택적으로이 하나의 쿼리를 저장 프로 시저에 넣고 원하는 경우 호출 할 수 있습니다.


정말 궁금한 삽입물입니다. 당신이 경우 INSERT INTO 감사 SELECT <무엇> <primary_table> <저장 프로 시저에서 매개 변수> 어디서 당신이 대량으로 삽입 할 수 있습니다. 트리거에서 VALUES <업데이트 된 행의 데이터> 감사 감사를 삽입 합니다. 그렇다면 한 줄씩 한 줄씩 인서트가 벌크 인서트보다 빠를까요?
— Richard

간단하게하기 위해 트리거는 훨씬 우수합니다. 1) primary_table이 피크 타임 중간에 대량 삽입을 경험하지 않는 경우, 2) 감사 정보는 주어진 순간에 필요할 때마다 읽어야합니다. 트래픽이 적습니다.
— RolandoMySQLDBA
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.