MySQL 5.7 파티션 테이블 성능 저하의 근본 원인을 분석하는 사례를 사용합니다.

서문 :이 기사를 통해 MySQL 5.7.18 사용자가 파티션 테이블 사용의 함정을 인식하고이 버전을 계속 사용하지 않기를 바랍니다. 동시에, MySQL 5.7.18을 업그레이드 할 때 파티션 테이블 성능 저하의 근본 원인 인 소스 코드 공유를 통해 MySQL 소스 코드 애호가에게 파티션 테이블 구현에서 잠금 사용을 보여줍니다.

문제 설명

MySQL 5.7에는 많은 성능 관련 개선 사항이 있습니다. 임시 테이블 관련 성능 향상, 연결 설정 속도 최적화, 복제 및 배포 관련 성능 향상 등 기본적으로 구성을 변경할 필요가 없으며 버전 5.7로 업그레이드하기 만하면 많은 성능 향상을 가져올 수 있습니다.

환경을 테스트하고 데이터베이스를 버전 5.7.18로 업그레이드하고 MySQL 버전 5.7.18이 우리의 기대를 충족하는지 확인하고 있습니다. 일정 기간 동안 관찰이 실행되었으며 개발 피드백이 있으며 데이터베이스의 성능이 이전 버전 5.6.21보다 낮습니다. 주요 성능 기능은 더 많은 잠금 시간 초과 상황이 발생하는 것입니다. 성능 저하와 관련된 테이블이 모두 파티션 테이블이라는 개발의 또 다른 피드백입니다. 업데이트는 모두 기본 키입니다. 이 피드백은 우리의 관심을 끌었습니다. 우리는 다음과 같이 시도했습니다.

  • 데이터베이스 버전은 5.7.18입니다. 파티션 테이블을 예약하면 성능이 저하됩니다.
  • 데이터베이스 버전은 5.7.18이고 테이블은 파티션되지 않은 테이블로 조정되며 성능은 정상입니다.
  • 데이터베이스 버전을 버전 5.6.21로 롤백하고 파티션 테이블을 유지하면 성능이 정상입니다.

위의 테스트를 통해 이러한 성능 저하가 MySQL5.7 버전 업그레이드와 관련이 있음을 대략적으로 확인했습니다.

문제 재현

테스트 환경의 데이터베이스 테이블 구조가 더 많고 호출 관계도 더 복잡합니다. 문제를 더 분석하고 찾아 내기 위해 우리는 누에 고치를 가져와 다음과 같은 간단한 복제 과정을 구성했습니다.

// 创建一个测试分区表t2: 
 CREATE TABLE `t2`( 
 
  `id` INT(11) NOT NULL, 
 
  `dt` DATETIME NOT NULL, 
 
  `data` VARCHAR(10) DEFAULT NULL, 
 
  PRIMARYKEY (`id`,`dt`), 
 
  KEY`idx_dt`(`dt`) 
 
) ENGINE=INNODB DEFAULTCHARSET=latin1 
 /*!50100 PARTITION BY RANGE (to_days(dt)) 
 
(PARTITION p20170218 VALUES LESS THAN (736744)ENGINE = InnoDB, 
 
 PARTITIONp20170219 VALUES LESS THAN (736745) ENGINE = InnoDB, 
 
 PARTITIONpMax VALUES LESS THAN MAXVALUE ENGINE = InnoDB) */  
  
 
// 插入测试数据 
 INSERT INTO t2 VALUES (1, NOW(), '1'); 
 INSERT INTO t2 VALUES (2, NOW(), '2'); 
 INSERT INTO t2 VALUES (3, NOW(), '3');  
  
 
// SESSION 1 对id = 1的 记录 做一个更新操作,事务先不提交。 
 BEGIN;UPDATE t2 SET DATA = '12' WHERE id = 1;  
  
 
