2015년 10월 26일 월요일

[MySQL] MySQL 5.7.9 Replication Error Fix - Error_no 1593 , related Master SERVER_UUID


MySQL 5.7.9 를 Replication Slave 로 추가하려고 시도하였는데, 실패했다.

[Replication 정보]

Master DB  : MariaDB 5.5.24
ㄴ Slave DB : MySQL 5.7.9  /  ( 그리고 MySQL 5.7.8-rc )


위 Replication 정보를 바탕으로 하여 CHANGE MASTER TO 구문으로 replication 정보를 입력하고,
start slave 를 시작하면 I/O thread 가 연결 error 가 발생한다.

Last_IO_Errno: 1593
Last_IO_Error: Fatal error: The slave I/O thread stops because a fatal error is encountered when it tries to get the value of SERVER_UUID variable from master.

error log 에는 다음과 같이 남는다.

2015-10-23T07:26:49.143546Z 4 [ERROR] Slave I/O for channel '': Fatal error: The slave I/O thread stops because a fatal error is encountered when it tries to get the value of SERVER_UUID variable from master. Error_code: 1593

원인은, 마스터DB 의 SERVER_UUID 값을 가져오지 못해서 슬레이브 DB 의 I/O thread 가 연결 실패했다는 것이다.

그런데 의문은 여기서부터 시작이다.
MySQL 5.7.8 버전에서는 error log 에 warning 구문이 남지만, Replication 에는 문제 없이 연결이 잘 된다.

2015-10-26T10:00:38.596947Z 2 [Warning] Slave I/O for channel '': Unknown system variable 'SERVER_UUID' on master. A probable cause is that the variable is not supported on the master (version: 5.5.24-MariaDB-log), even though it is on the slave (version: 5.7.8-rc-log), Error_code: 1193

MySQL 5.7.9 버전에서는 error 가 발생하면서 Replication 연결이 실패된다.
하위버전에서 상위버전으로 마이그레이션을 하기 위해 replication 연결을 해야 하는데, 연결 할 수 없는 문제가 발생한다.

MySQL 5.7.8 버전과 MySQL 5.7.9 버전에서 어떤 차이로 인해 Replication 연결 성공 여부가 결졍되는 것인가 ?

마스터 DB 에서 general log 를 활성화 한 뒤에 확인해 보았다.

MASTER_DB> set global general_log = ON ; 


그리고 슬레이브 DB 에서 MySQL 5.7.8 버전과 MySQL 5.7.9 버전 각각에서 Replication 연결을 해 보았다.
마스터 DB에 남은 쿼리의 결과는 다음과 같다.

-- mysql 5.7.8 에서 replication start 시 다음과 같은 명령이 실행된다 ( general log ) 

151026 10:05:12   302 Connect replication@10.10.10.10 as anonymous on
      302 Query SELECT UNIX_TIMESTAMP()
      302 Query SHOW GLOBAL VARIABLES LIKE 'SERVER_ID'
      302 Query SET @master_heartbeat_period= 30000001024
      302 Query SET @master_binlog_checksum= @@global.binlog_checksum
      302 Query SELECT @master_binlog_checksum
      302 Query SELECT @@GLOBAL.GTID_MODE
      302 Query SHOW GLOBAL VARIABLES LIKE 'SERVER_UUID'
      302 Query SET @slave_uuid= '5f141444-72e7-11e5-9072-fa163e688ead'
      302 Binlog Dump Log: 'mysql-binary.000004'  Pos: 527

-- mysql 5.7.9 에서 replication start 시 다음과 같은 명령이 실행된다 ( general log )

151026 10:03:07   299 Connect replication@10.10.10.10 as anonymous on
      299 Query SELECT UNIX_TIMESTAMP()
      299 Query SELECT @@GLOBAL.SERVER_ID
      299 Query SET @master_heartbeat_period= 30000001024
      299 Query SET @master_binlog_checksum= @@global.binlog_checksum
      299 Query SELECT @master_binlog_checksum
      299 Query SELECT @@GLOBAL.GTID_MODE
      299 Query SELECT @@GLOBAL.SERVER_UUID     <--- 여기서  SERVER_UUID 를 얻지 못하면 에러가 발생한다.
      299 Quit


general log 에 남은 차이점을 살펴보면, SERVER_UUID 를 얻어내기 위해 마스터 DB 에 실행하는 명령이 다르다.

