MySQL 시트 삭제 시 I/O 오류 원인 분석 및 해결

문제 현상
최근 sysbench 를 사용 하여 MySQL 을 테스트 합 니 다.테스트 시간 이 길 어서 prepare->run->cleanup 순 으로 백 스테이지 에서 스 크 립 트 를 썼 습 니 다.달 린 후 로 그 를 살 펴 보 니 MySQL 서비스의 오류 로그 에 다음 과 같은 정보 가 여러 개 있 습 니 다.
[ERROR] InnoDB: Trying to do I/O to a tablespace which does not exist. I/O type: read, page: [page id: space=32, page number=57890], I/O length: 16384 bytes。
I/O 에 오류 가 발생 한 것 처럼 보이 지만 MySQL 프로 세 스 가 무 너 지지 않 았 고 sysbench 클 라 이언 트 도 잘못 보고 하지 않 았 습 니 다.
문제 발견 과정
잘못된 시간 기록 과 스 크 립 트 출력의 각 단계 의 시간 점 을 비교 하여 당시 스 크 립 트 가 실행 중인 명령 을 다음 과 같이 확정 하 였 습 니 다.
sysbench --tables=100 --table-size=4000000 --threads=50 --mysql-db=sbtest --time=300 oltp_delete cleanup
이 용례 를 다시 한 번 수 동 으로 실행 하 였 으 나,다 시 는 같은 상황 이 발생 하지 않 았 다.그러나 스 크 립 트 로 실행 하면 이 오류 정 보 를 발견 할 수 있 습 니 다.run 과 cleanup 사이 에 너무 오래 간격 을 두 지 않 아야 이 문 제 를 일 으 킬 수 있다 는 것 이 초보 적 으로 의심 된다.100 G 데 이 터 를 한 번 에 실행 하 는 시간 이 길 고 재현 대가 가 크기 때문에 먼저 용례 데 이 터 량 을 줄 이려 고 시도 한다.-table-size=4000000 을 2000000 으로 수정 합 니 다.이때 스 크 립 트 를 실행 하면 이 문 제 를 촉발 하지 않 습 니 다.마지막 으로-table-size=300000 을 안정 적 으로 촉발 할 수 있 고 일부 재현 시간 을 줄 일 수 있 습 니 다.너무 긴 간격 으로 재현 할 수 없 는 지 확인 하기 위해 서 스 크 립 트 를 수정 하 는 것 은 run 과 cleanup 두 단계 사이 에 sleep 10 초 입 니 다.과연 이 오류 메 시 지 를 촉발 하지 않 습 니 다.sleep 5 초 로 수정 하면 촉발 할 수 있 지만,오 답 은 이미 감소 하 였 습 니 다.
문제 조사
대응 하 는 버 전 my sql 5.7.22 의 코드 를 살 펴 보 니 이 오 류 는 한 위치 만 있 습 니 다:fil0 fil.cc 파일 의 5578 줄 filio()함수 내.gdb 디 버 깅 을 직접 사용 하여 이 위치 에 정지점 을 추가 하고 재현 가능 한 스 크 립 트 를 실행 하여 다음 스 택 을 얻 습 니 다.

