JDBC 와 MySQL 임시 표 공간 에 대한 심도 있 는 분석

배경
임시 표 공간 은 데이터베이스 정렬 작업 관리 및 임시 표,중간 정렬 결과 저장 등 임시 대상 에 사 용 됩 니 다.여러분 들 이 개발 과정 에서 관련 수 요 를 자주 만 날 것 이 라 고 믿 습 니 다.다음은 JDBC 와 MySQL 임시 표 공간의 관련 내용 을 상세 하 게 공유 하여 여러분 들 이 참고 학습 을 할 수 있 도록 하 겠 습 니 다.다음은 더 이상 말씀 드 리 지 않 겠 습 니 다.상세 한 소 개 를 해 보 겠 습 니 다.
JDBC 연결 매개 변 수 를 사용 하여useCursorFetch=true검색 결과 집합 을 my sqld 임시 표 공간 에 저장 하여 ibtmp 1 파일 크기 가 90 여 G 로 폭 증 하여 서버 디스크 공간 을 소모 합 니 다.임시 테이블 공간의 크기 를 제한 하기 위해 설정:

innodb_temp_data_file_path = ibtmp1:12M:autoextend:max:2G
문제 설명
임시 테이블 공간 이 제 한 된 후에 도 응용 프로그램 이 이전 방식 으로 접근 할 때 ibtmp 1 파일 이 2G 에 이 르 렀 을 때 프로그램 은 시간 이 초과 되 어 연결 이 끊 길 때 까지 기 다 렸 다.SHOW PROCESSLIST 디 스 플레이 프로그램의 연결 스 레 드 는 sleep 상태 이 고 state 와 info 정 보 는 비어 있 습 니 다.이것 은 응용 개발 에 있어 서 그다지 우호 적 이지 않 고 프로그램 이 시간 을 초과 한 후에 원인 을 분석 하려 면 제시 정보 가 부족 하 다.
문제 분석 과정
문 제 를 분석 하기 위해 서 우 리 는 다음 과 같은 테스트 를 진행 하 였 다.
테스트 환경:
mysql:5.7.16
java:1.8u162
jdbc 구동:5.1.36
OS:Red Hat 6.4
1.수 동 시 뮬 레이 션 임시 표 가 최대 제한 을 초과 한 장면
다음 환경 시 뮬 레이 션:
ibtmp1:12M:autoextend:max:30M
500 만 줄 의 sbtest 표 의 k 필드 색인 을 삭제 합 니 다.
group by 조 회 를 실행 하면 임시 표 크기 가 제한 을 초과 하면 오류 가 발생 합 니 다.
select sum(k) from sbtest1 group by k;
ERROR 1114 (HY000): The table '/tmp/#sql_60f1_0' is full
2.mysql 에 대한 드라이버 설정 확인
우 리 는 지난 단계 에 sql 수 동 으로 실행 하면 오 류 를 되 돌려 줍 니 다.그러나 jdbc 가 오 류 를 되 돌려 주지 않 아 연결 이 계속 sleep 되 었 습 니 다.my sql 구동 이 특수 설정 을 한 것 으로 의심 되 고 구동 연결 my sql 을 통 해 general로그 에서 어떤 설정 을 했 는 지 확인 합 니 다.특수 설정 이 발견 되 지 않 았 습 니 다.
3.JDBC 연결 테스트
문제 의 배경 에는 JDBC 에 대한 특별한 설정 이 있 습 니 다:useCursor Fetch=true,숨겨 진 오류 와 관련 이 있 는 지 모 르 겠 습 니 다.다음 테스트 를 진행 하 겠 습 니 다.

다음 현상 발견:
・매개 변수 useCursor Fetch=true 를 추가 할 때 같은 조 회 를 하 는 것 은 틀 리 지 않 습 니 다.
이 매개 변 수 는 결과 집합 이 너무 크 지 않도록 세그먼트 로 읽 는 방식 입 니 다.즉,프로그램 이 다음 sql 을 my sql 에 보 낸 후에 my sql 이 결 과 를 읽 을 수 있 는 피드백 을 기다 릴 것 입 니 다.my sql 이 sql 을 실행 할 때 결 과 를 ibtmp 상한 선 에 도달 한 후에 오 류 를 보고 하지만 이 스 레 드 를 닫 지 않 았 습 니 다.이 스 레 드 는 sleep 상 태 를 처리 하고 프로그램 이 피드백 을 받 지 못 하면 계속 기다 리 고 오류 가 발생 하지 않 습 니 다.kill 라 는 스 레 드 가 있 으 면 프로그램 이 잘못 보 고 됩 니 다.
・매개 변수 useCursorFetch=true 를 추가 하지 않 으 면 같은 조 회 를 하면 오류 가 발생 합 니 다.