MySQL 5.7.8 version :  SHOW GLOBAL VARIABLES LIKE 'SERVER_UUID' 구문 사용
MySQL 5.7.9 version :  SELECT @@GLOBAL.SERVER_UUID 구문 사용 



참고로 마스터 DB 는  mariaDB 5.5 버전이며, 해당 버전은 server_uuid 시스템 변수가 존재하지 않는다.

SERVER_UUID 시스템 변수가 존재하지 않을 경우, 각각 명령 실행 시 리턴되는 문장이 다르다.

-- MySQL 5.7.8
-- 다음 명령으로 실행하면 empty set 이 나오지만 
SHOW GLOBAL VARIABLES LIKE 'SERVER_UUID' ;
Empty set (0.00 sec)

-- MySQL 5.7.9
-- 다음 명령으로 실행하면 에러가 발생한다. 
SELECT @@GLOBAL.SERVER_UUID  ;
ERROR 1193 (HY000): Unknown system variable 'SERVER_UUID'

이와 같은 문제로, 현재 MySQL 5.7.9 버전에서는 마스터 DB 버전에서 시스템 변수로 SERVER_UUID 가 존재하지 않으면
Replication 연결이 불가능한 이슈가 있다.

( 유사 버그 : https://bugs.mysql.com/bug.php?id=77732 )

이 부분이 문제가 된다고 판단하여, MySQL 5.7.9 source 에서 SELECT @@GLOBAL.SERVER_UUID 쿼리를 SHOW GLOBAL VARIABLES LIKE 'SERVER_UUID' ;
로 실행할 수 있도록 변경하고 build 하였다.

결과는 성공적으로 Replication 연결이 된다.

쿼리는 다음과 같이 실행된다.

-- 슬레이브 DB 에서 버전 및 replication 상태 확인

select @@version ;
+-----------+
| @@version |
+-----------+
| 5.7.9-log |
+-----------+


start slave ;

-- error log 에는 다음과 같이 Warning 으로 남는다.

2015-10-27T04:57:52.716054Z 10 [Warning] Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information.
2015-10-27T04:57:52.717232Z 11 [Warning] Slave SQL for channel '': If a crash happens this configuration does not guarantee that the relay log info will be consistent, Error_code: 0
2015-10-27T04:57:52.717622Z 11 [Note] Slave SQL thread for channel '' initialized, starting replication in log 'mysql-binary.000004' at position 1179, relay log './relay-bin.000006' position: 1097
2015-10-27T04:57:52.719683Z 10 [Note] Slave I/O thread for channel '': connected to master 'replication@10.10.10.10:3306',replication started in log 'mysql-binary.000004' at position 1179
2015-10-27T04:57:52.723828Z 10 [Warning] Slave I/O for channel '': Unknown system variable 'SERVER_UUID' on master. A probable cause is that the variable is not supported on the master (version: 5.5.24-MariaDB), even though it is on the slave (version: 5.7.9-log), Error_code: 1193


 1449 Query SELECT UNIX_TIMESTAMP()
 1449 Query SELECT @@GLOBAL.SERVER_ID
 1449 Query SET @master_heartbeat_period= 30000001024
 1449 Query SET @master_binlog_checksum= @@global.binlog_checksum
 1449 Query SELECT @master_binlog_checksum
 1449 Query SELECT @@GLOBAL.GTID_MODE
 1449 Query SHOW GLOBAL VARIABLES LIKE 'SERVER_UUID'
 1449 Query SET @slave_uuid= '5f141444-72e7-11e5-9072-fa163e688ead'
 1449 Binlog Dump Log: 'mysql-binary.000004'  Pos: 527


MySQL 5.7.13 이후 버전 에서는 해당 문제가 더이상 발생하지 않는다.

2015년 10월 22일 목요일

[MySQL] 5.7 Online DDL 그리고 varchar 타입의 특성


MySQL 5.7 버전부터 새롭게 지원되는 Online DDL 기능이 있다.

1. Index rename
2. varchar 컬럼 확장


해당 기능이 MariaDB 5.5 / MySQL 5.6 / MySQL 5.7.8 버전에서 각각 어떻게 실행되는지 살펴본다.

먼저 index rename 을 각 버전에서 실행한 결과이다.

-- ===================================================
-- MariaDB 5.5  
-- ===================================================

: MariaDB 5.5 는 index rename 명령을 지원하지 않는다. 실행 시 에러가 발생하며,
인덱스명을 변경하려면 drop index, create index 명령으로 수행해야 한다.

mysql> alter table test_tbl rename index idx_test_tbl to idx_test_tbl_normal ;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'index idx_test_tbl to idx_test_tbl_normal' at line 1


-- ===================================================
-- MySQL 5.6
-- ===================================================

: MySQL 5.6 은 index rename 명령을 지원하지 않는다. 실행 시 에러가 발생하며, 
인덱스명을 변경하려면 drop index, create index 명령으로 수행해야 한다.

mysql> alter table test rename index idx_test to idx_test_rename ;

ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'index idx_test to idx_test_rename' at line 

그러나 MySQL 5.6 에서는 인덱스 추가가 Online 으로 수행이 가능하기 때문에,

-- 1. 변경하고자 하는 인덱스 명으로 새로운 인덱스를 추가한다.
-- 2. old 인덱스를 drop 한다.

이와 같은 순서로도 Online 작업을 유도할 수 있지만,  동일 컬럼으로 인덱스를 중복하여 생성하게 되므로 ( old 인덱스를 drop 하기 전 까지 )
인덱스의 크기가 클 경우, 저장공간의 여유가 있어야 한다.

동일 컬럼으로 인덱스를 생성할 경우 다음과 같은 warning 이 발생한다.

| Note  | 1831 | Duplicate index 'idx_xxx' defined on the table 'test.test_tbl'. This is deprecated and will be disallowed in a future release. |



-- ===================================================
-- MySQL 5.7.8-rc
-- ===================================================

MySQL 5.7.8 버전에서는 rename index 명령이 지원되고, INPLACE 방식으로 동작함 ( table copy 하지 않음 ) 

mysql> select @@version ;
+--------------+
| @@version    |
+--------------+
| 5.7.8-rc-log |
+--------------+


mysql> show create table test_tbl \G
*************************** 1. row ***************************
       Table: test_tbl
Create Table: CREATE TABLE `test_tbl` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `val` varchar(60) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_test_tbl` (`id`,`val`)
) ENGINE=InnoDB AUTO_INCREMENT=2228434 DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)


-- idx_test_tbl 인덱스를 idx_test_tbl_normal 로 변경한다.

mysql>  alter table test_tbl rename index idx_test_tbl to idx_test_tbl_normal ;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0


-- 0.01 초 만에 완료된 것을 확인할 수 있다.

mysql> show create table test_tbl \G
*************************** 1. row ***************************
       Table: test_tbl
Create Table: CREATE TABLE `test_tbl` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `val` varchar(60) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_test_tbl_normal` (`id`,`val`)
) ENGINE=InnoDB AUTO_INCREMENT=2228434 DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)



