InnoDB DELETE 성능을 향상시키는 방법은 무엇입니까?


9

따라서이 감사 테이블이 있습니다 (데이터베이스의 모든 테이블에 대한 작업을 추적합니다).

CREATE TABLE `track_table` (
  `id` int(16) unsigned NOT NULL,
  `userID` smallint(16) unsigned NOT NULL,
  `tableName` varchar(255) NOT NULL DEFAULT '',
  `tupleID` int(16) unsigned NOT NULL,
  `date_insert` datetime NOT NULL,
  `action` char(12) NOT NULL DEFAULT '',
  `className` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `userID` (`userID`),
  KEY `tableID` (`tableName`,`tupleID`,`date_insert`),
  KEY `actionDate` (`action`,`date_insert`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

오래된 항목을 보관해야합니다. 테이블이 약 5 천만 행으로 커 졌으므로 행을 삭제할 수있는 가장 빠른 방법은 한 번에 테이블을 삭제하는 것입니다 (기반 tableName).

이것은 꽤 잘 작동하지만 쓰기가 많은 일부 테이블에서는 완료되지 않습니다. 내 쿼리 delete는 tupleID / tableName 조합에 대해 관련된 작업 이있는 모든 항목을 삭제합니다 .

DELETE FROM track_table WHERE tableName='someTable' AND tupleID IN (
  SELECT DISTINCT tupleID FROM track_table
  WHERE tableName='someTable' AND action='DELETE' AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
)

나는 이것을 3 일 동안 내 서버에서 실행하게하고 가장 큰 테이블을 완성하지 못했습니다. Explain 출력 (삭제를 선택하여 전환하는 경우 :

| id | select_type        | table       | type | possible_keys      | key     | key_len | ref        | rows    | Extra                        |
|  1 | PRIMARY            | track_table | ref  | tableID            | tableID | 257     | const      | 3941832 | Using where                  |
|  2 | DEPENDENT SUBQUERY | track_table | ref  | tableID,actionDate | tableID | 261     | const,func |       1 | Using where; Using temporary |

따라서 4 백만 행을 삭제하는 데 3 일이 걸리지 않아야합니다. innodb_buffer_pool_size를 3GB로 설정했으며 서버가 one_file_per_table을 사용하도록 설정되어 있지 않습니다. InnoDB 삭제 성능을 향상시킬 수있는 다른 방법은 무엇입니까? (Mac OSX에서 MySQL 5.1.43 실행)

답변:


11

데이터를 일괄 적으로 삭제할 수 있습니다.

SQL Server에서 구문은 delete top X테이블의 행입니다. 그런 다음 각 배치에 대한 트랜잭션과 함께 루프에서 수행합니다 (물론 둘 이상의 명령문이있는 경우). 트랜잭션을 짧게 유지하고 짧은 기간 동안 만 잠금을 유지하십시오.

MySQL 구문에서 : DELETE FROM userTable LIMIT 1000

제한 사항이 있습니다 ( LIMIT예를 들어 조인으로 삭제에 사용할 수 없음 ).이 경우에는 그렇게 할 수 있습니다.

복제 LIMIT와 관련 하여 사용할 경우 추가 위험 DELETE이 있습니다. 삭제 된 행은 때때로 마스터에서 삭제 된 것과 동일한 순서로 슬레이브에서 삭제되지 않습니다.


6

임시 테이블 접근 방식을 사용해보십시오. 다음과 같이 해보십시오.

1 단계) CREATE TABLE track_table_new LIKE track_table;

2 단계) INSERT INTO track_table_new SELECT * FROM track_table WHERE action='DELETE' AND date_insert >= DATE_SUB(CURDATE(), INTERVAL 30 day);

3 단계) ALTER TABLE track_table RENAME track_table_old;

4 단계) ALTER TABLE track_table_new RENAME track_table;

5 단계) DROP TABLE track_table_old;

