테이블 잠금없이 거대한 MySQL 프로덕션 테이블에 인덱스 생성


104

~ 5M 행 MySQL 테이블에 인덱스를 만들어야합니다. 그것은 생산 테이블이며 CREATE INDEX 문을 실행하면 모든 것의 완전한 블록이 두려워 ...

삽입 및 선택을 차단하지 않고 해당 인덱스를 만드는 방법이 있습니까?

중지하고 색인을 생성하고 시스템을 다시 시작할 필요가 없는지 궁금합니다!


1
myisam_sort_buffer_size 및 myisam_max_sort_file_size가 충분히 큰지 확인하십시오.
Jon Black

답변:


130

[2017] 업데이트 : MySQL 5.6은 온라인 인덱스 업데이트를 지원합니다.

https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-operations.html#online-ddl-index-syntax-notes

MySQL 5.6 이상에서는 인덱스가 생성되거나 삭제되는 동안 테이블을 읽기 및 쓰기 작업에 사용할 수 있습니다. CREATE INDEX 또는 DROP INDEX 문은 테이블에 액세스하는 모든 트랜잭션이 완료된 후에 만 ​​완료되므로 인덱스의 초기 상태는 테이블의 가장 최근 내용을 반영합니다. 이전에는 인덱스를 만들거나 삭제하는 동안 테이블을 수정하면 일반적으로 테이블에서 INSERT, UPDATE 또는 DELETE 문을 취소하는 교착 상태가 발생했습니다.

[2015] MySQL 5.5에서 테이블 인덱스 블록 쓰기 업데이트

위의 답변에서 :

"데이터베이스가 온라인 상태 일 때 인덱스가 5.1보다 큰 버전을 사용하는 경우 생성됩니다. 따라서 프로덕션 시스템 사용이 중단되지 않을 것이라고 걱정하지 마십시오."

이것은 **** FALSE ****입니다 (최소한 MyISAM / InnoDB 테이블의 경우 99.999 %의 사람들이 사용합니다. Clustered Edition은 다릅니다.)

테이블에서 UPDATE 작업을 수행하면 인덱스가 생성되는 동안 BLOCK 됩니다 . MySQL은 이것에 대해 정말, 정말 어리 석습니다.

테스트 스크립트 :

(   
  for n in {1..50}; do
    #(time mysql -uroot -e 'select  * from website_development.users where id = 41225\G'>/dev/null) 2>&1 | grep real;
    (time mysql -uroot -e 'update website_development.users set bio="" where id = 41225\G'>/dev/null) 2>&1 | grep real;
  done
) | cat -n &
PID=$!
sleep 0.05
echo "Index Update - START"
mysql -uroot website_development -e 'alter table users add index ddopsonfu (last_name, email, first_name, confirmation_token, current_sign_in_ip);'
echo "Index Update - FINISH"
sleep 0.05
kill $PID
time mysql -uroot website_development -e 'drop index ddopsonfu on users;'

내 서버 (InnoDB) :

Server version: 5.5.25a Source distribution

출력 (인덱스 업데이트를 완료하는 데 걸리는 ~ 400ms 동안 6 번째 작업이 어떻게 차단되는지 확인) :

 1  real    0m0.009s
 2  real    0m0.009s
 3  real    0m0.009s
 4  real    0m0.012s
 5  real    0m0.009s
Index Update - START
Index Update - FINISH
 6  real    0m0.388s
 7  real    0m0.009s
 8  real    0m0.009s
 9  real    0m0.009s
10  real    0m0.009s
11  real    0m0.009s

차단하지 않는 읽기 작업 대 (스크립트에서 줄 주석을 바꿉니다) :

 1  real    0m0.010s
 2  real    0m0.009s
 3  real    0m0.009s
 4  real    0m0.010s
 5  real    0m0.009s
Index Update - START
 6  real    0m0.010s
 7  real    0m0.010s
 8  real    0m0.011s
 9  real    0m0.010s
...
41  real    0m0.009s
42  real    0m0.010s
43  real    0m0.009s
Index Update - FINISH
44  real    0m0.012s
45  real    0m0.009s
46  real    0m0.009s
47  real    0m0.010s
48  real    0m0.009s

다운 타임없이 MySQL의 스키마 업데이트