두 번째로, MySQL 5.7 버전에서 varchar 컬럼 길이 확장이 online 으로 가능한데 이 부부은 한가지 제약사항이 있다.

* INPLACE ( table copy 하지 않고 ) 로 varchar 타입 컬럼 길이를 확장 할 수 있는 경우

-- 1. VARCHAR  0 ~ 255 bytes 내에서 확장하는 경우
-- 2. VARCHAR 256 ~ bytes 내에서 확장하는 경우

만일 컬럼 크기가 255 byte 이내인 컬럼을 256 bytes 이상으로 변경하려고 하면 Table copy 로 동작한다. 즉 INPLACE 로 동작하지 않는다.
이 제약사항에 대한 내용은 아래에서 설명한다.

* 참고 : 다음 설명부터 컬럼의 "글자 수" 와 "bytes 수"  라는 표현을 주의한다.

MySQL 의 VARCHAR 타입은 괄호 안에 있는 값  - VARCHAR( N )  , N 이 글자 수 이다. byte 수 가 아니다.

예를 들어, character set 이 utf8 인 경우 한 글자의 max byte 가 3byte 로, VARCHAR(255) 는 255 * 3 = 764 byte 까지 저장 가능하다.

위 설명한 제약사항을 바탕으로 하여,
varchar 컬럼 크기를 255 byte 이내의 값으로 변경할 때 online 으로 수행되는지 테스트를 진행해본다.

테스트 MySQL 의 character set 은 utf8mb4 로 max length 가 4byte 이다. 255 byte  / 4 = 63 자 이며,

varchar(20) 인 컬럼을 varchar(63) 으로 변경 해본다.

varchar(20) 은 20 * 4byte (utf8mb4) = 80 bytes
varchar(63) 은 63 * 4byte (utf8mb4) = 254 bytes

