【GaussDB(for MySQL)】 Big IN 쿼리 최적화

이 기사는 Huawei Cloud 커뮤니티 " [MySQL 기술 칼럼] GaussDB (for MySQL) Big IN Query Optimization ", 저자: GaussDB 데이터베이스에서 공유되었습니다.

20240508-164135(위링크PC).jpg

배경 소개

프로덕션 환경에서는 필터링 및 쿼리를 수행한 후 집계 처리를 수행하는 고객 비즈니스 SQL 문을 자주 접하게 되며 IN 조건자 목록에는 수천 또는 수만 개의 상수 값이 포함됩니다. 아래와 같이 이러한 문장의 실행 시간은 매우 길다.

 

111.PNG

MySQL 최적화

오픈 소스 MySQL이 IN 컬럼(const1, const2, ....)을 처리할 때 해당 컬럼에 인덱스가 있으면 옵티마이저는 Range 스캔을 선택하여 스캔하고, 그렇지 않으면 전체 테이블 스캔을 사용합니다. range_optimizer_max_mem_size 시스템 변수는 범위 최적화 프로세스 분석 중에 사용할 수 있는 최대 메모리를 제어합니다. IN 조건자에 목록 요소가 많으면 각 IN의 내용이 OR로 처리됩니다. OR은 요소가 많으면 더 많은 메모리를 사용합니다. 메모리 사용량이 정의된 최대 메모리를 초과하면 범위 최적화가 실패하고 최적화 프로그램이 전체 테이블 스캔으로 변환하는 등 전략을 변경하여 쿼리 성능이 저하됩니다.

이 최적화 문제의 경우 range_optimizer_max_mem_size를 조정하여 처리할 수 있습니다. range_optimizer_max_mem_size로 정의된 메모리는 세션 수준에 있습니다. 이 유형의 문을 실행하는 각 세션은 동일한 메모리를 차지합니다. 대규모 동시성 시나리오에서는 인스턴스 메모리 사용량이 과도해지고 인스턴스 OOM이 발생할 위험이 있습니다.

범위 쿼리의 경우 MySQL은 동등한 범위 쿼리를 처리할 때 최적화 프로그램이 인덱스 다이빙(index div)을 수행할지 여부를 제어하기 위해 eq_range_index_dive_limit 시스템 변수를 정의합니다. 인덱스 다이빙은 인덱스를 사용하여 튜플 수에 대한 설명을 완성하므로 더 정확한 정보를 얻고 더 나은 쿼리 전략 최적화를 만들 수 있지만 실행 시간도 길어집니다. IN 조합 수가 특정 수를 초과하면 인덱스 다이빙이 적용되지 않습니다. 시스템은 정적 인덱스 통계 정보 값을 사용하여 인덱스를 선택합니다. 이로 인해 MySQL이 인덱스를 제대로 활용하지 못하게 되어 성능이 저하될 수 있습니다.

GaussDB의 Big IN 최적화(MySQL용)

 
GaussDB(MySQL용) Big IN 성능 문제 방법은 Big IN 조건자를 IN 하위 쿼리로 변환합니다. 따라서 IN 술어의 형식은 다음과 같습니다.
IN 열(const1, const2, ....)
해당 IN 하위 쿼리로 변환합니다.
IN 열(SELECT ... FROM temporary_table)
위 변경 후 IN 함수 쿼리는 IN 하위 쿼리가 되고 하위 쿼리는 상관되지 않은 하위 쿼리가 됩니다.
 
IN 비상관 하위 쿼리의 경우 MySQL 최적화 프로그램은 최적화 처리를 위한 semi-join 구체화 전략을 제공합니다. 세미 조인 구체화 전략은 서브 쿼리 결과를 임시 테이블로 구체화한 후 이를 외관으로 조인하는 전략이다. 아래 그림과 같이:
 

1.png

 

연결은 두 가지 순서로 이루어질 수 있습니다.

  • Materialization-scan : 구체화된 테이블부터 외관까지 구체화된 테이블의 전체 테이블 스캔을 나타낸다.
  • Materialization-lookup: 구체화된 테이블의 모양부터 메인 빌더를 사용하여 구체화된 테이블의 데이터를 검색할 수 있음을 나타냅니다.

물리적, 화학적 스캔

  1. 하위 쿼리를 실행하고, auto_distinct_key 인덱스를 사용하고, 동시에 결과를 중복 제거합니다.
  2. 임시 테이블 템플릿 1에 이전 단계의 결과를 저장합니다.
  3. 임시 테이블에서 데이터 행을 가져와서 모양새의 보충 조건을 충족하는 행을 찾습니다.
  4. 임시 테이블의 순회가 완료될 때까지 3단계를 반복합니다.

