{"id":"IL-0451","org":"Daangn","date":"2026-10-06","docs":["https://dev.mysql.com/doc/refman/8.0/en/information-schema-innodb-buffer-page-table.html","https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-23.html","https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-24.html"],"title":{"en":"Setting indexes INVISIBLE on Aurora MySQL 3.10 spiked CPU and ReadIOPS on all three readers but not on the writer","ko":"Aurora MySQL 에서 인덱스를 INVISIBLE 로 바꿀 때마다 Reader 3대의 버퍼풀이 무효화돼 CPU 와 ReadIOPS 가 튀었다"},"domain":["database","aws"],"impact":{"scope":"All three readers of one service's Aurora MySQL cluster: each index DDL invalidated the table's cached pages, raising ReadIOPS from a few hundred to 3.1K and CPU from 30% to 50-60%","users":"unknown","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"On Aurora MySQL from 3.08.0 up to before 3.14, making an index INVISIBLE is not free: the writer sees nothing, but every reader loses its cached pages of the whole table, and the larger and busier the table, the bigger the CPU and ReadIOPS spike while the cache warms again. Until the fixed version is in use, index DDL should be treated as an operation that empties the reader buffer pools. When looking for unused indexes, the usage statistics must be read on every node, because an index unused on the writer may be used heavily on one reader. Querying information_schema.INNODB_BUFFER_PAGE scans the whole buffer pool and takes seconds and adds load on large instances, so it is not a query to run routinely in production.","ko":"Aurora MySQL 에서 인덱스를 INVISIBLE 로 바꾸는 작업은 메타데이터만 바꾸므로 부하가 없다는 생각은 Writer 에서만 맞았다. 3.08.0 부터 3.14 이전 버전에서는 인덱스 DDL 을 Reader 버퍼풀을 비우는 작업으로 보고 운영한다. 행이 많고 트래픽이 높은 테이블일수록 버퍼풀이 다시 채워지는 동안 ReadIOPS 와 CPU 가 크게 오를 수 있다.\n\n미사용 인덱스를 고를 때는 performance_schema.table_io_waits_summary_by_index_usage 를 모든 노드에서 확인한다. 노드마다 쿼리 패턴이 달라서, Writer 에서 count_read 가 0 인 인덱스를 어떤 Reader 는 자주 쓸 수 있다. INNODB_BUFFER_PAGE 조회는 버퍼풀 전체를 훑으므로 버퍼풀이 큰 인스턴스에서는 몇 초씩 걸리고 부하도 준다. 운영 중에는 꼭 필요할 때만 조회한다."},"related":[],"sources":[{"url":"https://medium.com/daangn/aurora-mysql-%EC%9D%B8%EB%8D%B1%EC%8A%A4-ddl%EC%9D%B4-reader-%EB%B2%84%ED%8D%BC%ED%92%80%EC%9D%84-%EB%B9%84%EC%9A%B4%EB%8B%A4-16c6328dea50","kind":"blog","archived":null}],"summary":{"en":"In Aurora the instances are separate from the storage: the writer sends only redo logs to the storage layer, which builds the pages, and each reader has its own buffer pool over the same storage, updating cached pages from the redo it receives. One Daangn service ran an Aurora MySQL 3.10.x cluster with one writer and three readers, and one table had more than 20 indexes. To cut write cost and planning cost, the team picked six indexes whose count_read in performance_schema.table_io_waits_summary_by_index_usage had not grown on any of the four nodes over two weekly snapshots, and planned to make them INVISIBLE, watch for a few days and then drop them. Each INVISIBLE statement made all three readers mark almost every cached page of the table as stale, so the next reads went to storage and reader CPU and ReadIOPS jumped while the buffer pool refilled.\n\nThe IS_STALE column, added in MySQL 8.0.24, shows pages whose tablespace version no longer matches; community MySQL uses this since 8.0.23 to discard pages of dropped or truncated tablespaces lazily. The team sent the seven-case reproduction to AWS technical support. The Aurora MySQL service team confirmed that index DDL triggered the invalidation of the table's buffer pool pages on readers, that this is unnecessary because index operations are metadata-only and change neither the tablespace nor data pages, and that the behaviour came in with Aurora MySQL 3.08.0 as part of a feature improvement and was applied more widely than intended to DDL such as INDEX INVISIBLE, CREATE INDEX and DROP INDEX. AWS identified it as an engine defect, planned a fix that makes readers refresh only the table definition cache for metadata-only DDL, and recommended running index DDL in low-traffic hours until then.\n\nAWS later said the fix would be in Aurora MySQL 3.14. Versions from 3.08.0 up to before 3.14 are affected, and there is no plan to backport the fix to earlier versions. Because Daangn runs the LTS version 3.10.x and cannot move to 3.14 right away, the team is waiting for the next LTS release that contains the fix.","ko":"당근의 한 서비스는 Writer 1대와 Reader 3대로 이루어진 Aurora MySQL 3.10.x 클러스터를 쓴다. 이 클러스터의 한 테이블에는 인덱스가 20개 넘게 달려 있었다. DB팀은 performance_schema.table_io_waits_summary_by_index_usage 의 스냅샷을 일주일 간격으로 두 번 떠서, 4개 노드 모두에서 2주 동안 읽기가 0 이었던 인덱스 6개를 골랐다. 팀은 바로 DROP 하지 않고 먼저 INVISIBLE 로 바꿔 며칠 지켜볼 생각이었다. INVISIBLE 은 메타데이터만 바꾸는 작업이라 DB 에 부하가 없다고 여겼다.\n\n인덱스를 INVISIBLE 로 바꿀 때마다 Reader 3대에서 CPU 와 ReadIOPS 가 함께 튀었고 Writer 는 조용했다. INNODB_BUFFER_PAGE 를 보니 Reader 버퍼풀의 페이지 수는 그대로인데 IS_STALE 이 YES 인 페이지가 테이블 전체에서 94~98% 로 늘었다. Aurora 의 Reader 는 Writer 와 같은 스토리지를 쓰지만 버퍼풀은 따로 가지며, Writer 의 redo 로그로 캐시 페이지를 갱신한다. 인덱스 DDL 은 Reader 에서 그 테이블의 캐시 페이지를 통째로 무효화했고, 그 뒤의 읽기는 스토리지에서 다시 가져와야 했다. DDL 종류별로 재현해 보니 INSTANT DDL 과 rebuild 없는 INPLACE DDL 은 영향이 없었다. tablespace 가 바뀌는 DDL 은 예상대로 캐시가 사라졌다. tablespace 가 바뀌지 않는 CREATE INDEX, DROP INDEX, RENAME INDEX 와 INVISIBLE 은 예상과 달리 테이블 전체를 무효화했다.\n\nDB팀은 재현 결과를 붙여 AWS 기술 지원에 문의했다. Aurora MySQL 서비스팀은 인덱스 DDL 이 Reader 의 버퍼풀을 무효화한 것이 맞다고 확인했다. 원인은 3.08.0 에서 기능 개선의 하나로 들어온 동작이 metadata-only DDL 에까지 의도와 달리 너무 넓게 적용된 엔진 결함이었다. 수정은 Aurora MySQL 3.14 에 들어가며, 그 이전 버전으로 백포트할 계획은 없다. 결함이 있는 버전은 3.08.0 부터 3.14 직전까지다. DB팀은 LTS 버전인 3.10.x 를 쓰고 있어 3.14 로 바로 올리기 어렵다. 그래서 수정이 들어간 다음 LTS 버전을 기다리고 있다."},"trigger":{"en":"The DB team ran ALTER TABLE store_reviews ALTER INDEX ... INVISIBLE for unused indexes, one at a time, as the first step before dropping them.","ko":"DB팀이 미사용 인덱스를 정리하려고 Aurora MySQL 3.10.x 클러스터의 Writer 에서 인덱스를 하나씩 시간 간격을 두고 ALTER INDEX ... INVISIBLE 로 바꿨다."},"severity":"unknown","symptoms":["cache-hit-ratio-drop"],"timeline":{"detected_after":null,"resolved_after":null},"date_kind":"published","detection":{"en":"CloudWatch showed reader CPU and ReadIOPS spikes that lined up one to one with the three ALTER INDEX INVISIBLE statements.","ko":"CloudWatch 에서 ALTER INDEX INVISIBLE 을 실행한 세 번의 시각에 Reader 의 CPU 와 ReadIOPS 스파이크가 하나씩 정확히 겹치는 것을 보고 알아챘다."},"components":["database","schema-migration"],"root_cause":"software-bug","remediation":[],"action_items":[{"text":{"en":"Upgrade to the next Aurora MySQL LTS release that contains the fix","ko":"Aurora MySQL 3.14 로 바로 올리기는 어려우므로, 이 수정이 들어간 다음 LTS 버전이 나오면 그 버전으로 업그레이드한다."}},{"text":{"en":"Until then, run index DDL in low-traffic hours","ko":"그 전까지 인덱스 DDL 은 트래픽이 적은 시간대에 실행한다."}},{"text":{"en":"Combine changes to several indexes into one ALTER statement so the reader buffer pools are invalidated once instead of N times","ko":"인덱스 여러 개를 바꿀 때는 한 ALTER 문으로 묶어 Reader 버퍼풀 무효화를 N 번에서 1 번으로 줄인다."}}],"contributing":["assumption-unverified"],"recurrence_of":null,"symptoms_text":{"en":"Daangn's DB team set unused indexes of one table to INVISIBLE, one at a time, on an Aurora MySQL 3.10.x cluster with one writer and three readers.\n\nIn CloudWatch, each of the three ALTER INDEX ... INVISIBLE statements matched one spike on all three readers at once: ReadIOPS rose from a few hundred to 3.1K, CPU from 30% to 50-60%, and BufferCacheHitRatio dropped for a moment. On the writer, ReadIOPS, CPU and BufferCacheHitRatio stayed as usual.\n\nThe statements were of the form ALTER TABLE store_reviews ALTER INDEX idx_store_id_score_created_at INVISIBLE. The team had expected no load, because making an index invisible only changes the data dictionary and leaves data and index pages untouched, so a reader's cached pages of the table should stay valid; only DDL that rebuilds the table into a new tablespace should empty them.\n\nThe falling hit ratio and rising ReadIOPS suggested that pages were disappearing from the reader buffer pool. Counting the table's pages per index in information_schema.INNODB_BUFFER_PAGE on a reader just before and just after the DDL showed no change (511866 pages before, 511847 after, about 7998 MB). The IS_STALE column of the same view did show it: after ALTER TABLE store_reviews ALTER INDEX idx_user_id_created_at_visibility INVISIBLE, 245625 of 252656 PRIMARY pages (97.2%), 98.2% of the changed index and 94.5% of another index were stale, against 0 before. The writer had no stale pages. The invalidation covered the whole table, data pages and every index, not only the index that changed.\n\nReproducing seven kinds of DDL on a test table order_logs showed the same pattern. ADD COLUMN with ALGORITHM=INSTANT and ALTER COLUMN ... SET DEFAULT with ALGORITHM=INPLACE left the reader buffer pool untouched. MODIFY COLUMN ... NOT NULL (INPLACE with rebuild) and a column type change with ALGORITHM=COPY moved the table to a new tablespace (98989 to 98990, 98423 to 98424), which empties the cache as expected. CREATE INDEX, DROP INDEX and RENAME INDEX kept the same tablespace but made 99.8% to 100% of the table's cached pages on the reader stale.","ko":"미사용 인덱스를 INVISIBLE 로 바꾸는 ALTER 문을 실행할 때마다 Aurora MySQL 클러스터의 Reader 3대에서 CPU 와 ReadIOPS 가 동시에 튀었다. 같은 시각 Writer 의 지표는 평소와 같았다.\n\nCloudWatch 에서 보니 ALTER INDEX INVISIBLE 을 실행한 세 번과 Reader 스파이크 세 번이 정확히 하나씩 맞물렸다. ReadIOPS 는 평소 수백에서 3.1K 로, CPU 는 30% 에서 50~60% 로 올라갔고 BufferCacheHitRatio 는 순간적으로 떨어졌다. Writer 는 ReadIOPS, CPU, BufferCacheHitRatio 모두 변화가 없었다.\n\nDB팀은 버퍼풀의 데이터 페이지가 한순간에 빠져나가 스토리지에서 다시 읽어 오고 있다고 의심했다. 그래서 DDL 직전과 직후에 Reader 에서 information_schema.INNODB_BUFFER_PAGE 를 조회해 인덱스별 캐시 페이지 수를 비교했다. 그러나 그 테이블의 페이지 수는 511,866 에서 511,847 로 거의 줄지 않았다.\n\n같은 뷰의 IS_STALE 컬럼을 보자 차이가 드러났다. 인덱스 하나를 INVISIBLE 로 바꾼 직후 Reader 에서 PRIMARY 인덱스의 252,656 페이지 가운데 245,625 페이지, 곧 97.2% 가 stale 이 됐다. INVISIBLE 로 바꾸지 않은 다른 인덱스도 94~98% 가 stale 이 됐다. 페이지는 버퍼풀에 남아 있었지만 더 이상 유효하지 않은 상태였다. 무효화는 인덱스 하나가 아니라 테이블 전체에 걸쳤다. 같은 시각 Writer 의 stale 페이지는 0개였다.\n\n테스트 테이블에서 DDL 일곱 가지를 실행해 보니 ADD COLUMN 같은 INSTANT DDL 과 rebuild 없는 INPLACE DDL 은 Reader 버퍼풀에 영향이 없었다. COPY DDL 과 rebuild 가 따르는 INPLACE DDL 은 tablespace 가 바뀌므로 캐시가 사라졌는데, 이는 예상한 대로였다. tablespace 가 그대로인 CREATE INDEX, DROP INDEX, RENAME INDEX 는 INVISIBLE 과 똑같이 그 테이블의 캐시 페이지를 거의 전부 stale 로 만들었다."},"updated_at":"2026-11-14T12:34:09.973655Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0451","org_slug":"daangn"}