Last Updated on 12월 16, 2022 by Jade(정현호)
안녕하세요
이번 포스팅에서는 MySQL 8.0.29 버전으로 업그레이드 이후 INSTANT 방식으로 컬럼 추가/삭제 작업시 인스턴스 비정상 종료(장애) 에 대한 내용과 INSTANT 방식으로 컬럼 추가/삭제시 64회 제한(limit)에 대한 내용을 확인해보려고 합니다.
이슈 상황 요약
MySQL 8.0.29 로 업그레이드 후, DB 를 사용하는 과정에서 INSTANT 방식으로 ADD Column or DROP Column 수행 후에 인스턴스 비정상 종료(Instance Crash) 가 발생되는 현상 입니다.
사용 버전: MySQL 8.0.25 , MySQL 8.0.29
문제 발생 조건 및 재현
해당 이슈는 MySQL 8.0.29 이전 버전에서 8.0.29 버전으로 업그레이드한 DB에서 INSTANT 방식으로 ADD/DROP Column 을 하였을 경우에 발생되는 이슈 입니다.
INSTANT 방식으로 ADD/DROP Column 시 문제가 발생되는 연관된 이슈는 포스팅 작성 시점에서 확인되는 bug 은 총 5건 입니다.
Bug 34233264, Bug 34243694, Bug 34181432, Bug 34302445, Bug 34488482
그 중에서 Bug 34488482 의 경우 fixed version이 8.0.31 로 확인되기 때문에 해당 bug의 경우 Reproduce Condition 으로 테스트한다면 8.0.30에서도 재현될 것으로 예상이 됩니다.
그 외 8.0.29버전에서 INSTANT 방식으로 ADD/DROP Column 이 된 경우 Percona XtraBackup 수행하게 되면 MySQL 인스턴스가 Crash 가 되는 케이스도 있습니다.
재현 케이스
재현을 위해서는 먼저 MySQL 버전 8.0.29 이하에서 아래와 같은 절차를 진행합니다.
(해당 재현 케이스 이외에 재현 케이스(Condition) 은 다양하게 있습니다.)
• 테이블 생성 및 데이터 입력
mysql> select @@version; +-----------+ | @@version | +-----------+ | 8.0.25 | +-----------+ mysql> create table tb_test_1 (`id` bigint unsigned not null auto_increment primary key, `k` bigint unsigned not null, col1 varchar(50) not null default '8.0.25', col2 varchar(50) not null default '8.0.25' ) engine=innodb default charset=utf8mb4 collate=utf8mb4_general_ci ; mysql> insert into tb_test_1(`k`) select a.help_topic_id from mysql.help_relation a join mysql.help_relation b limit 100000;
• instant_cols 컬럼값 조회
ALTER TABLE ... ADD COLUMN with ALGORITHM=INSTANT를 사용하여 첫 번째 INSTANT Column 이 추가되기 전에 존재했던 컬럼 수에 대한 정보입니다. 해당 컬럼은 8.0.28까지만 유효하고 8.0.29 버전 부터는 사용되지 않으나, 8.0.29 이전 버전에서 있던 정보는 계속 표시됩니다.
현재는 INSTANT 로 추가한 컬럼이 없기 때문에 정보가 출력 되지 않습니다.
mysql> select * from information_schema.innodb_tables where INSTANT_COLS>0\G Empty set (0.03 sec)
• 컬럼 추가 및 instant_cols 조회
컬럼을 추가한 다음 instant_cols 컬럼을 다시 확인하면 4 라는 값이 확인 되는 것을 확인할 수 있습니다.
mysql> alter table tb_test_1 add column j_1 varchar(60) default 'kkk', algorithm=instant; mysql> alter table tb_test_1 add column j_2 varchar(60) default 'kkk', algorithm=instant; mysql> alter table tb_test_1 add column j_3 varchar(60) default 'kkk', algorithm=instant; mysql> select * from information_schema.innodb_tables where INSTANT_COLS>0\G *************************** 1. row *************************** TABLE_ID: 1361 NAME: test/tb_test_1 FLAG: 33 N_COLS: 10 SPACE: 303 ROW_FORMAT: Dynamic ZIP_PAGE_SIZE: 0 SPACE_TYPE: Single INSTANT_COLS: 4
이 상태에서 MySQL 8.0.25 를 종료하고 업그레이드를 진행 합니다.
• 종료
./mysql.server stop Shutting down MySQL.. SUCCESS!
• 업그레이드(8.0.25 -> 8.0.29) 로그
[System] [MY-013576] [InnoDB] InnoDB initialization has started. [System] [MY-013577] [InnoDB] InnoDB initialization has ended. [System] [MY-013381] [Server] Server upgrade from '80025' to '80029' started. [System] [MY-013381] [Server] Server upgrade from '80025' to '80029' completed.
* 8.0.29 이전 버전 에서 8.0.29 로 업그레이드후에 발생되는 이슈로 이전 버전에 대해서는 버전이 특정 되지 않으며 포스팅에서 사용한 8.0.25 이외 다른 버전에서도 발생됩니다.
• TOTAL_ROW_VERSIONS 확인
업그레이드 완료 후에 innodb_tables 테이블에는 total_row_versions 컬럼이 추가 되게 됩니다.
초기값은 0 이며 ALTER TABLE ... ALGORITHM=INSTANT 에 의해서 컬럼을 추가하거나 제거 작업에 의해 값이 증가합니다. ALTER TABLE 또는 OPTIMIZE TABLE 을 하거나 mysqldump 등에 의한 테이블 재생성시 값이 0으로 재설정 됩니다.
mysql> select * from information_schema.innodb_tables where INSTANT_COLS>0\G *************************** 1. row *************************** TABLE_ID: 1361 NAME: test/tb_test_1 FLAG: 33 N_COLS: 10 SPACE: 303 ROW_FORMAT: Dynamic ZIP_PAGE_SIZE: 0 SPACE_TYPE: Single INSTANT_COLS: 4 TOTAL_ROW_VERSIONS: 0
permitted is 64
MySQL 8.0.29 버전에서 부터 새롭게 추가된 information_schema.innodb_tables 의 total_row_versions 컬럼과 관련하여 Instant 방식으로 ADD/DROP Column 시 total_row_versions 컬럼값이 증가하며, 이 total_row_versions 은 64회 제한을 가집니다.
The maximum number of row versions permitted is 64, as each row version requires additional space for table metadata. When the row version limit is reached, ADD COLUMN and DROP COLUMN operations using ALGORITHM=INSTANT are rejected with an error message that recommends rebuilding the table using the COPY or INPLACE algorithm.
total_row_versions 제한 내용 확인
• 테이블 생성 및 TOTAL_ROW_VERSIONS 조회
-- 테스트 테이블 생성 mysql> create table tb_ddl_test (no bigint unsigned auto_increment primary key, col1 varchar(10), col2 varchar(10) ); -- TOTAL_ROW_VERSIONS 값 조회 mysql> select NAME, TOTAL_ROW_VERSIONS from information_schema.innodb_tables where 1=1 and name='test/tb_ddl_test'; +------------------+--------------------+ | NAME | TOTAL_ROW_VERSIONS | +------------------+--------------------+ | test/tb_ddl_test | 0 | +------------------+--------------------+ <-- 테이블 생성시 0 임
* 해당 테이블은 total_row_number 64 제한과 관련한 테스트를 위해서 생성한 테스트 테이블로 Instance Crash 재현을 위한 테이블은 아닙니다.
• 컬럼 추가/삭제 후 조회
mysql> alter table tb_ddl_test add column col3 varchar(10), algorithm=instant; mysql> select NAME, TOTAL_ROW_VERSIONS from information_schema.innodb_tables where name='test/tb_ddl_test'; +------------------+--------------------+ | NAME | TOTAL_ROW_VERSIONS | +------------------+--------------------+ | test/tb_ddl_test | 1 | +------------------+--------------------+ <-- TOTAL_ROW_VERSIONS : 1 mysql> alter table tb_ddl_test drop column col3 , algorithm=instant; mysql> select NAME, TOTAL_ROW_VERSIONS from information_schema.innodb_tables where name='test/tb_ddl_test'; +------------------+--------------------+ | NAME | TOTAL_ROW_VERSIONS | +------------------+--------------------+ | test/tb_ddl_test | 2 | +------------------+--------------------+ <-- TOTAL_ROW_VERSIONS : 2
컬럼 추가 와 삭제를 진행하였고, total_row_versions 가 2가 되었습니다.
• 컬럼 추가 및 삭제(추가적으로)
INSTANT 방식으로 수 차례 컬럼 추가와 삭제를 진행하게 되어, total_row_versions 이 64가 넘게 되면 더이상 INSTANT 방식으로 수행이 불가능 하게 됩니다.
mysql> alter table tb_ddl_test add column col3 varchar(10), algorithm=instant; mysql> select NAME, TOTAL_ROW_VERSIONS from information_schema.innodb_tables where name='test/tb_ddl_test'; +------------------+--------------------+ | NAME | TOTAL_ROW_VERSIONS | +------------------+--------------------+ | test/tb_ddl_test | 63 | +------------------+--------------------+ -- TOTAL_ROW_VERSIONS : 63 mysql> alter table tb_ddl_test drop column col3 , algorithm=instant; mysql> select NAME, TOTAL_ROW_VERSIONS from information_schema.innodb_tables where name='test/tb_ddl_test'; +------------------+--------------------+ | NAME | TOTAL_ROW_VERSIONS | +------------------+--------------------+ | test/tb_ddl_test | 64 | +------------------+--------------------+ -- TOTAL_ROW_VERSIONS : 64 mysql> alter table tb_ddl_test add column col3 varchar(10), algorithm=instant; ERROR 4092 (HY000): Maximum row versions reached for table test/tb_ddl_test. No more columns can be added or dropped instantly. Please use COPY/INPLACE. <!!-- TOTAL_ROW_VERSIONS 이 64에 도달하면 그 다음에는 INSTANT 방식의 ADD/DROP Column 이 불가능해짐
위의 테스트와 같이 TOTAL_ROW_VERSIONS = 64 까지 INSTANT 방식으로 ADD/DROP Column 이 되고, 그 다음 부터는 에러가 발생 되며, INSTANT 대신 algorithm 방식을 INPLACE 또는 COPY 방식을 사용해야 합니다.
INPLACE 또는 COPY 방식의 ALTER TABLE을 수행하거나 OPTIMIZE TABLE 을 하거나 mysqldump 등에 의한 테이블 재생성시 값이 0으로 재설정 됩니다.
아래 부터는 Instance Crash 재현 테스트 내용이 계속됩니다.
• 컬럼 추가
테스트 테이블인 tb_test_1 에 INSTANT 방식으로 컬럼을 추가합니다.
mysql> alter table tb_test_1 add column j_4 varchar(60) default '8.0.29', algorithm=instant; mysql> alter table tb_test_1 add column j_5 varchar(60) default '8.0.29', algorithm=instant; mysql> alter table tb_test_1 add column j_6 varchar(60) default '8.0.29', algorithm=instant;
• TOTAL_ROW_VERSIONS 다시 확인
INSTANT 방식으로 컬럼을 추가 하게 되면 total_row_versions 컬럼이 증가 하게 됩니다.
mysql> select * from information_schema.innodb_tables where INSTANT_COLS>0\G *************************** 1. row *************************** TABLE_ID: 1361 NAME: test/tb_test_1 FLAG: 33 N_COLS: 13 SPACE: 303 ROW_FORMAT: Dynamic ZIP_PAGE_SIZE: 0 SPACE_TYPE: Single INSTANT_COLS: 4 TOTAL_ROW_VERSIONS: 3
• 데이터 입력
이제 데이터를 입력합니다. 테스트 예제에서는 2번째 Insert 구문에서 Instance Crash 가 발생하였습니다.
mysql> insert into tb_test_1(`k`) -> select a.help_topic_id -> from mysql.help_relation a join mysql.help_relation b -> limit 100000; Query OK, 100000 rows affected (1.13 sec) Records: 100000 Duplicates: 0 Warnings: 0 mysql> insert into tb_test_1(`k`) -> select a.help_topic_id -> from mysql.help_relation a join mysql.help_relation b -> limit 100000; ERROR 2013 (HY000): Lost connection to MySQL server during query No connection. Trying to reconnect... ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/usr/local/mysql-8.0.29/tmp/mysql.sock' (111) ERROR: Can't connect to the server
• Instance Crash 로그 내용 일부
022-11-29T21:52:03.063327+09:00 10 [ERROR] [MY-013183] [InnoDB] Assertion failure: rec.cc:392 thread 139674889688832 InnoDB: We intentionally generate a memory trap. InnoDB: Submit a detailed bug report to http://bugs.mysql.com. InnoDB: If you get repeated assertion failures or crashes, even InnoDB: immediately after the mysqld startup, there may be InnoDB: corruption in the InnoDB tablespace. Please refer to InnoDB: http://dev.mysql.com/doc/refman/8.0/en/forcing-innodb-recovery.html InnoDB: about forcing recovery. 12:52:03 UTC - mysqld got signal 6 ; Most likely, you have hit a bug, but this error can also be caused by malfunctioning hardware. Thread pointer: 0x7f0844f65640 Attempting backtrace. You can use the following information to find out where mysqld died. If you see no messages after this, something went terribly wrong... stack_bottom = 7f08982f0d60 thread_stack 0x100000 /usr/local/mysql-8.0.29/bin/mysqld(my_print_stacktrace(unsigned char const*, unsigned long)+0x2e) [0x1f29fae] /usr/local/mysql-8.0.29/bin/mysqld(print_fatal_signal(int)+0x2bf) [0xee188f] /usr/local/mysql-8.0.29/bin/mysqld(my_server_abort()+0x6e) [0xee19ce] /usr/local/mysql-8.0.29/bin/mysqld(my_abort()+0xa) [0x1f2477a] /usr/local/mysql-8.0.29/bin/mysqld(ut_dbg_assertion_failed(char const*, char const*, unsigned long)+0x329) [0x21de189] /usr/local/mysql-8.0.29/bin/mysqld(rec_get_offsets_func(unsigned char const*, dict_index_t const*, unsigned long*, unsigned long, mem_block_info_t**)+0xb7) [0x21eadc7] /usr/local/mysql-8.0.29/bin/mysqld(page_cur_search_with_match(buf_block_t const*, dict_index_t const*, dtuple_t const*, page_cur_mode_t, unsigned long*, unsigned long*, page_cur_t*, rtr_info*)+0x1d9) [0x20ef779] /usr/local/mysql-8.0.29/bin/mysqld(btr_root_raise_and_insert(unsigned int, btr_cur_t*, unsigned long**, mem_block_info_t**, dtuple_t const*, mtr_t*)+0x66c) [0x221551c] /usr/local/mysql-8.0.29/bin/mysqld(btr_cur_pessimistic_insert(unsigned int, btr_cur_t*, unsigned long**, mem_block_info_t**, dtuple_t*, unsigned char**, big_rec_t**, que_thr_t*, mtr_t*)+0x3cd) [0x221a81d] /usr/local/mysql-8.0.29/bin/mysqld(btr_insert_on_non_leaf_level(unsigned int, dict_index_t*, unsigned long, dtuple_t*, ut::Location, mtr_t*)+0x29b) [0x220a0ab] < ...중략... > /usr/local/mysql-8.0.29/bin/mysqld() [0x24490a5] /lib64/libpthread.so.0(+0x81cf) [0x7f08b3c371cf] /lib64/libc.so.6(clone+0x43) [0x7f08b1fb5e73] Trying to get some variables. Some pointers may be invalid and cause the dump to abort. Query (7f0844345520): is an invalid pointer Connection ID (thread ID): 10 Status: NOT_KILLED The manual page at http://dev.mysql.com/doc/mysql/en/crashing.html contains information that should help you find out what is causing the crash. 2022-11-29T21:52:03.390061+09:00 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started. 2022-11-29T21:52:04.837908+09:00 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended. InnoDB: Progress in percents: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 1002022-11-29T21:52:05.044911+09:00 0 [System] [MY-010229] [Server] Starting XA crash recovery... 2022-11-29T21:52:05.055235+09:00 0 [System] [MY-010232] [Server] XA crash recovery finished.
conclusion
확인되는 Bug 은 5개 이며 (연관된 Duplicate Bug 까지 하면 더 많음) Oracle Support 문서에서도 다양한 문제가 있는 상태임으로 fixed version 에 대해서는 8.0.31 버전으로 가이드 하고 있습니다.
이와 관련해서 Percona Xtrabackup 수행 과정에서 이슈가 있기 때문에 해당 내용에 대해서는 Percona 에서도 내용을 다루고 있습니다.
• percona.com/percona-xtrabackup/instant
• percona.com/percona-xtrabackup-8-0-29-and-instant-add-drop-columns
그 외 다양한 채널에서 해당 이슈로 인하여 Crash 가 난 부분에 대해서는 공유된 내용이 있습니다.
• Massive increase in InnoDB corruption issues since upgrade to 8.0.29
• forums.freebsd.org/dont-upgrade-to-mysql-8-0-29-corruption-issues
• serverfault.com/mysql-errors-corruption-after-updating-wordpress
이러한 문제로 인하여 AWS RDS for MySQL 에서는 8.0.29 로 업그레이드하지 못하도록 해당 버전을 RDS Version List 에서 제거된(Removed) 상태입니다.
• repost.aws/questions/rds-mysql-8-0-29-deprecated-not-do-upgrade-or-modify
[RDS 생성 화면 - 버전 리스트에서 8.0.29 가 제거된 상태]
MySQL 에서도 8.0.29 버전의 다운로드가 제거되었으며 release note 에도 내용이 추가되었습니다.
Important
This release is no longer available for download. It was removed due to a critical issue that could cause data inInnoDB
tables having added columns to be interpreted incorrectly. Please upgrade to MySQL 8.0.30 instead.
[MySQL 다운로드 - 리스트업에서 8.0.29 버전이 제거된 상태]
Support Say : These bugs are fixed in 8.0.31. However, latent corruption might exist.
그래서 업그레이드를 8.0.29~31 정도 버전으로 진행하시려고 한다면 지금은 8.0.31 버전으로 업그레이드하거나, 8.0.28 버전에서 조금 더 사용하면서 향후 8.0.32나 8.0.33 버전 정도까지 해서 관련된 이슈의 fixed가 포함되어 릴리즈 되는지를 확인해보는 것이 좋을 것 같다고 생각 합니다.
Reference
Reference URL
• mysql.com/relnotes/news-8-0-29
• Massive increase in InnoDB corruption issues since upgrade to 8.0.29
• repost.aws/questions/rds-mysql-8-0-29-deprecated-not-do-upgrade-or-modify
• forums.freebsd.org/dont-upgrade-to-mysql-8-0-29-corruption-issues
• serverfault.com/mysql-errors-corruption-after-updating-wordpress
• percona.com/percona-xtrabackup/instant
• percona.com/percona-xtrabackup-8-0-29-and-instant-add-drop-columns
관련된 다른 글
Principal DBA(MySQL, AWS Aurora, Oracle)
핀테크 서비스인 핀다에서 데이터베이스를 운영하고 있어요(at finda.co.kr)
Previous - 당근마켓, 위메프, Oracle Korea ACS / Fedora Kor UserGroup 운영중
Database 외에도 NoSQL , Linux , Python, Cloud, Http/PHP CGI 등에도 관심이 있습니다
purityboy83@gmail.com / admin@hoing.io