구체화된 검색

  1. 하위 쿼리를 먼저 실행하십시오.
  2. 이전 단계에서 얻은 결과를 임시 테이블에 저장합니다.
  3. 겉모습에서 한 행의 데이터를 가져와서 구체화된 임시 테이블로 가서 보충 조건을 만족하는 행을 찾고, 구체화된 테이블의 기본 키를 사용하여 한 번에 한 행씩 스캔하는 단계;
  4. 전체 모양을 볼 때까지 3을 반복합니다.

옵티마이저는 내부 모양의 크기에 따라 서로 다른 연결 순서를 선택합니다. 실제 시나리오에서는 일반적으로 쿼리되는 테이블의 데이터 양이 매우 커서 수천만 또는 수억 개에 달하며 IN 목록의 요소 수는 테이블 수보다 훨씬 적으며 최적화 프로그램은 Materialization을 선택합니다. - 검색을 위한 검색 방법. 표시 쿼리 인덱스 중에 기본 키를 사용하는 경우 최적화 후 검색된 총 행 수는 N입니다. M이 N보다 훨씬 클 경우 성능 향상이 매우 분명해집니다.

지침

rds_in_predicate_conversion_threshold 매개변수는 IN 술어 하단의 쿼리 함수를 수정하기 위한 스위치로, SQL 문의 IN 술어 목록에 있는 요소 수가 해당 매개변수 값을 초과하면 최적화 전략이 시작됩니다. 이 변수의 값을 통해 함수가 사용됩니다. 다음은 최적화 사용을 보여주는 간단한 예입니다.

테이블 구조

테이블 t1(id int, a int, key idx1(a))을 생성합니다.
문구를 확인하세요
t1에서 *를 선택합니다. 여기서 a는 (1,2,3,4,5)입니다.

set rds_in_predicate_conversion_threshold = 0으로 설정하고 range_optimizer_max_mem_size=1로 설정하여 대형 IN 조건자 최적화 기능과 범위 스캔 최적화 전략을 해제합니다. 위 쿼리 문의 실행 계획을 확인하면 다음과 같습니다.

> rds_in_predicate_conversion_threshold = 0으로 설정; > range_optimizer_max_mem_size=1로 설정; > (1,2,3,4,5)의 a가 있는 t1에서 *를 선택하는 방법을 설명합니다.  
결과는 다음과 같습니다.
+------+------------+-------+------------+------+-- -------------+------+---------+------+------+----- ------+---------------+ | 아이디 | 선택_유형 | 테이블 | 파티션 | 유형 | 가능한_키 | 키 | key_len | 심판 | 행 | 필터링 | 추가 | +------+------------+-------+------------+------+-- -------------+------+---------+------+------+----- ------+---------------+ | 1 | 단순 | t3 | NULL | 전체 | 키1 | NULL | NULL | NULL | 3 | 50.00 | 어디에 사용 | +------+------------+-------+------------+------+-- -------------+------+---------+------+------+----- ---+-------------+ 세트의 행 1개, 경고 2개(0.00초)  
경고 표시; +---------+------+------ ------------------------------------- ----------------------------+ | 레벨 | 코드 | 메시지 | +---------+------+------ ------------------------------------- ----------------------------+ | 경고 | 3170 | 'range_optimizer_max_mem_size'에 대한 메모리 용량 1바이트를 초과했습니다. 이 쿼리에 대해 범위 최적화가 수행되지 않았습니다. | | 참고 | 1003 | /* select#1 */ `test`.`t3`.`id` AS `id`,`test`.`t3`.`a` AS `a` from `test`.`t3` 여기서 (` test`.`t3`.`a` in (3,4,5)) | +---------+------+------ ------------------------------------- ---------------+ 2줄 세트 (0.00초)

위 명령문 실행 시 경고가 보고된 것으로 확인되었으며, 경고 정보에 따르면 범위 최적화 프로세스 중 사용된 메모리가 range_optimizer_max_mem_size를 초과하여 해당 명령문에 범위 제한 최적화가 사용되지 않은 것으로 나타났습니다. 결과적으로 스캔 유형이 ALL로 변경되어 전체 테이블 스캔이 됩니다.

대규모 IN 조건자 최적화 옵션을 활성화하려면 set rds_in_predicate_conversion_threshold = 3을 설정합니다. 즉, IN 조건자 목록 요소가 3을 초과하면 대규모 IN 대기열 쿼리 최적화 전략이 활성화됩니다. EXPLAIN FORMAT=TREE 문을 실행하여 최적화가 적용되는지 확인하세요.