결론.
1.정상 적 인 상황 에서 sql 실행 과정 에서 임시 표 크기 가 ibtmp 상한 선 에 도달 하면 오류 가 발생 합 니 다.
2.JDBC 설정useCursorFetch=true,sql 실행 과정 에서 임시 표 크기 가 ibtmp 상한 선 에 도달 하면 오류 가 발생 하지 않 습 니 다.
해결 방안
더 나 아가 사용useCursorFetch=true은 조회 결과 가 너무 많이 수집 되 어 jvm 이 폭발 하 는 것 을 방지 하기 위해 서 라 는 것 을 알 게 되 었 다.
그러나 사용useCursorFetch=true 은 일반 조회 도 임시 표를 생 성하 여 임시 표 공간 이 너무 큰 문 제 를 초래 할 수 있다.
임시 표 공간 이 너무 큰 해결 방안 은 ibtmp 1 의 크기 를 제한 하 는 것 이지 만useCursorFetch=true JDBC 가 오 류 를 되 돌려 주지 않 습 니 다.
따라서 다른 방법 을 사용 하여 같은 효 과 를 거 둘 필요 가 있 으 며,sql 오류 보고 후 절차 도 상응 하 게 오 류 를 보고 해 야 한다.useCursor Fetch=true 라 는 단락 을 읽 는 방식 외 에 스 트림 으로 읽 을 수 있 습 니 다.스 트림 읽 기 프로그램 은 첨부 파일 부분 을 참조 합 니 다.
오보 대비
/세그먼트 읽 기 방식,sql 오류 보고 후 프로그램 오류 신고 하지 않 음
/흐름 읽 기 방식,sql 오류 보고 후 프로그램 오류 보고
메모리 사용량 대비
일반 읽 기,세그먼트 읽 기,스 트림 읽 기 세 가지 방식 을 비교 하 였 으 며,초기 메모리 사용량 은 28M 정도 입 니 다.
・일반 읽 기 후 메모리 사용량 100 M 이상
■세그먼트 읽 기 후 메모리 사용량 60M 정도
・스 트림 읽 기 후 메모리 사용량 60M 정도
지식 을 보충 하 다
MySQL 임시 테이블 공간 지식 공유
MySQL 5.7 은 temporary tablespace 에서 개선 되 었 으 며,temporary tablespace 를 ibdata(공유 표 공간 파일)에서 분리 하 는 것 이 실현 되 었 습 니 다.또한 이전 ibdata 와 같이 너무 큰 문제 가 발생 하지 않도록 크기 초기 화 를 다시 시작 할 수 있 습 니 다.
그 매개 변 수 는:innodbtemp_data_file_path
1.표현
MySQL 이 시 작 될 때 datadir 에서 ibtmp 1 파일 을 만 듭 니 다.초기 크기 는 12M 이 고 기본 값 에 서 는 무한 확장 합 니 다.
일반적으로 조회 로 인 한 임시 표(예 를 들 어 group by)가 tmp 를 초과 하면table_size、max_heap_table_size 크기 제한 은 innodb 디스크 임시 테이블(MySQL 5.7 기본 임시 테이블 엔진 은 innodb)을 만 들 고 공유 임시 테이블 공간 에 저장 합 니 다.
만약 어떤 조작 이 100 M 크기 의 임시 표를 만 들 었 다 면 임시 표 공간 데이터 파일 은 임시 표 의 수 요 를 만족 시 키 기 위해 100 M 크기 로 확 장 됩 니 다.임시 표를 삭제 할 때 방출 된 공간 은 새 임시 표 에 다시 사용 할 수 있 지만 ibtmp 1 파일 은 확장 크기 를 유지 합 니 다.
2.보기 조회
공유 임시 테이블 공간의 사용 상황 을 조회 할 수 있 습 니 다:

SELECT FILE_NAME, TABLESPACE_NAME, ENGINE, INITIAL_SIZE, TOTAL_EXTENTS*EXTENT_SIZE AS TotalSizeBytes, DATA_FREE,MAXIMUM_SIZE FROM INFORMATION_SCHEMA.FILES WHERE TABLESPACE_NAME = 'innodb_temporary'\G
*************************** 1. row ***************************
    FILE_NAME: /data/mysql5722/data/ibtmp1