지금까지 MySql 스키마를 업데이트하고 가용성 중단을 겪지 않는 방법은 하나뿐입니다. 원형 마스터 :

  • 마스터 A에는 MySQL 데이터베이스가 실행 중입니다.
  • 마스터 B를 서비스로 전환하고 마스터 A에서 쓰기를 복제하도록합니다 (B는 A의 슬레이브 임).
  • 마스터 B에서 스키마 업데이트를 수행합니다. 업그레이드 중에 뒤쳐집니다.
  • 마스터 B가 따라 잡도록하십시오. 불변 : 스키마 변경은 다운 버전 스키마에서 복제 된 명령을 처리 할 수 ​​있어야합니다. 인덱싱 변경 사항이 적용됩니다. 일반적으로 단순 열 추가가 적합합니다. 열을 제거 하시겠습니까? 아마 아닐 것입니다.
  • 원자는 (날 믿어, 당신이 할), 당신은 마지막 쓰기가 B에 복제되어 있는지 확인해야 안전하려면 마스터 B로 마스터 A로부터 모든 클라이언트를 교환 하기 전에B는 첫 번째 쓰기를받습니다. 2 개 이상의 마스터에 대한 동시 쓰기를 허용하면 ... 깊은 수준에서 MySQL 복제를 더 잘 이해하거나 고통의 세계로 향합니다. 극심한 고통. 마찬가지로 AUTOINCREMENT 열이 있습니까 ??? 당신은 망했다 (한 마스터에 짝수를 사용하고 다른 마스터에 확률을 사용하지 않는 한). "올바른 일을하기 위해"MySQL 복제를 신뢰하지 마십시오. 그것은 똑똑하지 않으며 당신을 구하지 않을 것입니다. 명령 줄에서 이진 트랜잭션 로그를 복사하여 수동으로 재생하는 것보다 약간 덜 안전합니다. 그래도 이전 마스터에서 모든 클라이언트의 연결을 끊고 새 마스터로 전환하는 작업은 몇 초 안에 스키마 업그레이드를 기다리는 것보다 훨씬 빠르게 수행 할 수 있습니다.
  • 이제 마스터 B는 새로운 마스터입니다. 새 스키마가 있습니다. 인생은 좋다. 맥주를 마셔 라. 최악은 끝났습니다.
  • 마스터 A로 프로세스를 반복하여 스키마를 업그레이드하여 1 차 마스터 (현재 마스터 B)가 전원을 잃거나 사용자가 죽는 경우를 대비하여 새로운 보조 마스터가되도록합니다.

스키마를 업데이트하는 쉬운 방법은 그렇지 않습니다. 심각한 생산 환경에서 실행 가능 네, 그렇습니다. 제발, 제발, 쓰기를 차단하지 않고 MySQL 테이블에 인덱스를 추가하는 더 쉬운 방법이 있다면 알려주세요.

인터넷 검색 은 유사한 기술을 설명하는 이 기사로 연결됩니다. 더 좋은 점은 절차의 동일한 시점에서 술을 마시는 것이 좋습니다 (기사를 읽기 전에 제 답변을 썼다는 점에 유의하세요)!

Percona의 pt-online-schema-change

기사 내가 도구에 대해 이야기 위의 링크는 PT-온라인 스키마 변경은 , 그 작품은 다음과 같습니다 :

  • 원본과 동일한 구조로 새 테이블을 만듭니다.
  • 새 테이블에서 스키마를 업데이트합니다.
  • 변경 내용이 복사본과 동기화되도록 원본 테이블에 트리거를 추가합니다.
  • 원본 테이블에서 일괄 적으로 행을 복사합니다.
  • 원래 테이블을 다른 곳으로 옮기고 새 테이블로 교체하십시오.
  • 이전 테이블을 삭제하십시오.

이 도구를 직접 사용해 본 적이 없습니다. YMMV

RDS

저는 현재 Amazon의 RDS를 통해 MySQL을 사용하고 있습니다. MySQL을 마무리하고 관리하는 정말 멋진 서비스로, 버튼 하나로 새로운 읽기 복제본을 추가하고 하드웨어 SKU에서 데이터베이스를 투명하게 업그레이드 할 수 있습니다. 정말 편리합니다. 데이터베이스에 대한 슈퍼 액세스 권한을 얻지 못하므로 복제를 직접 망칠 수 없습니다 (이것이 축복입니까, 저주입니까?). 그러나 읽기 전용 복제본 승격 을 사용하여 읽기 전용 슬레이브에서 스키마를 변경 한 다음 해당 슬레이브를 새 마스터로 승격 할 수 있습니다. 위에서 설명한 것과 똑같은 트릭으로 실행하기가 훨씬 쉽습니다. 그들은 여전히 ​​컷 오버를 돕기 위해 많은 일을하지 않습니다. 앱을 재구성하고 다시 시작해야합니다.