80 bytes 에서 254 bytes 로 변경하는 것 이므로, 0 ~ 255 byte 이내의 값으로 변경하는 것이다. 
이 경우에는 Online 으로 동작한다.


Create Table: CREATE TABLE `test_tbl` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `val` varchar(20) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_second` (`id`,`val`)
) ENGINE=InnoDB AUTO_INCREMENT=2228434 DEFAULT CHARSET=utf8mb4




mysql> alter table test_tbl change column val val varchar(60)  not null ;
Query OK, 0 rows affected (4.42 sec)
Records: 0  Duplicates: 0  Warnings: 0



mysql> show create table test_tbl \G

CREATE TABLE `test_tbl` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `val` varchar(60) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_test_tbl_normal` (`id`,`val`)
) ENGINE=InnoDB AUTO_INCREMENT=2228434 DEFAULT CHARSET=utf8mb4

다른 세션에서  DDL 명령을 실행중인 세션 상태를 보면, altering table 로 표시되는 것을 볼 수 있다. ( online - Inplace  로 실행되는 것이다. )

mysql> show processlist ;
+----+------+-----------+------+---------+------+----------------+------------------------------------------------------------------+
| Id | User | Host      | db   | Command | Time | State          | Info                                                             |
+----+------+-----------+------+---------+------+----------------+------------------------------------------------------------------+
| 37 | root | localhost | hong | Query   |    3 | altering table | alter table test_tbl change column val val varchar(60)  not null |
| 40 | root | localhost | NULL | Query   |    0 | starting       | show processlist                                                 |
+----+------+-----------+------+---------+------+----------------+------------------------------------------------------------------+

실제로 online 으로 적용이 되는지, 아닌지 명령을 실행하기 전에 확인하고자 한다면 
ALTER 구문에 ALGORITHM 옵션을 포함하여 실행하면 미리 확인할 수 있다.

참고로 MySQL 5.7 에서도 컬럼 축소를 할 경우엔 online 으로 수행이 불가능하다.

-- 실행 가능할 경우 
alter table test_tbl change column val val varchar(60)  not null , ALGORITHM=INPLACE ;
Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

-- 실행 불가능할 경우
alter table test_tbl change column val val varchar(20)  not null , ALGORITHM=INPLACE ;
ERROR 1846 (0A000): ALGORITHM=INPLACE is not supported. Reason: Cannot change column type INPLACE. Try ALGORITHM=COPY.


위 테스트에서는 varchar 컬럼 크기 변경을 0 ~ 255 byte 범위 내에서 변경한 것이다.

다음은 0 ~ 255 byte 범위에서 변경이 아닌, 
varchar(20) -> varchar(190) 으로 컬럼 사이즈를 변경해본다.

character set 은 utf8mb4 를 기준으로 한다.

varchar(20) : 20 * 4 = 80 byte 
varchar(190)  :  190 * 4 = 760 byte  로

실제로는 80 byte -> 760 byte 로 컬럼 크기를 변경하는 것이다. 

-- 다음과 같이 ALGORITHM 옵션을 함께 실행하면 INPLACE ( Online ) 으로 실행 불가능 한 것을 확인할 수 있다.
mysql> alter table test_tbl change column val  val varchar(190) NOT NULL , ALGORITHM=INPLACE ;
ERROR 1846 (0A000): ALGORITHM=INPLACE is not supported. Reason: Cannot change column type INPLACE. Try ALGORITHM=COPY.


-- 실제로 명령을 실행하면, thread status 가 copy to tmp table 임을 확인할 수 있다.
-- 이 DDL 구문이 실행중일 때 다른 스레드에서는 DML 명령 실행시, DDL 구문이 완료할 때 까지 대기하게 된다.

mysql> alter table test_tbl change column val  val varchar(190) NOT NULL ;
Query OK, 2135808 rows affected (29.58 sec)  <-- copy to tmp table 로 수행될 경우, rows affected 가 전체 테이블 row 로 나타난다.
Records: 2135808  Duplicates: 0  Warnings: 0