(gdb) bt
#0 fil_io (type=..., sync=sync@entry=false, page_id=..., page_size=..., byte_offset=byte_offset@entry=0, len=16384, buf=0x7f9ead544000, message=message@entry=0x7f9ea8ce9c78) at mysql-5.7.22/storage/innobase/fil/fil0fil.cc:5580
#1 0x00000000010f99fa in buf_read_page_low (err=0x7f9ddaffc72c, sync=<optimized out>, type=0, mode=<optimized out>, page_id=..., page_size=..., unzip=true) at mysql-5.7.22/storage/innobase/buf/buf0rea.cc:195
#2 0x00000000010fc5fa in buf_read_ibuf_merge_pages (sync=sync@entry=false, space_ids=space_ids@entry=0x7f9ddaffc7e0, page_nos=page_nos@entry=0x7f9ddaffc7a0, n_stored=2) at mysql-5.7.22/storage/innobase/buf/buf0rea.cc:834
#3 0x0000000000f3a86c in ibuf_merge_pages (n_pages=n_pages@entry=0x7f9ddaffce30, sync=sync@entry=false) at mysql-5.7.22/storage/innobase/ibuf/ibuf0ibuf.cc:2552
#4 0x0000000000f3a94a in ibuf_merge (sync=false, sync=false, n_pages=0x7f9ddaffce30) at mysql-5.7.22/storage/innobase/ibuf/ibuf0ibuf.cc:2656
#5 ibuf_merge_in_background (full=full@entry=false) at mysql-5.7.22/storage/innobase/ibuf/ibuf0ibuf.cc:2721
#6 0x000000000102bcf4 in srv_master_do_active_tasks () at mysql-5.7.22/storage/innobase/srv/srv0srv.cc:2132
#7 srv_master_thread (arg=<optimized out>) at mysql-5.7.22/storage/innobase/srv/srv0srv.cc:2383
#8 0x00007fa003eeddc5 in start_thread () from /lib64/libpthread.so.0
#9 0x00007fa002aab74d in clone () from /lib64/libc.so.6
배경 스 레 드 가 insert buffer merge 작업 을 하고 있 는 것 이 분명 합 니 다.이때 space->stop 발견new_ops 는 true,즉 처리 할 페이지 에 속 하 는 space 가 삭제 되 고 있 습 니 다.왜 삭제 되 고 있 는 space 를 조작 합 니까?이것 은 insert buffer 기능,insert buffer merge 의 절차 와 표를 삭제 하 는 절 차 를 조사해 야 합 니 다.
insert 버퍼 배경 지식
insert buffer 는 특수 한 데이터 구조(B+tree)입 니 다.보조 색인 페이지 가 버퍼 에 없 을 때 캐 시 를 변경 합 니 다.나중에 페이지 가 다른 읽 기 동작 으로 버퍼 에 불 러 올 때 합 칩 니 다.MySQL 은 처음에 이 기능 을 도 입 했 을 때 insert 작업 만 캐 시 할 수 있 기 때문에 insert buffer 라 고 부 릅 니 다.현재 이 작업 들 은 INSERT,UPDATE,or DELETE(DML)일 수 있 기 때문에 change buffer 라 고 부 릅 니 다.(본 고 는 여전히 insert buffer 설명)원본 코드 에 서 는 ibuf 를 표지 로 합 니 다.이 기능 은 같은 페이지 에 대한 업데이트 캐 시 를 여러 개 합 쳐 한꺼번에 업데이트 작업 으로 합 쳐 IO 를 줄 이 고 랜 덤 IO 를 순서 IO 로 전환 시 켜 랜 덤 IO 가 성능 손실 을 가 져 오지 않도록 데이터 베이스 의 쓰기 성능 을 향상 시 킬 수 있다.
관련 insert 버퍼 병합 논리
buffer page 가 buffer pool 을 읽 을 때 insert buffer merge 가 진 행 됩 니 다.주로 몇 장면 에서 merge 과정 이 나타 납 니 다.
4.567917.페이지 가 버퍼 풀 에 읽 혔 을 때 읽 기 완료 후 ibuf 의 merge 를 먼저 한 다음 에 페이지 를 사용 할 수 있 습 니 다4.567917.merge 작업 은 배경 작업 으로 실 행 됩 니 다.innodb_io_capacity 매개 변 수 는 InnoDB 배경 작업 의 매번 merge 과정의 페이지 수 상한 선 을 설정 할 수 있 습 니 다4.567917.충돌 복구 기간 에 색인 페이지 가 버퍼 풀 에 읽 힐 때 해당 페이지 의 insert buffer merge 를 실행 합 니 다4.567917.insert buffer 는 지속 성 을 가지 고 시스템 붕 괴 는 효력 을 잃 지 않 습 니 다.다시 시작 하면 insert buffer merge 작업 이 정상 으로 회 복 됩 니 다
  • 서버 가 닫 혔 을 때 사용 할 수 있 는 innodb-fast-shutdown=0 은 ibuf 의 완전 합병 을 강제 합 니 다
  • 우리 의 이번 문 제 는 분명히 두 번 째 상황 에 속한다.innodb 메 인 스 레 드(svrmaster_thread)insert buffer 의 merge 작업 을 1 초 간격 으로 주동 적 으로 진행 합 니 다.과거 1s 내 서버 에 이벤트 가 있 었 는 지(페이지 에 원 그룹 삽입,undo 표 의 줄 조작 등)를 먼저 판단 하고,발생 한 적 이 있 으 면 merge 의 최대 페이지 수 는 innodb 입 니 다.io_capacity 설정 의 5%입 니 다.없 으 면 merge 의 최대 페이지 수 는 innodb 입 니 다.io_capacity 설정 값 입 니 다.
    innodb 메 인 스 레 드(svrmaster_thread)merge 의 주 류 는 다음 과 같다.
    4.567917.메 인 스 레 드 는 ibuf 나무의 잎 노드 에서 페이지 번호 와 space 번 호 를 읽 고 이원 배열 에 기록 합 니 다(잠 금 되 지 않 음)4.567917.메 인 스 레 드 는 이원 그룹의 space 에 대해 표 공간 캐 시 에 있 는 지 여 부 를 검사 합 니 다.없 으 면 삭 제 된 것 을 설명 하고 ibuf 에 대응 하 는 기록 을 삭제 합 니 다4.567917.메 인 스 레 드 는 삭제 중인 space 에 대해 비동기 읽 기 작업 을 할 지 여 부 를 판단 합 니 다.만약 에 잘못 보고 하고 ibuf 에 대응 하 는 기록 을 삭제 하고 과정 2 로 넘 어가 다음 배열 요소 의 판단 을 계속 합 니 다
  • 모든 판단 이 정상 적 이면 메 인 스 레 드 는 async io 요청 을 보 내 고 async 는 merge 의 색인 페이지 를 읽 어야 합 니 다
  • I/O handler 스 레 드 는 완 성 된 async I/O 를 받 은 후에 merge 작업 을 합 니 다
  • merge 를 진행 할 때 fil 호출space_acquire 대 space->npending_ops 자체 증가.삭제 작업 의 병행 을 피하 기;
  • 실행 완료 후 fil 호출space_release 대 space->npending_ops 자체 감소 진행.
  • 삭제 표 의 논리
  • 대 fil시스템->mutex 잠 금 추가,sp->stop 설정new_ops=true,태그 space 가 삭제 되 고 있 습 니 다.새 작업 을 할 수 없습니다.그리고 filsystem->mutex 잠 금 해제;
  • 대 filsystem->mutex 잠 금,space->n 검출pending_ops,대 fil시스템->mutex 잠 금 해제.0 이상 이 검출 되면 의존 하 는 조작 이 완료 되 지 않 았 음 을 의미 하 며 20ms 수면 후 다시 시도 합 니 다
  • 대 filsystem->mutex 잠 금,space->n 검출pending_flushes 와(*node)->npending,대 fil시스템->mutex 잠 금 해제.0 이상 이 검출 되면 의존 하 는 I/O 가 완성 되 지 않 았 음 을 의미 하 며 20ms 수면 후 다시 시도 합 니 다
  • 4.567917.이때 충돌 하 는 동작 이 없다 고 생각 하고 모든 더러 운 페이지 를 닦 거나 주어진 표 공간의 페이지 를 삭제 합 니 다
  • 표 공간 캐 시 에서 지정 한 space 의 기록 을 삭제 합 니 다
  • 4.567917.대응 하 는 데이터 파일 을 삭제 합 니 다문제 결론
    상황 이 명확 합 니 다.메 인 스 레 드 가 ibuf(space,page)를 가 져 오 는 과정 과 삭제 작업 이 실 행 된 과정 은 서로 배척 하 는 것 을 보장 하지 않 습 니 다.async I/O 가 완 료 된 후에 만 merge 작업 과 삭제 작업 이 서로 배척 할 수 있 습 니 다.배경 스 레 드 가 ibuf merge 를 시작 하고 두 번 째 검 측 을 실 행 했 지만 세 번 째 검 측 이 실행 되 지 않 았 다 면 사용자 스 레 드 는 표 삭제 작업 을 시작 하고 stop 을 설정 합 니 다.new_ops 표 시 는 5 단계 까지 실행 되 지 않 았 지만 표 공간 캐 시 를 삭제 하면 오류 가 발생 합 니 다.두 스 레 드 의 상호작용 은 다음 그림 과 같다.

    의외 의 사고 가 발생 하지 않 으 면 중단 점 을 칠 때 반드시 스 레 드 가 해당 표 의 삭제 작업 을 수행 할 것 입 니 다.과연 우 리 는 아래 의 창 고 를 발견 할 수 있다.
    
    Thread 118 (Thread 0x7f9de0111700 (LWP 5234)):
    #0 0x00007fa003ef1e8e in pthread_cond_broadcast@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
    #1 0x0000000000f82f41 in broadcast (this=0xd452ef8) at mysql-5.7.22/storage/innobase/os/os0event.cc:184
    #2 set (this=0xd452ef8) at mysql-5.7.22/storage/innobase/os/os0event.cc:75
    #3 os_event_set (event=0xd452ef8) at mysql-5.7.22/storage/innobase/os/os0event.cc:483
    #4 0x00000000010ec8a4 in signal (this=<optimized out>) at mysql-5.7.22/storage/innobase/include/ut0mutex.ic:105
    #5 exit (this=<optimized out>) at mysql-5.7.22/storage/innobase/include/ib0mutex.h:690
    #6 exit (this=<optimized out>) at mysql-5.7.22/storage/innobase/include/ib0mutex.h:961
    #7 buf_flush_yield (bpage=<optimized out>, buf_pool=<optimized out>) at mysql-5.7.22/storage/innobase/buf/buf0lru.cc:405
    #8 buf_flush_try_yield (processed=<optimized out>, bpage=<optimized out>, buf_pool=<optimized out>) at mysql-5.7.22/storage/innobase/buf/buf0lru.cc:449
    #9 buf_flush_or_remove_pages (trx=<optimized out>, flush=<optimized out>, observer=<optimized out>, id=<optimized out>, buf_pool=<optimized out>) at mysql-5.7.22/storage/innobase/buf/buf0lru.cc:632
    #10 buf_flush_dirty_pages (buf_pool=<optimized out>, id=<optimized out>, observer=<optimized out>, flush=<optimized out>, trx=<optimized out>) at mysql-5.7.22/storage/innobase/buf/buf0lru.cc:693
    #11 0x00000000010f6de7 in buf_LRU_remove_pages (trx=0x0, buf_remove=BUF_REMOVE_FLUSH_NO_WRITE, id=55, buf_pool=0x31e55e8) at mysql-5.7.22/storage/innobase/buf/buf0lru.cc:893
    #12 buf_LRU_flush_or_remove_pages (id=id@entry=55, buf_remove=buf_remove@entry=BUF_REMOVE_FLUSH_NO_WRITE, trx=trx@entry=0x0) at mysql-5.7.22/storage/innobase/buf/buf0lru.cc:951
    #13 0x000000000114e488 in fil_delete_tablespace (id=id@entry=55, buf_remove=buf_remove@entry=BUF_REMOVE_FLUSH_NO_WRITE) at mysql-5.7.22/storage/innobase/fil/fil0fil.cc:2800
    #14 0x0000000000fe77bd in row_drop_single_table_tablespace (trx=0x0, is_encrypted=false, is_temp=false, filepath=0x7f9d7c209f38 "./sbtest/sbtest25.ibd", tablename=0x7f9d7c209dc8 "sbtest/sbtest25", space_id=55) at mysql-5.7.22/storage/innobase/row/row0mysql.cc:4189
    #15 row_drop_table_for_mysql (name=name@entry=0x7f9de010e020 "sbtest/sbtest25", trx=trx@entry=0x7f9ff9515750, drop_db=<optimized out>, nonatomic=<optimized out>, nonatomic@entry=true, handler=handler@entry=0x0) at mysql-5.7.22/storage/innobase/row/row0mysql.cc:4741
    #16 0x0000000000f092f3 in ha_innobase::delete_table (this=<optimized out>, name=0x7f9de010f5e0 "./sbtest/sbtest25") at mysql-5.7.22/storage/innobase/handler/ha_innodb.cc:12539
    #17 0x0000000000801a30 in ha_delete_table (thd=thd@entry=0x7f9d7c1f6910, table_type=table_type@entry=0x2ebd100, path=path@entry=0x7f9de010f5e0 "./sbtest/sbtest25", db=db@entry=0x7f9d7c00e560 "sbtest", alias=0x7f9d7c00df98 "sbtest25", generate_warning=generate_warning@entry=true) at mysql-5.7.22/sql/handler.cc:2586
    #18 0x0000000000d0a6af in mysql_rm_table_no_locks (thd=thd@entry=0x7f9d7c1f6910, tables=tables@entry=0x7f9d7c00dfe0, if_exists=true, drop_temporary=false, drop_view=drop_view@entry=false, dont_log_query=dont_log_query@entry=false) at mysql-5.7.22/sql/sql_table.cc:2546
    #19 0x0000000000d0ba58 in mysql_rm_table (thd=thd@entry=0x7f9d7c1f6910, tables=tables@entry=0x7f9d7c00dfe0, if_exists=<optimized out>, drop_temporary=<optimized out>) at mysql-5.7.22/sql/sql_table.cc:2196
    #20 0x0000000000c9d90b in mysql_execute_command (thd=thd@entry=0x7f9d7c1f6910, first_level=first_level@entry=true) at mysql-5.7.22/sql/sql_parse.cc:3589
    #21 0x0000000000ca1edd in mysql_parse (thd=thd@entry=0x7f9d7c1f6910, parser_state=parser_state@entry=0x7f9de01107a0) at mysql-5.7.22/sql/sql_parse.cc:5582
    #22 0x0000000000ca2a20 in dispatch_command (thd=thd@entry=0x7f9d7c1f6910, com_data=com_data@entry=0x7f9de0110e00, command=COM_QUERY) at mysql-5.7.22/sql/sql_parse.cc:1458
    #23 0x0000000000ca4377 in do_command (thd=thd@entry=0x7f9d7c1f6910) at mysql-5.7.22/sql/sql_parse.cc:999
    #24 0x0000000000d5ed00 in handle_connection (arg=arg@entry=0x10b8e910) at mysql-5.7.22/sql/conn_handler/connection_handler_per_thread.cc:300
    #25 0x0000000001223d74 in pfs_spawn_thread (arg=0x10c48f40) at mysql-5.7.22/storage/perfschema/pfs.cc:2190
    #26 0x00007fa003eeddc5 in start_thread () from /lib64/libpthread.so.0
    #27 0x00007fa002aab74d in clone () from /lib64/libc.so.6
    해결 방법
    위 bufread_ibuf_merge_pages、buf_read_page_low、fil_io 매개 변수 ignore 추가missing_space。삭제 중인 space 를 무시 합 니 다.기본 값 은 false 입 니 다.ibufmerge_pages 호출 시 true 로 설정 합 니 다.fil 에서io 오 류 는 이 매개 변수 가 true 인지 아 닌 지 를 추가 로 판단 하고 오 류 를 보고 하지 않 으 며 다른 절 차 를 계속 합 니 다.
    아니면 바로 bufread_ibuf_merge_pages 호출 bufread_page_low 시 IORequest::IGNOREMISSING 인자.
    구체 적 인 코드 는 MariaDB commt:8edbb1117a9e1fd81fbd08b8f1d06c72efe38f 44 참조
    영향 버 전
    관련 정 보 를 살 펴 보면 이 문 제 는 Bug\#19710564 를 수정 할 때 표 공간 버 전 을 삭제 하고 도입 한 것 이다.
  • MySQL Community Server 5.7.6 이 도입 되 었 고 버 전 5.7.22 가 복원 되 지 않 았 으 며 버 전 8.0.0 이 복원 되 었 습 니 다
  • MariaDB Server 10.2 가 영향 을 받 았 다.MariaDB Server 10.2.9,10.3.2 복구 되 었 습 니 다최적화 건의
    성능 최적화 가능:bufread_ibuf_merge_pages 에 잘못된 space id 를 기록 하고 순환 할 때 다음 page 의 space id 를 판단 합 니 다.space id 가 같다 면 ibuf 에 대응 하 는 기록 을 직접 삭제 합 니 다.
    총결산
    이상 은 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 참고 학습 가치 가 있 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.

    좋은 웹페이지 즐겨찾기