3
pt-online-schema-change는 마스터-슬레이브 복제에서도 훌륭하게 작동합니다. 나는 딸꾹질이나 중단 시간없이 2 개의 복제 슬레이브가있는 프로덕션 마스터 db의 바쁜 읽기 2 천만 개 이상의 레코드 테이블에서 라이브 마이그레이션을 수행하는 데 사용했습니다. 스크립트를 준비하는 데 약간의 시간이 걸리며 일반적으로 동일한 SQL을 실행하지만 조각 형식 (ALTER TABLE 없음)으로 실행하려면 원시 SQL 변경 사항이 포함 된 .sql 파일과 래퍼로 .sh 파일을 만들어야합니다. pt-online-schema-change를 사용하여 여러 명령을 문자열로 묶고 쉼표로 구분하여 실행할 수 있습니다.
Alex Le

-1; 이전 버전에 대해서는 모르지만 인덱스 생성이 MySQL 5.6 이상에서 동시 DML을 차단하지 않는다는 것을 알고 있습니다 (이 답변이 작성되었을 때 RC가 존재 했으며이 답변이 지속되었을 때 공식적으로 출시 된 RC) 삽입을 허용하면서 프로덕션 테이블에서 여러 시간 인덱스 생성을 실행하기 위해이 기능에 의존했기 때문입니다. 그리고 5.5 이하에서 DML을 차단하는 인덱스 생성에 대해 옳을 있지만 여기에서 설명하는 1 초 미만의 지연은 완전히 설득력이 없습니다.
Mark Amery

@MarkAmery-차단 동작은 차단 동작이며 400ms는 영원합니다. 인덱스 업데이트를위한 MySQL 5.5 블록. 더 큰 테스트 데이터베이스를 구축하면 몇 초, 몇 시간 또는 며칠 동안 차단됩니다. MySQL 5.6에 온라인 스키마 업데이트가 있기 전에이 게시물을 작성 했으므로 원래 콘텐츠에 그 사실이 반영되지 않았습니다. 새로운 정보를 반영하도록 게시물을 업데이트했습니다.
데이브 Dopson

@DaveDopson, UPDATE 작업 만 차단한다고 100 % 확신하십니까?
toto_tico

내가 테스트 한 버전의 경우였습니다.
Dave Dopson

67

블로그 게시물에서 설명하는 것처럼 InnoDB ALTER TABLE메커니즘은 MySQL 5.6 용으로 완전히 재 설계되었습니다.

(이 주제에 대한 독점적 인 개요를 보려면 MySQL 문서 에서 오후의 읽을 거리를 제공 할 수 있습니다.)

/ 에 대한 잠금 결과 없이 테이블에 인덱스를 추가하려면 다음 명령문 형식을 사용할 수 있습니다.UPDATEINSERT

ALTER TABLE my_table ADD INDEX my_table__idx (my_column), ALGORITHM=INPLACE, LOCK=NONE;


16

MySQL 5.6 업데이트 (2013 년 2 월) : 이제 InnoDB 테이블을 사용해도 인덱스가 생성되는 동안 읽기 및 쓰기 작업을 수행 할 수 있습니다-http: //dev.mysql.com/doc/refman/5.6/en/innodb-create-index -overview.html

MySQL 5.6 이상에서는 인덱스가 생성되거나 삭제되는 동안 테이블을 읽기 및 쓰기 작업에 사용할 수 있습니다. CREATE INDEX 또는 DROP INDEX 문은 테이블에 액세스하는 모든 트랜잭션이 완료된 후에 만 ​​완료되므로 인덱스의 초기 상태는 테이블의 가장 최근 내용을 반영합니다. 이전에는 인덱스를 만들거나 삭제하는 동안 테이블을 수정하면 일반적으로 테이블에서 INSERT, UPDATE 또는 DELETE 문을 취소하는 교착 상태가 발생했습니다.

과:

