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.