-- 다른 세션에서 DDL 구문을 실행중인 세션의 상태를 확인하면 copy to tmp table 로 나타난다.
mysql> show full processlist ;
+----+------+-----------+------+---------+------+-------------------+------------------------------------------------------------------+
| Id | User | Host      | db   | Command | Time | State             | Info                                                             |
+----+------+-----------+------+---------+------+-------------------+------------------------------------------------------------------+
| 28 | root | localhost | hong | Query   |    2 | copy to tmp table | alter table test_tbl change column val  val varchar(90) NOT NULL |
| 30 | root | localhost | hong | Query   |    0 | starting          | show full processlist                                            |
+----+------+-----------+------+---------+------+-------------------+------------------------------------------------------------------+


그러면 256 bytes 이상의 범위에서 컬럼 사이즈를 변경하면 어떻게 될까 ?

현재 varchar(190) 인 컬럼의 bytes 수는 760 이다. ( 190 * 4bytes = 760 bytes ) 
이 컬럼을 varchar(255) 로 변경해보자.

character set 은 utf8mb4 를 기준으로 한다.

varchar(190) : 190 * 4 = 760 bytes 
varchar(255) : 255 * 4 = 1020 bytes

760 bytes 를 1020 bytes 로 변경한다.

-- ALGORITM 옵션으로 Online 동작 여부를 확인한다.
mysql> alter table test_tbl change column val  val varchar(255) NOT NULL , ALGORITHM=INPLACE ;
Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0


-- Online으로 동작하는 것을 확인하고 실제로 명령을 실행해본다.
mysql> alter table test_tbl change column val  val varchar(255) NOT NULL ;
Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0


-- 0.00 초 내로 완료된 것을 확인할 수 있다.
mysql> show create table test_tbl \G
*************************** 1. row ***************************
       Table: test_tbl
Create Table: CREATE TABLE `test_tbl` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `val` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2228434 DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)



-- ===================================================
-- varchar 타입의 특성 
-- ===================================================

varchar 컬럼 확장이 0 ~ 255 byte 이내 / 256 byte 이상 범위에서만 각각 in-place 로 동작하는 varchar 타입의 특성 때문이다.

varchar 는 가변길이 문자열을 저장하는 타입으로, 
문자열 저장 시, 맨 앞의 1 또는 2 byte 를 컬럼에 저장된 문자열의 byte 길이를 나타낸다.

여기서 1 또는 2 bytes 에 문자열 byte 길이를 저장할 때는 2 진수로 저장한다.

1 byte = 8 bit 로 이진수 여덟자리를 저장할 수 있다.

위에서 설명한 바와 같이, VARCHAR 의 괄호 안의 값은 저장 가능한 문자열 갯수이다. 
만일 varchar(255) 의 경우 255 자 문자열을 저장할 수 있다는 것을 의미한다. 저장 가능한 byte 수는 character set 마다 값이 다르다. 
( ex. utf8 은 max length 가 3byte 이므로, 255 개 문자열 * 3byte = 756 bytes ) 

이 때, 255 자가 들어있다는 것을, varchar 저장 시 맨 앞의 1 byte 에 2진수로 표현하면 
10진수 255 = 2진수 11111111  이다.

그런데, 256 자가 저장되어있다는 것을 표현하려면 2진수 여덟자리로는 표현이 불가능하다.
10진수 255 = 2진수 1 0000 0000

결과적으로 2진수로 256 개의 문자열이 저장되어있다는 것을 저장하려면 2byte 가 필요하다.
2진수 16자리로 표현해야 한다는 것이다. ( 0000 0001 0000 0000 )


그래서 varchar(255) 를 varchar(256) 으로 변경을 하려고 하면, 맨 앞의 문자열 길이를 저장하는 바이트 수를 1byte 에서 2byte 로 변경해야한다.

이러한 문제로 0 ~ 255 자 / 256 자 이상 각각의  범위 내에서는 online 으로 컬럼 사이즈 변경이 가능하지만
그렇지 않을 경우 맨 앞자리 바이트 수를 늘려줘야 하기 때문에 테이블을 재생성하여, 데이터가 저장된 블록을 재 할당시키는 방식으로 동작한다.

MySQL 5.7 의 online DDL new feature 는 varchar 타입에 한정된 것으로, 다른 타입을 변경하면  table copy 로 동작한다.

MySQL 버전이 올라가면서 Online 기능들도 추가되고 성능적인 부분도 개선되고 있지만,
서비스중에 DDL ( ALTER ) 구문을 실행할 때에는 항상 주의깊게 모니터링을 해야 한다는 점을 명심한다.

-- unsigned 로 변경해도 INPLACE 로 동작 안됨

-- session1
mysql> alter table test_tbl change column id id int(10) unsigned not null auto_increment ;


