MySQL-InnoDB를 위해 ALTER TABLE하는 가장 빠른 방법


12

변경하려는 InnoDB 테이블이 있습니다. 테이블에는 ~ 80M 개의 행이 있으며 몇 개의 인덱스를 종료합니다.

열 중 하나의 이름을 변경하고 색인을 몇 개 더 추가하고 싶습니다.

  • 가장 빠른 방법은 무엇입니까?
  • "일반" alter table이 가장 빠른 솔루션입니까?

현재 내가 관심있는 것은 속도 :)


제발 SHOW CREATE TABLE tblname\G,에 대한 요구 사항이 변경되는 열, 열의 데이터 유형과 열에 대한 새 이름을 보여줍니다.
RolandoMySQLDBA

여기에 있습니다 : pastie.org/3078349 이름을 변경해야하는 열 sent_at은 몇 가지 인덱스를 추가하는 것입니다.
Ran

sent_at의 이름을 무엇으로 바꾸어야합니까?
RolandoMySQLDBA

new_sent_at
Ran

답변:


14

ALTER TABLE의 속도를 높이는 한 가지 확실한 방법은 불필요한 인덱스를 제거하는 것입니다

다음은 새 버전의 테이블을로드하는 초기 단계입니다.

CREATE TABLE s_relations_new LIKE s_relations;
#
# Drop Duplicate Indexes
#
ALTER TABLE s_relations_new
    DROP INDEX source_persona_index,
    DROP INDEX target_persona_index,
    DROP INDEX target_persona_relation_type_index
;

다음 사항에 유의하십시오.

  • source_persona_index는 4 개의 다른 인덱스에서 첫 번째 열이므로 삭제했습니다.

    • unique_target_persona
    • unique_target_object
    • source_and_target_object_index
    • source_target_persona_index
  • target_persona_index가 다른 두 인덱스의 첫 번째 열이므로 삭제했습니다.

    • target_persona_relation_type_index
    • target_persona_relation_type_message_id_index
  • 처음 두 열도 target_persona_relation_type_message_id_index에 있기 때문에 target_persona_relation_type_index를 삭제했습니다.

OK 불필요한 인덱스를 처리합니다. 카디널리티가 낮은 인덱스가 있습니까? 이를 결정하는 방법은 다음과 같습니다.

다음 쿼리를 실행하십시오.

SELECT COUNT(DISTINCT sent_at)               FROM s_relations;
SELECT COUNT(DISTINCT message_id)            FROM s_relations;
SELECT COUNT(DISTINCT target_object_id)      FROM s_relations;

귀하의 질문에 따르면 약 80,000,000 개의 행이 있습니다. 일반적으로 MySQL Query Optimizer는 선택한 열의 카디널리티가 테이블 행 수의 5 %보다 큰 경우 인덱스를 사용하지 않습니다. 이 경우 4,000,000이됩니다.

  • COUNT(DISTINCT sent_at)> 4,000,000 인 경우
    • 그때 ALTER TABLE s_relations_new DROP INDEX sent_at_index;
  • COUNT(DISTINCT message_id)> 4,000,000 인 경우
    • 그때 ALTER TABLE s_relations_new DROP INDEX message_id_index;
  • COUNT(DISTINCT target_object_id)> 4,000,000 인 경우
    • 그때 ALTER TABLE s_relations_new DROP INDEX target_object_index;

해당 인덱스의 유용성 또는 사용 불능이 결정되면 데이터를 다시로드 할 수 있습니다

#
# Change the Column Name
# Load the Table
#
ALTER TABLE s_relations_new CHANGE sent_at sent_at_new int(11) DEFAULT NULL;
INSERT INTO s_relations_new SELECT * FROM s_relations;

그게 맞나요? 아니야!

귀하의 웹 사이트가이 기간 동안 가동 된 경우 s_relations_new를로드하는 동안 s_relations에 대해 INSERT가 실행 중일 수 있습니다. 누락 된 행을 어떻게 검색 할 수 있습니까?

s_relations_new에서 최대 ID를 찾아 s_relations에서 해당 ID 뒤에있는 모든 것을 추가하십시오. 테이블이 고정되어이 업데이트에만 사용되도록하려면 s_relation_new에 삽입 된 마지막 행을 가져 오기 위해 약간의 가동 중지 시간이 있어야합니다. 여기 당신이하는 일이 있습니다 :

OS에서 아무도 로그인 할 수 없도록 mysql을 다시 시작하십시오. root @ localhost (TCP / IP 비활성화) :

$ service mysql restart --skip-networking

다음으로 mysql에 로그인하고 마지막 행을로드하십시오.

mysql> SELECT MAX(id) INTO @maxidnew FROM s_relations_new;
mysql> INSERT INTO s_relations_new SELECT * FROM s_relations WHERE id > @maxidnew;
mysql> ALTER TABLE s_relations RENAME s_relations_old;
mysql> ALTER TABLE s_relations_new RENAME s_relations;

그런 다음 mysql을 정상적으로 다시 시작하십시오.

$ service mysql restart

이제 mysql을 중단시킬 수 없다면 s_relations에서 미끼 및 스위치를 수행해야합니다. mysql에 로그인하고 다음을 수행하십시오.

mysql> ALTER TABLE s_relations RENAME s_relations_old;
mysql> SELECT MAX(id) INTO @maxidnew FROM s_relations_new;
mysql> INSERT INTO s_relations_new SELECT * FROM s_relations_old WHERE id > @maxidnew;
mysql> ALTER TABLE s_relations_new RENAME s_relations;