2 단계에서 튜플 필드를 포함하지 않았습니다. 원하는 효과가 있는지 확인하십시오. 이것이 원하는 경우, 다른 이유로 튜플 필드를 사용하지 않는 한 튜플 필드를 모두 버릴 수 있습니다.


흥미로운 해결책입니다. 테이블에 튜플 필드가 필요합니다. tableName / tupleID는 기록중인 테이블의 정의되지 않은 외래 키입니다. 최근까지이 테이블은 외래 키를 지원하지 않는 MyISAM이므로 정의되지 않았습니다.
데릭 다우니

1

배치에서 원하지 않는 행을 삭제하면 다른 작업을 수행 할 수 있어야합니다. 그러나 작업을 삭제하면 조건이 있으므로 조건에 대한 열에 적절한 인덱스가 있는지 확인하십시오.

MySQL은 느슨한 인덱스 스캔의 완전한 기능을 지원하지 않기 때문에, 당신의 순서를 조정하려고 할 수 있습니다 KEY actionDate (action, date_insert)KEY actionDate (date_insert, action). 접두사가 'date_insert'인 경우 MySQL은이 인덱스를 사용하여 날짜 시간 조건 이전의 행을 스캔해야합니다.

이러한 인덱스를 사용하면 다음과 같이 SQL을 작성할 수 있습니다.

DELETE
FROM track_table
WHERE tableName='someTable'
    AND action='DELETE'
    AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
LIMIT 1000 -- Your size of batch

1
| id | select_type        | table       | type | possible_keys      | key     | key_len | ref        | rows    | Extra                        |
|  1 | PRIMARY            | track_table | ref  | tableID            | tableID | 257     | const      | 3941832 | Using where                  |
|  2 | DEPENDENT SUBQUERY | track_table | ref  | tableID,actionDate | tableID | 261     | const,func |       1 | Using where; Using temporary |

-주먹에서 key_len을 너무 크게 설명하면 가능한 한 작게 크기를 다운 그레이드해야합니다. 귀하의 쿼리에 대한 가장 좋은 방법은 작업 유형의 데이터 필드를 char (12)에서 tinyint로 변경하는 것이므로 데이터 매핑은 다음과 같습니다.

1: -> DELETE
2: -> UPDATE
3: -> INSERT
...

table_id 대신 tablename도 변경할 수 있습니다. 최상의 성능을위한 DDL은 다음을 수행 할 수 있습니다.

CREATE TABLE `track_table` (
  `id` int(11) unsigned NOT NULL,
  `userID` smallint(6) unsigned NOT NULL,
  `tableid` smallint(6) UNSIGNED NOT NULL DEFAULT 0,
  `tupleID` int(11) unsigned NOT NULL,
  `date_insert` datetime NOT NULL,
  `actionid` tinyin(4) UNSIGNED NOT NULL DEFAULT 0,
  `className` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `userID` (`userID`),
  KEY `tableID` (`tableid`,`tupleID`,`date_insert`),
  KEY `actionDate` (`actionid`,`date_insert`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE `actions` (
  `id` tinyint(4) unsigned NOT NULL 
  `actionname` varchar(255) NOT NULL,
  PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE `table_name` (
  `id` tinyint(4) unsigned NOT NULL 
  `tablename` varchar(255) NOT NULL,
  PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

쿼리가 다음과 같이 실행될 수 있습니다.

DELETE FROM track_table WHERE tableid=@tblid AND tupleID IN (
  SELECT DISTINCT tupleID FROM track_table
  WHERE tableid=@tblid AND actionid=@actionid AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
).

그러나 가장 빠른 방법은 파티션을 사용하는 것입니다. 파티션을 삭제할 수 있습니다. 현재 내 테이블에는 약 40mil 이상의 행이 있습니다. 매시간 업데이트 (매번 400k 행 업데이트), curr_date 파티션을 삭제하고 데이터를 테이블에 다시로드 할 수 있습니다. 드롭 명령이 매우 빠릅니다 (<100ms). 이 도움을 바랍니다.

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