-- session2 
show full processlist ;
+----+------+-----------+------+---------+------+-------------------+-----------------------------------------------------------------------------------+
| Id | User | Host      | db   | Command | Time | State             | Info                                                                              |
+----+------+-----------+------+---------+------+-------------------+-----------------------------------------------------------------------------------+
| 28 | root | localhost | hong | Query   |    5 | copy to tmp table | alter table test_tbl change column id id int(10) unsigned not null auto_increment |
| 30 | root | localhost | hong | Query   |    0 | starting          | show full processlist                                                             |
+----+------+-----------+------+---------+------+-------------------+-----------------------------------------------------------------------------------+


-- bigint 로 변경해도 INPLACE 로 동작 안됨

alter table test_tbl change column id id bigint(20) not null auto_increment ;



show full processlist ;
+----+------+-----------+------+---------+------+-------------------+-----------------------------------------------------------------------------+
| Id | User | Host      | db   | Command | Time | State             | Info                                                                        |
+----+------+-----------+------+---------+------+-------------------+-----------------------------------------------------------------------------+
| 28 | root | localhost | hong | Query   |    3 | copy to tmp table | alter table test_tbl change column id id bigint(20) not null auto_increment |
| 30 | root | localhost | hong | Query   |    0 | starting          | show full processlist                                                       |
+----+------+-----------+------+---------+------+-------------------+-----------------------------------------------------------------------------+
2 rows in set (0.00 sec)



2015년 10월 6일 화요일

[MySQL] 5.6 Online DDL


MySQL 5.1 ~ 5.5 버전까지는 스키마 변경 작업 시 (DDL) Online 으로 작업이 되지 않아서,
DBA 작업 시 다른 세션에서의 write 작업이 모두 블럭킹 되었다. 또한 과거의 MySQL 에서는 모든 DDL 작업 시 전체테이블이 재생되도록 동작하였고,
테이블이 변경되는 동안에는 ( altering ) 테이블에 write 가 불가하였다.

-- 1. 빈 테이블 생성 -> row 한 개씩 새로운 테이블로 copy
-- 2. 인덱스는 row 가 insert 될 때 마다 update 됨
-- 3. 모든 row 가 copy 되고 나면 과거의 테이블은 drop
-- 4. 새로운 테이블의 이름이 원래의 테이블 이름으로 rename 된다.

그러나  MySQL 5.6 부터는 DDL 의 일부가 Online 으로 수행할 수 있도록 변경 되었다.
테이블이 alter 되는 동안에도 SELECT 쿼리와 INSERT, UPDATE, DELETE (DML) 구문이 수행될 수 있게 허용하는 것이다.

CREATE INDEX, DROP INDEX 명령의 문법은 변경된 것이 없지만, 몇몇 요소는 성능, 공간 사용과 관련하여 영향을 줄 수 있다.

다음은 mysql 래퍼런스에 Online DDL 을 설명하는 표를 해석하였다.


- INPLACE 여부 
: ALGORITHM=INPLACE 구문을 허용하는지 여부. 값이 O 여야 ONLINE 으로 수행할 수 있는 작업이라는 뜻.

- Copies Table 여부 
: 해당 작업이 고비용의 table copy 작업을 요구하는지 여부. INPLACE 여부 컬럼과 함께 확인한다.
몇몇 작업은 ALGORITHM=INPLACE 가 허용되지만, 여전히 table copy 작업이 포함되어있는 경우가 있다.

[참고] Online 으로 수행하는 것도 두 가지 경우로 나뉜다.

1. 내부적으로 임시 테이블을 생성하면서 Online 으로 동작하는 방식 
    SELECT, DML 은 허용하지만 내부적으로 copy-table 되므로, 시스템 리소스를 사용한다.

2. 테이블 구조 메타정보만 변경하는 방식. 시스템 리소스 사용 이슈가 없음. 

1번의 경우 Online 동작으로 다른 세션에서 write 는 가능하지만, 내부적으로 임시테이블을 만드는 동작으로
2번과 비교하여 디스크 read, write 로 인한 I/O 리소스 사용량이 증가한다.

- 동시 DML 허용 여부 
완전히 online 으로 동작할 수 있는지 여부를 나타낸다. 사용자는 LOCK=NONE 을 명시할 수 있지만, MySQL 이 가능한 경우 자동으로 동시 레벨을 허용한다.
동시 DML 이 허용될 때, 동시 쿼리도 마찬가지로 허용된다.