// SESSION 2 对id = 2 的记录做一个更新。  
 BEGIN;UPDATE t2 SET DATA = '21' WHERE id = 2;  

SESSION 2에서이 업데이트 작업이 대기 중임을 확인했습니다. ID는 기본 키입니다. 논리적으로 기본 키 ID가 1 인 레코드 업데이트는 기본 키 ID가 2 인 레코드 업데이트에 영향을주지 않습니다.

information_schema 아래의 innodb_locks 테이블을 쿼리합니다. 이 테이블은 InnoDB 트랜잭션이 적용하려고하지만 아직 획득하지 않은 잠금과 다른 트랜잭션을 차단하는 트랜잭션이 소유 한 잠금을 기록하는 데 사용됩니다. 두 가지 레코드가 있습니다.

여기에 사진 설명 삽입

이때 innodb_locks 테이블을 확인하십시오. 트랜잭션 id = 40021은 3 페이지의 두 번째 행 레코드를 잠그므로 트랜잭션 id = 40022를 진행할 수 없습니다.

데이터베이스를 버전 5.6.21로 롤백하면 위의 시나리오를 재현 할 수 없습니다.

추가 분석

innodb_locks 테이블에서 제공하는 정보에 따르면 문제는 InnoDB가 부적절한 행을 잠근다는 것입니다. 이 테이블은 메모리 스토리지 엔진입니다. 메모리 저장 엔진의 삽입 인터페이스에 중단 점을 설정하고 다음 스택 정보를 얻습니다. 빨간색 상자의 일부를 확인하고 잠금 정보를 innodb_locks 테이블에 씁니다.

여기에 사진 설명 삽입

그리고 fill_innodb_locks_from_cache 함수에서 데이터가 행에 기록 될 때마다 다음 코드에서 Cache 개체에서 가져 오는 것으로 확인됩니다.

여기에 사진 설명 삽입

트랜잭션 잠금 정보가 캐시에 저장된다는 것을 알고 있으므로 캐시의 데이터가 추가되는 방법을 더 자세히 알아봐야합니다. innodb 코드에서 캐시 객체가 나타나는 위치를 검색하여 add_lock_to_cache 함수를 찾습니다. 디버깅을 위해이 함수에 중단 점을 설정 한 후 해당 내용이 innodb_locks 테이블에 채워진 데이터와 일치 함을 발견했습니다. 이 함수에서 사용하는 잠금 개체, 즉 우리가 찾고있는 잠금 개체를 결정합니다.

여기에 사진 설명 삽입

lock_t 유형이 사용되는 위치의 문제를 해결하십시오. 스크리닝 및 디버깅 후 RecLock :: lock_add 함수에서 생성 된 행 잠금이 잠금이있는 트랜잭션 연결 목록에 추가되는 것을 확인했습니다.

여기에 사진 설명 삽입

RecLock :: lock_add 함수는 행 잠금 생성 이유를 추론 할 수 있습니다. 따라서 함수에 중단 점을 설정하여 함수 스택을보고 다음 스택의 빨간색 상자 위치에서 함수를 찾습니다.

여기에 사진 설명 삽입

다음과 같은 Partition_helper :: handle_ordered_index_scan 코드가 추적됩니다.이 코드 분석에 따르면 m_part_spec.end_part는 잠글 최대 행 수를 결정합니다. 이것이 비정상적인 행 잠금 생성의 원인입니다.

여기에 사진 설명 삽입

결국 문제는 m_part_spec.end_part의 생성으로 귀결됩니다. end_part의 사용을 조사하여 최종적으로 사용 전 변수의 초기 설정 값을 get_partition_set 함수에 위치시킵니다. 단일 레코드가 업데이트 될 때마다 인덱스 스캔이 잠기면 파티션 테이블의 동일한 수의 행이 잠기는 것을 코드에서 알 수 있습니다. 이것이 근본 원인입니다.

여기에 사진 설명 삽입