> rds_in_predicate_conversion_threshold = 3으로 설정; > 형식 설명=트리 선택 * t1에서 (1,2,3,4,5); +------------------------------------------------- ------------------------------------- ------------------------------------- ------------------------------------- ------------------------------------- -------+ | 설명 | +------------------------------------------------- ------------------------------------- ------------------------------------- ------------------------------------- ------------------------------------- -------+ | -> 중첩 루프 내부 조인(비용=0.70행=1) -> 필터: (t1.a가 null이 아님)(비용=0.35행=1) -> t1의 테이블 스캔(비용=0.35행=1) -> <auto_distinct_key>를 사용하여 <in_predicate_2>에서 단일 행 인덱스 조회(a=t1.a)(비용=0.35행=1) | +------------------------------------------------- ------------------------------------- ------------------------------------- ------------------------------------- ------------------------------------- -------+ 1행 세트(0.00초)

실행 계획의 <in_predicate_*>(*는 숫자) 테이블은 IN 술어 목록의 모든 데이터를 저장하는 Big INTool에서 생성된 임시 테이블입니다.

사용 제한

Big IN 최적화에서 지원되는 쿼리 문에는 다음 문 목록이 포함됩니다.

  • 선택하다
  • 삽입...선택
  • 바꾸기...선택
  • 지지하는 관점
  • 준비된 STMT

제약과 한계

Big IN 로터 쿼리는 성능을 달성하기 위해 mysql에서 제공하는 하위 쿼리 최적화 솔루션을 사용하므로 사용 시 다음과 같은 제한 사항이 있으며, 그렇지 않으면 성능이 저하됩니다.

  • 인덱싱을 사용할 수 없는 시나리오는 지원되지 않습니다.
  • 상수 IN LIST(NOW(), ? 및 테이블 쿼리와 관련되지 않은 기타 문 포함)만 지원합니다.
  • 저장 프로시저/함수/트리거는 지원되지 않습니다.
  • 지원되지 않거나 없음

일반적인 시나리오 테스트 비교

테이블 테스트 구조는 다음과 같습니다.

CREATE TABLE `sbtest1` ( `id` int NOT NULL AUTO_INCREMENT, `k` int NOT NULL DEFAULT '0', `c` char(120) NOT NULL DEFAULT '', `pad` char(60) NOT NULL DEFAULT '' , 기본 키 (`id`), 키 `k_1` (`k`) ) ENGINE=InnoDB;  
테이블의 데이터 볼륨은 1000w입니다.
> sbtest1에서 count(*)를 선택합니다. +----------+ | 개수(*) | +----------+ | 10000000 | +------------+

쿼리문은 다음과 같으며, 조건 필드가 인덱싱되고 IN 목록에는 10,000개의 상수가 포함됩니다.

sbtest1에서 개수(*)를 선택합니다. 여기서 k는 (2708275,5580784,7626186,8747250,228703,4589267,5938459,6982345,2665948,4830545,4929382,8723757,354179,1903입니다. 875,5111120,5471341,7098051,3113388,2584956,6550102 ,2842606,2744112,7077924,4580644,5515358,1787655,6391388,6044316,2658197,5628504,413887,6058866,3321587,1430333,445303,73 73496,9133196,6760595,4735642,4756387,9845147,9362192,7271805,4351748,6625915 ,3813276,4236692,8308973,4407131,9481423,3301846,432577,810938,3830320,6120078,6765157,6456566,6649509,1123840,2906490,99 65014,3725748, ... );

성능 비교는 아래 그림과 같습니다.

2.png

In-list 최적화 이후 기존 방법에 비해 성능이 36배 향상되는 것을 확인할 수 있습니다.

화웨이 클라우드의 신기술에 대해 빨리 알아보고 팔로우하려면 클릭하세요~

 

고등학생들이 성인식으로 자신만의 오픈소스 프로그래밍 언어를 만든다 - 네티즌들의 날카로운 논평: 애플은 방어에 의존해 만연한 사기로 인해 국내 서비스가 중단됐다 . 앞으로는 윈도 플랫폼 타오바오(taobao.com)에서 독립 게임을 제작할 계획이다. 웹 버전 최적화 작업을 다시 시작해 프로그래머들의 종착지, 비주얼 스튜디오 코드 1.89에서 가장 많이 쓰이는 자바 LTS 버전인 자바 17이 출시되고, 윈도 10에는 시장 점유율 70%, Windows 11은 계속해서 하락
{{o.이름}}
{{이름}}

추천

출처my.oschina.net/u/4526289/blog/11105468