- 동시 쿼리 허용 여부
: 특정 테이블에 작업이 진행되는 동안에 DDL 작업이 동시 쿼리를 허용하는지 여부를 나타낸다.
동시 쿼리는 모든 online DDL 작업 동안에 허용된다. 기본적으로 값이 O 이다.
DDL 을 수행하는 동안에 동시 쿼리를 허용하려고 LOCK=SHARED 옵션을 명시 할 수 있지만, MySQL 이 가능한 경우 자동으로 동시 레벨을 허용한다.


 작업  INPLACE 여부 Copies Table 여부 동시 DML 허용 여부  동시 쿼리 허용 여부  비고
 CREATE INDEX
 ADD INDEX
 O  X  O  O  단, FULLTEXT 인덱스에 대해서는 제한이 있음.
 ADD FULLTEXT INDEX O X X O 사용자가 지정한 FTS_DOC_ID 컬럼이 없다면, table-copy 를 포함하여 테이블에 FULLTEXT 인덱스를 생성한다. 이후 FULLTEXT 인덱스는 동일한 테이블에 INPLACE 로 생성 가능하다.
 DROP INDEX O X O O .frm 파일만 수정하며, 데이터파일은 수정하지 않는다.
 OPTIMIZE TABLE O O O O MySQL 5.6.17 버전에서 ALGORITHM=INPLACE 로 사용한다. 만일 old_alter_table=1 또는 mysqld 가 --skip-new 옵션이 활성화 된 상태라면, ALGORITHM=COPY 가 사용된다. OPTIMIZE TABLE 은 FULLTEXT 인덱스를 가진 테이블에는 Online DDL (ALGORITHM=INPLACE) 을 지원하지 않는다.
 컬럼에 default 값 설정 O X O O .frm 파일만 수정하며, 데이터파일은 수정하지 않는다.
 컬럼 auto-increment 값  변경 O X O O 메모리에 저장된 값을 수정하고, 데이터파일은 수정하지 않는다.
 외래키 제약조건 추가 O X O O table-copy 를 하지 않으려면, 제약조건을 생성할 때 foreign_key_checks 옵션을 비활성화 해야한다.
 외래키 제약조건 삭제 O X O O foreign_key_checks 옵션은 활성화/비활성화 될 수 있다.
 컬럼 RENAME O X O O 동시 DML 을 허용하기 위해서 동일한 데이터를 유지하고 컬럼명만 변경한다.
 컬럼 ADD O O O O auto-increment 컬럼을 추가할 때는 동시 DML 이 허용되지 않는다.
 ALGORITHM=INPLACE 가 허용되지만, 대체적으로 데이터가 재구성되므로 여전히 고비용 작업이다.
 컬럼 DROP O O O O ALGORITHM=INPLACE 가 허용되지만, 대체적으로 데이터가 재구성되므로 여전히 고비용 작업이다.
 컬럼 순서 변경 O O O O ALGORITHM=INPLACE 가 허용되지만, 대체적으로 데이터가 재구성되므로 여전히 고비용 작업이다.
 ROW_FORMAT 설정 변경 O O O O  ALGORITHM=INPLACE 가 허용되지만, 대체적으로 데이터가 재구성되므로 여전히 고비용 작업이다.
 KEY_BLOCK_SIZE 변경 O O O O  ALGORITHM=INPLACE 가 허용되지만, 대체적으로 데이터가 재구성되므로 여전히 고비용 작업이다.
 컬럼 NULL 변경 O O O O ALGORITHM=INPLACE 가 허용되지만, 대체적으로 데이터가 재구성되므로 여전히 고비용 작업이다. 
 컬럼 NOT NULL 변경 O O O O SQL_MODE 가 STRICT_ALL_TABLES 또는 STRICT_TRANS_TABLES 를 포함할때, 컬럼에 null 이 포함되어있다면 작업은 실패한다. mysql 5.6.7 서버에서는 참조 무결성의 손실이 발생할 수 있는 외래키 컬럼은 변경할 수 없다.