시도 해봐 !!!

주의 사항 :이 작업에 만족하면 최대한 빨리 이전 테이블을 삭제할 수 있습니다.

mysql> DROP TABLE s_relations_old;

12

정답은 사용중인 MySQL 엔진의 버전에 따라 다릅니다.

5.6+를 사용하는 경우 이름 변경 및 인덱스 추가 / 제거는 온라인 으로 수행 됩니다 (예 : 모든 테이블 데이터를 복사하지 않음).

ALTER TABLE평소와 같이 사용 하면 이름 바꾸기 및 인덱스 삭제가 즉각적으로 이루어지고 인덱스 추가가 합리적으로 빠릅니다 (모든 테이블을 한 번 읽는 것만 큼 빠름).

5.1 이상을 사용하고 InnoDB 플러그인이 활성화 된 경우 인덱스 추가 / 제거도 온라인 상태가됩니다. 이름 변경에 대해 잘 모르겠습니다.

이전 버전을 사용하는 경우 ALTER TABLE여전히 가장 빠르지 만 모든 데이터 가 임시 테이블 아래의 임시 테이블에 다시 삽입 되므로 엄청나게 느릴 수 있습니다.

마지막으로, 신화를 해제 할 시간입니다. 불행히도 여기에 답변에 대해 언급 할 카르마가 충분하지 않지만 가장 투표가 많은 답변을 수정하는 것이 중요하다고 생각합니다. 이것은 잘못되었습니다 :

일반적으로 MySQL Query Optimizer는 선택한 열의 카디널리티가 테이블 행 수의 5 %보다 큰 경우 인덱스를 사용하지 않습니다.

실제로 다른 방법 입니다.

인덱스는 적은 행 을 선택하는 데 유용 하므로 높은 카디널리티 를 갖는 것이 중요합니다 . 이는 많은 고유 값과 동일한 값을 가진 통계적으로 적은 행을 의미합니다.


링크 플러그인 이노 문서 (대표 한계로 인해 붙여 넣을 수 없었다).
mezis

2
MySQL 5.5에서 RENAME TABLE예상대로 인스턴트를 찾았 지만 CHANGE COLUMN기본 키의 이름을 바꾸는 것이 전체 사본을 수행했습니다 ... 7 시간! 아마도 기본 키이기 때문일까요? 안좋다.
KCD

2

Maria DB 10.1.12와 동일한 문제가 있었지만 설명서를 읽은 후 "사본"작업을 수행하여 테이블 복사본을 제거하는 옵션이 있음을 알았습니다. 이 옵션을 사용하면 alter 테이블이 매우 빠릅니다. 제 경우에는 다음과 같습니다.

alter table user add column (resettoken varchar(256),
  resettoken_date date, resettoken_count int), algorithm=inplace;

이것은 매우 빠릅니다. 알고리즘 옵션이 없으면 종료되지 않습니다.

https://mariadb.com/kb/en/mariadb/alter-table/


0

열 이름을 바꾸려면

ALTER TABLE tablename CHANGE columnname newcolumnname datatype;

괜찮고 다운 타임이 없어야합니다.

인덱스의 경우 CREATE INDEX 문은 테이블을 잠급니다. 언급 한대로 사용하지 않은 슬레이브라면 문제가되지 않습니다.

다른 옵션 중 하나는 적절한 열 이름과 인덱스가있는 완전히 새로운 테이블을 만드는 것입니다. 그런 다음 모든 데이터를 복사하고 일련의 데이터를 실행할 수 있습니다.

BEGIN TRAN;
ALTER TABLE RENAME tablename tablenameold;
ALTER TABLE RENAME newtablename tablename;
DROP TABLE tablenameold;
COMMIT TRAN;

이렇게하면 공간을 두 배로 일시적으로 사용하는 비용으로 가동 중지 시간이 최소화됩니다.


1
MySQL의 DDL은 트랜잭션이 아닙니다. 각 DDL 문은 COMMIT를 트리거합니다. 나는 이것에 대해 썼습니다 : dba.stackexchange.com/a/36799/877
RolandoMySQLDBA

0

나는이 문제도 가지고 있으며이 SQL을 사용했다.

/*on créé la table COPY SANS les nouveaux champs et SANS les FKs */
CREATE TABLE IF NOT EXISTS prestations_copy LIKE prestations;

/* on supprime les FKs de la table actuelle */
ALTER TABLE `prestations`
DROP FOREIGN KEY `fk_prestations_pres_promos`,
DROP FOREIGN KEY `fk_prestations_activites`;

/* on remet les FKs sur la table copy */
ALTER TABLE prestations_copy 
    ADD CONSTRAINT `fk_prestations_activites` FOREIGN KEY (`act_id`) REFERENCES `activites` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION,
    ADD CONSTRAINT `fk_prestations_pres_promos` FOREIGN KEY (`presp_id`) REFERENCES `pres_promos` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION;

/* On fait le transfert des données de la table actuelle vers la copy, ATTENTION: il faut le même nombre de colonnes */
INSERT INTO prestations_copy
SELECT * FROM prestations;

/* On modifie notre table copy de la façon que l'on souhaite */
ALTER TABLE `prestations_copy`
    ADD COLUMN `seo_mot_clef` VARCHAR(50) NULL;

/* on supprime la table actuelle et renome la copy avec le bon nom de table */
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE prestations;
RENAME TABLE prestations_copy TO prestations;
SET FOREIGN_KEY_CHECKS=1;   

누군가에게 도움이되기를 바랍니다.

문안 인사,

의지

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