MySQL 5.6에서는이 기능이 더욱 일반화되었습니다. 인덱스가 생성되는 동안 테이블을 읽고 쓸 수 있으며, DML 작업을 차단하지 않고 테이블을 복사하지 않고 더 많은 종류의 ALTER TABLE 작업을 수행 할 수 있습니다. 따라서 MySQL 5.6 이상에서는 일반적으로이 기능 세트를 빠른 인덱스 생성이 아닌 온라인 DDL이라고합니다.

에서 http://dev.mysql.com/doc/refman/5.6/en/glossary.html#glos_fast_index_creation


그렇다면 Dave의 분석을 어떻게 설명 할 수 있습니까?
Nikhil Sahu

1
@NikhilSahu Dave는 분명히 MySQL 5.6에서 테스트하지 않았지만 일부 이전 버전에서 테스트했습니다. Dave가 답변의 초기 개정판을 게시 한 시점에는 5.6이 아직 출시되지 않았습니다.
Mark Amery

+1. 내 분석은 MySQL 5.5 (2013 년에 사용 가능한 최신 버전)에 대한 것입니다. MySQL 5.6의 새로운 기능을 반영하기 위해 답변을 업데이트하고 있습니다.
데이브 Dopson

3

pt-online-schema-change는 마이그레이션으로 인해 사이트가 중단되지 않도록 정말로 원하는 경우 사용할 수있는 방법입니다.

위의 의견에서 썼 듯이 프로덕션에서 pt-online-schema-change에 대한 몇 가지 경험이 있습니다. 2,000 만개 이상의 레코드와 마스터-> 2 개의 읽기 전용 복제 슬레이브로 구성된 기본 테이블이 있습니다. pt-online-schema-change를 사용하여 새 열 추가, 문자 집합 변경, 여러 인덱스 추가에 이르기까지 최소한 수십 번의 마이그레이션을 수행했습니다. 마이그레이션 시간 동안에도 많은 트래픽을 처리하며 문제가 발생하지 않았습니다. 물론 프로덕션에서 실행하기 전에 모든 스크립트를 매우 철저하게 테스트해야합니다.

pt-online-schema-change가 데이터를 한 번만 복사하면되도록 변경 사항을 하나의 스크립트로 일괄 처리하려고했습니다. 그리고 데이터를 잃을 수 있으므로 열 이름을 변경할 때는 매우주의해야합니다. 그러나 색인을 추가하는 것은 괜찮습니다.


에 대한 귀하의 비정규 추천에 동의하지 않습니다 pt-online-schema-change. 훌륭하지만 MySQL 5.6+의 온라인 DDL 기능이 이미 잘 작동하는 많은 상황에서는 과잉입니다. 또한 제한 사항 (예 : 트리거를 잘 사용하지 않음)이 있으며 스키마 변경이 진행되는 동안 원본 테이블에 삽입 할 때마다 필요한 쓰기 양이 두 배가됩니다. 이것은 일반적인 온라인 스키마 변경보다 디스크에 많은 부담을 주므로 간단한 방식으로 스키마 변경을 실행하는 것만으로도 문제가없는 상황에서 "사이트를 중단"할 가능성이 있습니다.
Mark Amery

당시 pt-online-schema-change에 대한 실제 경험을 바탕으로 작성했기 때문에 내 추천을 "무자격"이라고 부르는 이유를 잘 모르겠습니다. 스키마 변경을 실행할 때 주어진 순간에 사이트에 최소 1000 명 이상의 방문자가 있었고 물론 디스크 IO가 부담 스러웠지만 사이트가 다운되지 않았습니다. 좋은 캐싱도 도움이되었습니다. 나는 MySQL 5.6+ 온라인 DDL을 사용하지 않았지만 경험상 pt-online-schema-change는 우리의 경우에 잘 작동했습니다.
Alex Le

1
@AlexYe Yikes, 나는 "언급 할 자격이없는 사람에 의해 배달된다"라는 의미가 아니라 "예약없이"의 의미에서 "무자격"을 의미했습니다. 후자의 해석은 귀하의 의견을 볼 때까지 나에게 발생하지 않았으며 확실히 그렇지 않습니다. 내가 의도 한 것이 아닙니다! 즉 pt-online-schema-change, 유용한 도구이기는하지만 일반적인 온라인 DDL이 좋은 경우가 많고 더 좋은 경우가 많으므로 모든 권장 사항은 보편적이지 않고 신중하게주의해야합니다.
Mark Amery
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.