ALGORITHM=INPLACE 가 허용되지만, 대체적으로 데이터가 재구성되므로 여전히 고비용 작업이다. 
 컬럼 데이터타입 변경 X O X O
 PRIMARY KEY 추가 O O O O ALGORITHM=INPLACE 가 허용되지만, 대체적으로 데이터가 재구성되므로 여전히 고비용 작업이다.
 만일 컬럼이 NOT NULL 로 변경되어야 한다면 ALGORITHM=INPLACE 는 특정 조건에서 허용되지 않는다.
 PRIMARY KEY 삭제와 
 다른 컬럼 추가
 O O O O  동일한 ALTER TABLE 구문 내에 새로운 PRIMARY KEY 를 추가할 때만 ALGORITHM=INPLACE 가  허용된다.
데이터는 대체로 재구성되므로, 여전히 고비용 작업이다.
 PRIMARY KEY DROP X O X O 동일한 ALTER TABLE 구문에 새로운 PK 추가 없이 PK 를 삭제할 때에는 제한사항이 적용된다.
 캐릭터셋 변경 X O X O 기존과 다른 새로운 캐릭터 인코딩일 경우 테이블이 재구성(Rebuild) 된다.
 캐릭터셋 지정 X O X O 기존과 다른 새로운 캐릭터 인코딩일 경우 테이블이 재구성(Rebuild) 된다.
 FORCE 옵션으로 Rebuild O O O O MySQL 5.6.17 버전에서 ALGORITHM=INPLACE 로 사용한다. 만일 old_alter_table=1 또는 mysqld 가 --skip-new 옵션이 활성화 된 상태라면, ALGORITHM=COPY 가 사용된다. OPTIMIZE TABLE 은 FULLTEXT 인덱스를 가진 테이블에는 Online DDL (ALGORITHM=INPLACE) 을 지원하지 않는다.
 ALTER TABLE ... ENGINE=INNODB
 null로 Rebuild
 O O O O MySQL 5.6.17 버전에서 ALGORITHM=INPLACE 로 사용한다. 만일 old_alter_table=1 또는 mysqld 가 --skip-new 옵션이 활성화 된 상태라면, ALGORITHM=COPY 가 사용된다. OPTIMIZE TABLE 은 FULLTEXT 인덱스를 가진 테이블에는 Online DDL (ALGORITHM=INPLACE) 을 지원하지 않는다.
 테이블 레벨로
persistent statistics 옵션 설정
(STATS_PERSISTENT, STATS_AUTO_RECALC
STATS_SAMPLE_PAGES )
  O X O O .frm 파일만 수정되며, 데이터파일은 수정되지 않는다. 


Secondary Indexes

- InnoDB 테이블에 세컨더리 인덱스를 생성하고 삭제하는 것은, table-copying 작업을 건너뛰는 것은 InnoDB 플러그인을 사용하는 MySQL 5.1 과 5.5 가 동일하다.

MySQL 5.6 이상 버전에서는 인덱스가 생성 또는 삭제되는 동안, 테이블 읽기 및 쓰기 작업이 모두 가능하다.
CREATE INDEX  또는 DROP INDEX 구문은 해당 테이블에 접근하는 모든 트랜잭션이 완료된 이후에 완료되기 때문에,
인덱스의 초기 상태는 테이블의 가장 최근의 내용을 반영한다.

이전에는, 인덱스가 생성되고 삭제되는 동안에 테이블을 수정하는 것이 일반적으로 데드락으로 인해
테이블에 INSERT, UPDATE, DELETE 구문이 취소되었다.

동시 DML 은 여전히 table-copy 작업을 필요로 한다.

몇몇 다른 ALTER TABLE 작업은 동시 DML 을 허용하지만, 여전히 table-copy 를 필요로 한다. 
그러나  MySQL 5.5 이전버전보다는 table-copy 작업이 빠르다.

다음은 Online DDL 로 실행되어 동시 DML 쿼리가 가능하지만, 내부적으로 table-copy 가 발생하는 DDL 의 종류이다.
(위 표에 모두 표기되어있음.)

- 컬럼 add, drop, reorder 
- primary key add drop
- 테이블의 ROW_FORMAT 또는 KEY_BLOCK_SIZE 설정 변경
- 컬럼을 nullable 로 변경
- OPTIMIZE TABLE
- FORCE 옵션으로 테이블 재구성 (Rebuild)
- null 을 사용하여 ALTER TABLE ... ENGINE=INNODB 구문으로 테이블 재구성


참고링크
https://dev.mysql.com/doc/refman/5.6/en/innodb-create-index-overview.html
https://dev.mysql.com/doc/refman/5.6/en/innodb-online-ddl.html
https://dev.mysql.com/doc/refman/5.6/en/innodb-create-index-limitations.html