이 기사는 mysql에 대한 관련 지식을 제공합니다. MySQL의 스트리밍 쿼리 및 커서 쿼리 방법을 주로 소개하며 좋은 참조 가치가 있으므로 모든 사람에게 도움이 되기를 바랍니다.
추천 학습: mysql 비디오 튜토리얼
이제 비즈니스 시스템은 처리를 위해 MySQL 데이터베이스에서 5백만 개의 데이터 행을 읽어야 합니다
기본적으로 전체 검색 결과 집합은 메모리에 저장됩니다. . 대부분의 경우 이는 가장 효율적인 작동 방법이며 구현하기도 더 쉽습니다.
단일 테이블의 데이터량이 500만개라고 가정할 때, 누구도 한번에 메모리에 로드하지 않으며 일반적으로 페이징을 사용합니다.
여기서 테스트 데모는 JVM을 모니터링하기 위한 것이므로 페이징을 사용하지 않고 데이터가 한 번에 메모리에 로드됩니다.
@Test public void generalQuery() throws Exception { // 1核2G:查询一百条记录:47ms // 1核2G:查询一千条记录:2050 ms // 1核2G:查询一万条记录:26589 ms // 1核2G:查询五万条记录:135966 ms String sql = "select * from wh_b_inventory limit 10000"; ps = conn.prepareStatement(sql); ResultSet rs = ps.executeQuery(sql); int count = 0; while (rs.next()) { count++; } System.out.println(count); }
JVM 모니터링
메모리를 -Xms70m -Xmx70m
으로 조정하겠습니다.전체 쿼리 프로세스에서 힙 메모리 사용량이 점차 증가하고 결국 OOM이 발생합니다.
java.lang.OutOfMemoryError: GC 오버헤드 제한 초과
1. OOM에 숨겨진 위험이 있습니다
.2.2 스트리밍 쿼리
테스트 결과 스트리밍 쿼리로는 쿼리 속도가 향상되지 않았습니다
@Test public void streamQuery() throws Exception { // 1核2G:查询一百条记录:138ms // 1核2G:查询一千条记录:2304 ms // 1核2G:查询一万条记录:26536 ms // 1核2G:查询五万条记录:135931 ms String sql = "select * from wh_b_inventory limit 50000"; statement = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); statement.setFetchSize(Integer.MIN_VALUE); ResultSet rs = statement.executeQuery(sql); int count = 0; while (rs.next()) { count++; } System.out.println(count); }
힙 메모리를 줄였습니다. -Xms70m -Xmx70m
힙 메모리가 70m에 불과하더라도 여전히 OOM
2.3 커서 쿼리
1. 데이터베이스 연결 정보에 매개변수를 연결해야 합니다.
useCursorFetch=true2. 두 번째로 매번 문이 읽는 데이터 수를 설정합니다. , 한 번에 1000개
테스트 결과에 따르면 커서 쿼리가 쿼리 속도를 어느 정도 단축시켰습니다
@Test public void cursorQuery() throws Exception { Class.forName("com.mysql.jdbc.Driver"); // 注意这里需要拼接参数,否则就是普通查询 conn = DriverManager.getConnection("jdbc:mysql://101.34.50.82:3306/mysql-demo?useCursorFetch=true", "root", "123456"); start = System.currentTimeMillis(); // 1核2G:查询一百条记录:52 ms // 1核2G:查询一千条记录:1095 ms // 1核2G:查询一万条记录:17432 ms // 1核2G:查询五万条记录:90244 ms String sql = "select * from wh_b_inventory limit 50000"; ((JDBC4Connection) conn).setUseCursorFetch(true); statement = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); statement.setFetchSize(1000); ResultSet rs = statement.executeQuery(sql); int count = 0; while (rs.next()) { count++; } System.out.println(count); }
힙 메모리를 줄였습니다. -Xms70m -Xmx70m
단일 스레드의 경우 커서 쿼리 스트리밍 쿼리와 마찬가지로 OOM도 매우 잘 피할 수 있으며 커서 쿼리는 쿼리 속도를 최적화할 수 있습니다.3. RowData
3.1 RowDataStatic
기본적으로 ResultSet은 RowDataStatic 객체를 생성할 때 RowDataStatic 인스턴스를 사용합니다. next() 메모리에서 하나씩 읽기3.2 RowDataDynamic스트리밍 처리를 사용할 때 ResultSet은 RowDataDynamic 개체를 사용하며 이 개체는 next()가 호출될 때마다 데이터의 단일 행을 읽도록 IO를 시작합니다3.3 RowDataCursor RowDataCursor 호출은 일괄 처리되며 내부 캐싱이 수행됩니다. 프로세스는 다음과 같습니다.먼저 내부 버퍼에 반환되지 않은 데이터가 있는지 확인합니다.
기본 RowDataStatic은 모든 데이터를 클라이언트 메모리로 읽어 들이고 또한 JVM입니다. RowDataDynamic은 IO 호출당 하나의 데이터를 읽습니다.
RowDataCursor는 한 번에 fetchSize 행을 읽습니다. 소비가 완료된 후 요청 호출을 시작합니다.
4. JDBC 통신 원리
JDBC와 MySQL 서버 간의 상호 작용은 소켓을 통해 완료됩니다. 네트워크 프로그래밍에 따라 MySQL은 SocketServer로 간주될 수 있으므로 전체 요청 링크는 다음과 같아야 합니다.
JDBC 클라이언트 -> MySQL -> 데이터 반환 -> 네트워크 -> JDBC 클라이언트
쿼리 데이터의 양이 너무 많으면 계속 GC가 발생하고 메모리 오버플로가 발생합니다.
4.2 streamQuery 스트리밍 쿼리
4.3cursorQuery Cursor Query
MySQL은 클라이언트가 언제 데이터 소비를 완료했는지 알지 못하므로 해당 테이블에 DML 쓰기 작업이 있을 수 있습니다. 이때 MySQL은 가져가야 할 데이터를 저장할 임시 공간을 만들어야 합니다.
useCursorFetch를 활성화하여 큰 테이블을 읽으면 MySQL에서 몇 가지 현상을 볼 수 있습니다.
1. IOPS가 급증합니다.스트림 쿼리 호출의 메모리 성능 보고서는 다음과 같습니다
동시 호출도 메모리 사용량에 매우 좋으며 중첩 증가도 없습니다
커서 쿼리의 메모리 성능 보고서는 다음과 같습니다
6. 요약
2. 쿼리 속도 측면에서 커서 쿼리는 스트리밍 쿼리보다 빠르며 스트리밍 쿼리는 쿼리 시간을 단축할 수 없습니다. 일반 쿼리 사용
3. 동시 시나리오에서는 스트리밍 쿼리 힙 메모리의 추세가 더 안정적이며 추가 증가가 없습니다.
추천 학습:
mysql 비디오 튜토리얼위 내용은 MySQL의 스트리밍 쿼리 및 커서 쿼리 방법(요약 공유)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!