검증 결론

이전 분석에 따르면 단일 레코드의 각 업데이트 작업은 파티션 테이블에서 동일한 수의 행을 잠급니다. 우리는 우리의 발견을 확인하려고 노력합니다.

다음 두 개의 레코드를 추가하십시오.

INSERT INTO t2 VALUES (4, NOW(), '4'); 
 INSERT INTO t2 VALUES (5, NOW(), '5');  
 
// SESSION 1 对id = 1的 记录 做一个更新操作,事务先不提交。 
 BEGIN;UPDATE t2 SET DATA = '12' WHERE id = 1; 
 
// SESSION 2 现在对id = 4 的记录做一个更新。  
 BEGIN;UPDATE t2 SET DATA = '44' WHERE id = 4;  

id = 4에 대한 업데이트가 정상적으로 진행될 수 있음을 확인했습니다. id = 1 인 업데이트의 영향을받지 않습니다. 이는 id = 4 인 레코드가 테스트 케이스의 파티션 수를 초과하여 잠기지 않기 때문입니다. 실제 애플리케이션에서 파티션 테이블에 정의 된 파티션의 수는 테스트 케이스에서 3 개가 아니라 수십 또는 수백 개입니다. 이러한 잠금의 결과는 업데이트 상황에서 잠금 충돌을 악화시켜 트랜잭션이 잠금 대기 상태가됩니다. 아래 그림에서 볼 수 있듯이 각 트랜잭션에는 N 개의 행 잠금이 있으므로 서로를 덮는 이러한 잠긴 레코드의 가능성이 크게 향상되어 동시성이 감소하고 효율성이 저하됩니다.

여기에 사진 설명 삽입

결론적으로

위의 분석을 통해 우리는 이것이 MySQL 5.7의 회귀라고 확신합니다. 오픈 소스 커뮤니티에 버그를 제출했습니다. Oracle은 문제가 있음을 확인하고이 버그를 추가로 분석하고 조사해야합니다.

집중 해 길을 잃지 마

자, 여러분, 위는이 기사의 전체 내용입니다. 여기서 볼 수있는 사람은 모두 재능 입니다. 앞서 말했듯이 PHP에는 기술적 인 점이 많습니다. 너무 많기 때문에 쓰기가 정말 불가능하고, 작성한 후 너무 많이 읽지 않을 것이므로 필요한 경우 여기에서 PDF와 문서로 구성하겠습니다. 할 수있다

비밀 코드를 입력하려면 클릭하세요 : PHP + 「Platform」

여기에 사진 설명 삽입

여기에 사진 설명 삽입


더 많은 학습 내용을 보려면 [비교 표준 팩토리] 우수한 PHP Architect Tutorials 카탈로그를 방문 할 수 있습니다. 단, 급여가 한 단계 올라갈 수 있도록 읽을 수만 있으면됩니다 (연속 업데이트).

위의 내용이 도움이되기를 바랍니다 . 많은 PHP 사용자 가 고급 단계에서 항상 몇 가지 문제와 병목 현상에 직면 합니다 . 너무 많은 비즈니스 코드를 작성하면 방향 감각이 없습니다. 어디에서 개선을 시작해야할지 모르겠습니다. 다음과 같은 몇 가지 정보를 수집했습니다. 그러나 이에 국한되지는 않습니다 : 분산 아키텍처, 높은 확장 성, 고성능, 높은 동시성, 서버 성능 튜닝, TP6, laravel, YII2, Redis, Swoole, Swoft, Kafka, Mysql 최적화, 셸 스크립트, Docker, 마이크로 서비스, Nginx 등. 여러 지식 포인트, 고급 고급 건조 제품을 모든 사람과 무료로 공유 할 수 있으며 필요한 사람들은 내 PHP 기술 교환 그룹에 가입 할 수 있습니다.

추천

출처blog.csdn.net/weixin_49163826/article/details/108717529