TABLESPACE_NAME: innodb_temporary
      ENGINE: InnoDB
    INITIAL_SIZE: 12582912
   TotalSizeBytes: 31457280
    DATA_FREE: 27262976
  MAXIMUM_SIZE: 31457280
1 row in set (0.00 sec)
3.회수 방식
MySQL 을 다시 시작 해 야 회수 할 수 있 습 니 다.
4.크기 제한
임시 데이터 파일 이 너무 커지 는 것 을 방지 하기 위해 서 이 innodb 를 설정 할 수 있 습 니 다.temp_data_file_path(다시 시작 해 야 합 니 다)옵션 은 최대 파일 크기 를 지정 합 니 다.데이터 파일 이 최대 시간 에 이 르 면 검색 은 오 류 를 되 돌려 줍 니 다.
innodb_temp_data_file_path=ibtmp1:12M:autoextend:max:2G
5.임시 테이블 공간 과 tmpdir 비교
임시 테이블 공간 을 공유 하면 비 압축 InnoDB 임시 테이블(non-compressed InnoDB 임시 테이블),관계 대상(related objects),스크롤 백 세그먼트(rollback segment)등 데 이 터 를 저장 하 는 데 사 용 됩 니 다.
tmpdir 는 지정 한 임시 파일(temporary files)과 임시 테이블(tempory tables)을 저장 하 는 데 사 용 됩 니 다.공유 임시 테이블 공간 과 달리 tmpdir 는 compressed InnoDB tempory tables 를 저장 합 니 다.
다음 문장 테스트 를 통과 할 수 있 습 니 다:

CREATE TEMPORARY TABLE compress_table (id int, name char(255)) ROW_FORMAT=COMPRESSED;
CREATE TEMPORARY TABLE uncompress_table (id int, name char(255)) ;
첨부 파일
SimpleExample.java

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
public class SimpleExample {
 public static void main(String[] args) throws Exception {
  Class.forName("com.mysql.jdbc.Driver");
  Properties props = new Properties();
  props.setProperty("user", "root");
  props.setProperty("password", "root");
  SimpleExample engine = new SimpleExample();
//  engine.execute(props,"jdbc:mysql://10.186.24.31:3336/hucq?useSSL=false");
  engine.execute(props,"jdbc:mysql://10.186.24.31:3336/hucq?useSSL=false&useCursorFetch=true");
 }
 final AtomicLong tmAl = new AtomicLong();
 final String tableName="test";
 public void execute(Properties props,String url) {
  CountDownLatch cdl = new CountDownLatch(1);
  long start = System.currentTimeMillis();
  for (int i = 0; i < 1; i++) {
   TestThread insertThread = new TestThread(props,cdl, url);
   Thread t = new Thread(insertThread);
   t.start();
   System.out.println("Test start");
  }
  try {
   cdl.await();
   long end = System.currentTimeMillis();
   System.out.println("Test end,total cost:" + (end-start) + "ms");
  } catch (Exception e) {
  }
 }
 
 class TestThread implements Runnable {
  Properties props;
  private CountDownLatch countDownLatch;
  String url;
  public TestThread(Properties props,CountDownLatch cdl,String url) {
   this.props = props;
   this.countDownLatch = cdl;
   this.url = url;
  }
  public void run() {
   Connection connection = null;
   PreparedStatement ps = null;
   Statement st = null;
   long start = System.currentTimeMillis();
   try {
    connection = DriverManager.getConnection(url,props);
    connection.setAutoCommit(false);
    st = connection.createStatement();
     
    //st.setFetchSize(500);
    st.setFetchSize(Integer.MIN_VALUE); //       
     
    ResultSet rstmp;
     
    st.executeQuery("select sum(k) from sbtest1 group by k");
    rstmp = st.getResultSet();
    while(rstmp.next()){
      
    }
   } catch (Exception e) {
    System.out.println(System.currentTimeMillis() - start);
    System.out.println(new java.util.Date().toString());
    e.printStackTrace();
   } finally {
    if (ps != null)
     try {
      ps.close();
     } catch (SQLException e1) {
      e1.printStackTrace();
     }
    if (connection != null)
     try {
      connection.close();
     } catch (SQLException e1) {
      e1.printStackTrace();
     }
    this.countDownLatch.countDown();
   }
  }
 }
}
총결산
이상 은 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 참고 학습 가치 가 있 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.

좋은 웹페이지 즐겨찾기