MySQL 성능 병목 현상 심층 포지셔닝 분석

       성능 테스트 과정에서 MySQL에서 성능 병목 현상이 자주 발생합니다.데이터베이스의 경우 소위 성능 병목 현상은 SQL 느린, 높은 CPU, 높은 IO 및 높은 메모리에 불과합니다. 처음 세 가지는 실제 예제를 기반으로 합니다. 성능 분석, 최종 높은 메모리는 방법론적 설명일 뿐입니다(실제 테스트 프로젝트에서는 발생하지 않음).

       먼저 데이터베이스 구성에 성능 문제가 없는지 확인해야 하며, 결국 성능 테스트 전에 몇 가지 기본 구성을 다시 확인하여 낮은 수준의 실수를 방지해야 합니다.

       이 기사는 1. 느린 SQL 포지셔닝 분석, 2. 높은 CPU 포지셔닝 분석, 3. 높은 IO 포지셔닝 분석, 4. 높은 메모리 포지셔닝 분석을 포함한 분석을 위한 실제 프로젝트 예제(절대적으로 하드 코어)를 결합합니다.

1. 느린 SQL 포지셔닝 분석

       먼저 느린 비즈니스 시스템은 응답시간에 반영되어야 하므로 성능 테스트에서 느리다고 판단되면 응답시간에서 분할하여 최종적으로 mysql로 ​​분할하게 되는데, 이는 마찬가지로 느린 SQL을 분석한다.마찬가지로 높은 동시성에서 발견된다면 mysql 프로세스는 높은 CPU를 차지하며, 느린 SQL이 있는지 여부를 분석하는 것도 우선순위이며 느린 SQL을 판단하는 것은 비교적 간단하다. 느린 로그 쿼리를 보는 것입니다.

1. 첫 번째는 느린 로그 쿼리를 여는 것입니다.

#查看是否开启,以及存放路径
show variables like '%slow_query_log%';

#开启
set global slow_query_log = 1;

#记录慢日志的时间,默认情况下为10秒
show variables like '%long_query_time%'

#查看慢日志条数
show global status like '%slow_queries%'

set global slow_query_log=1;을 사용하여 느린 쿼리 로그가 현재 데이터베이스에만 적용되도록 하려면 MySQL이 다시 시작되면 무효가 됩니다. 영구적으로 적용하고 싶다면 설정파일을 수정해야 하는데 사실 그럴 필요는 없다 성능문제 분석을 위해 일시적으로 열어보기만 한다(분석 후 닫아야 함).

2. 테스트 프로세스가 느린 SQL을 얻습니다.

로그를 수동으로 분석하고 SQL을 찾고 분석하는 것은 분명히 수동 작업입니다. MySql은 로그 분석 도구 mysqldumpslow를 제공합니다.

#得到返回记录集最多的10个SQL
Mysqldumpslow –s r –t 10 /usr/local/mysql/data/localhost-slow.log
#得到访问次数最多的10个SQL
Mysqldumpslow –s c –t 10 /usr/local/mysql/data/localhost-slow.log
#得到按照时间排序的前10条里面含有左连接的查询
Mysqldumpslow –s t –t 10 –g “left join” /usr/local/mysql/data/localhost-slow.log
#另外建议在使用这些命令时结合|和more使用,否则可能出现爆破情况
Mysqldumpslow –s r –t 10 /usr/local/mysql/data/localhost-slow.log | more
参数含义
s: 表示按照何种方式排序
c:访问次数
l:锁定时间
r:返回记录
t:查询时间
al:平均锁定时间
t:返回前面多少条的数据
g:后面搭配一个正则表达式

또한 APM(전체 링크 모니터링)을 통해 모니터링할 수도 있고 느린 SQL도 모니터링할 수 있습니다(물론 스트레스 테스트 프로세스 중에 일부 무거운 도구에 의존하는 것은 권장하지 않음).

3. 예비 해석 분석

이것은 가장 기본적인 기능입니다. 물론 느린 SQL을 얻으려면 실제로 얼마나 느린지와 인덱스 구성 여부를 확인해야 합니다. 실제 테스트 프로젝트의 SQL 문을 사용하여 분석하십시오.

explain SELECT count(c.id)
        FROM administrative_check_content c
        LEFT JOIN administrative_check_report_enforcers e ON c.report_id=e.report_id
        LEFT JOIN administrative_check_report r ON c.report_id = r.id
        WHERE e.enforcer_id= 'ec66d95c8c6d437b9e3a460f93f1a592';

86%의 시간이 데이터 전송에 소비된 것으로 분석될 수 있습니다(소위 "데이터 전송"은 단순히 데이터를 전송하는 것이 아니라 "[검색] + 데이터 전송"을 포함합니다).

Explain 설명을 통해 다음과 같이 인덱스(enforcer_id_index)가 추가되었고 인덱스를 통해 거의 30084개의 데이터가 검색되었음을 알 수 있다.

 일반적으로 인덱스가 추가되지 않거나 무리하게 추가되는 경우 이러한 분석을 통해 바로 알 수 있는데 인덱스 문제는 SQL이 느려지는 주요 원인 중 하나라고 할 수 있으며 이는 2차 스트레스 테스트 항목의 예에서 지수가 없는 최고 TPS는 200에 불과하고 지수가 있는 최고 TPS는 아래와 같이 900입니다.

Explain 문에서 주로 인덱스와 관련된 다음 열에 중점을 둡니다.

(1)
type 컬럼은 연결에 사용되는 카테고리와 인덱스 사용 여부를 보여주는 매우 중요합니다. 가장 좋은 것부터 가장 나쁜 것까지의 일반적인 조인 유형은 const, eq_reg, ref, range, indexhe 및 ALL입니다.

(2) possible_keys
possible_keys 열은 MySQL이 테이블에서 행을 찾는 데 사용할 수 있는 인덱스를 나타냅니다. 이 열은 EXPLAIN 출력에 표시된 테이블의 순서와 완전히 독립적입니다. 이는 possible_keys의 일부 키가 실제로 생성된 테이블 순서로 사용될 수 없음을 의미합니다.
열이 NULL이면 연결된 인덱스가 없습니다. 이 경우 WHERE 절이 특정 열이나 인덱싱에 적합한 열을 참조하는지 확인하여 쿼리 성능을 향상시킬 수 있습니다. 그렇다면 적절한 인덱스를 만들고 EXPLAIN으로 쿼리를 다시 확인하십시오.
(3) key
key 컬럼은 MySQL이 실제로 사용하기로 결정한 키(index)를 보여준다. 인덱스를 선택하지 않으면 키는 NULL입니다. MySQL이 possible_keys 열의 인덱스를 사용하거나 무시하도록 하려면 쿼리에서 FORCE INDEX, USE INDEX 또는 IGNORE INDEX를 사용하십시오.
(4) key_len
key_len 열은 MySQL이 사용하기로 결정한 키 길이를 표시합니다. 키가 NULL이면 길이는 NULL입니다. 인덱스의 길이를 사용하면 정확도를 잃지 않고 짧을수록 좋습니다.
(5) ref
ref 열은 키와 함께 테이블에서 행을 선택하는 데 사용되는 열 또는 상수를 보여줍니다. 이 열에는 여러 테이블과 연결된 필드가 포함되며 const는 상수 연결을 나타냅니다. Ref는 종종 인덱스에 영향을 미치는 위치입니다.

4. SQL 분석을 위해 show profile 사용

분석을 시작하는 것도 매우 간단합니다. set profiling=1을 사용하여 임시로 엽니다(이 함수는 가장 최근 쿼리의 분석 문을 캐시하며 기본값은 15, 최대값은 100입니다. sql 분석에 적합합니다. 압력 테스트가 완료된 후 다음과 같이 사용 후 설정하십시오.

#显示是否开启Profiling,以及最多存储多少条
show variables like '%profil%';

#开启Profiling
set profiling=1;

#执行你的SQL
#在这里我们主要是执行前面所找到的慢SQL

#查看分析
show profiles;

프로필 표시를 통해 위에서 실행한 SQL을 볼 수 있습니다(Query_ID=18, 최신 데이터 모니터링을 위해 Query_ID는 25를 사용하는 것이 가장 좋습니다).

 执行: 쿼리 18에 대한 프로필 CPU, 메모리, 블록 io 표시;

데이터를 보내는 데도 총 0.39초가 소요되며 이 중 CPU_user 시간이 상대적으로 높은 비율을 차지하는 것을 알 수 있으며(단순한 SQL 문은 그만큼 많은 시간을 소비함) 이 SQL의 IO 오버헤드도 볼 수 있습니다(왜냐하면 쿼리는 모두 op out 블록 출력입니다).

SQL에서 테이블을 조회하여 위의 레코드를 볼 수도 있습니다.

select QUERY_ID,SEQ,STATE,DURATION,CPU_USER,CPU_SYSTEM,BLOCK_OPS_IN,BLOCK_OPS_OUT from information_schema.PROFILING where QUERY_ID = 18

또한 이 쇼 프로필 설명을 설명하세요.

show profile cpu, block io, memory,swaps,context switches,source for query [Query_ID];

# Show profile后面的一些参数:
# - All:显示所有的开销信息
# - Cpu:显示cpu相关开销
# - Block io:显示块IO相关开销
# - Context switches: 上下文切换相关开销
# - Memory:显示内存相关开销
# - Source:显示和source_function,source_file,source_line相关的开销信息

결론: 느린 SQL을 통해 소스를 추적하여 느린 원인을 찾을 수 있으므로 성능 조정을 매우 잘 안내할 수 있습니다(성능 테스트 엔지니어는 조정할 수 없지만 성능 분석은 실제로 좋지 않습니다. 느린 것에 대해서만 이야기하지만 느린 것이 개발자가 존경하거나 존경 받기조차 어렵다는 것을 개발자에게 알려주지 않습니다!).

둘째, 높은 CPU 위치 분석

1. SQL로 인한 높은 CPU

성능 스트레스 테스트 동안 데이터베이스의 높은 CPU에 대한 많은 이유가 있습니다. 일반적으로 느린 SQL과 관련이 있습니다(각 SQL이 일반적으로 높은 CPU 또는 높은 IO를 차지하기 때문에), 그런 다음 특정 일부 SQL을 분석하는 방법 원인?

(1) 먼저 CPU를 많이 차지하는 프로세스를 찾습니다.

TOP 명령을 통해 MySQL의 높은 CPU 사용량을 찾은 다음 MySQL 프로세스에서 CPU 사용량이 많은 스레드 수를 확인합니다.

# top -p [pid] H
top -p 44662 H

6개가 Running 상태이고 CPU가 상당히 높은 것을 알 수 있는데 36개는 Sleeping이고 2개는 Sleeping 상태로 CPU가 매우 높습니다.

(2) 다음과 같이 SHOW FULL PROCESSLIST를 사용합니다.

 필터링을 통해 지정된 SQL을 얻으려면 mysql의 information_schema 라이브러리를 직접 쿼리할 수 있습니다.

SELECT * FROM `information_schema`.`PROCESSLIST` where host like '172.16.1.133%'

많은 항목이 Sending data 상태에 있음을 알 수 있습니다. 분석할 가장 복잡한 문을 선택합니다.

(SELECT r.id,c.check_action_name,check_date,check_end_date,c.id AS check_content_id,c.check_object_name AS checkObjectName,c.update_time,c.verify	
        FROM administrative_check_content c	
        LEFT JOIN administrative_check_report r ON c.report_id=r.id	
        LEFT JOIN administrative_check_report_enforcers e ON r.id=e.report_id	
        WHERE e.enforcer_id= 'ec66d95c8c6d437b9e3a460f93f1a592'	      	      	
        )		
            UNION ALL	
            (	
            SELECT r.id,r.check_action_name,check_date,check_end_date,'0','无' AS checkObjectName,r.update_time,false	
            FROM administrative_check_report r	
            LEFT JOIN administrative_check_report_enforcers e ON r.id=e.report_id	
            WHERE e.enforcer_id= 'ec66d95c8c6d437b9e3a460f93f1a592'	
            AND r.check_content_id='0'	
             	
            )	    	
        ORDER BY update_time DESC,check_content_id	
        LIMIT 0, 15	

(3) 앞서 언급한 쇼 프로필 을 사용하여 이 진술을 분석합니다 .

 많은 시간과 시간을 소모하는 Sending 데이터가 2개 있음을 알 수 있다.실제로 이 문장은 공동 쿼리를 사용한다.우리는 SQL 문장을 분할하여 분석을 계속할 수 있다(복잡한 문장은 비교적 불쾌한 간단한 진술. 때로는 분리 된 진술도 매우 느림) 분석을 계속하려면 진술을 꺼내십시오.

SELECT r.id,c.check_action_name,check_date,check_end_date,c.id AS check_content_id,c.check_object_name AS checkObjectName,c.update_time,c.verify	
        FROM administrative_check_content c	
        LEFT JOIN administrative_check_report r ON c.report_id=r.id	
        LEFT JOIN administrative_check_report_enforcers e ON r.id=e.report_id	
        WHERE e.enforcer_id= 'ec66d95c8c6d437b9e3a460f93f1a592'	 

이 split 문을 실행하려면 다음과 같이 쿼리 시간이 0.8초 이상 걸립니다.

EXPLAIN 분석을 실행하고 다음과 같이 인덱스를 통해 쿼리된 콘텐츠 행이 최대 30084개 있는지 확인합니다.

일반적으로 이와 같은 시스템을 스트레스 테스트에 사용하면 대부분의 데이터 분포가 불합리하고 테스트 데이터가 실제 비즈니스 시나리오를 반영하지 않으며 데이터 분포가 명백히 고르지 않습니다. 하나의 ID 번호는 30,000개의 데이터와 연결됩니다. , 인덱스 활용의 효율성이 반영되지 않습니다. .

요약: SHOW PROCESSLIST를 통해 Mysql의 현재 쓰레드 상태와 주요 자원 소비가 어디에 있는지 알 수 있고, show profile과 결합하여 높은 CPU를 차지하는 특정 SQL을 분석하면 SQL로 인한 높은 CPU의 원인을 추가로 찾을 수 있습니다. , 이 단계는 의심할 여지 없이 개발자를 최적화 방향으로 안내할 수 있습니다.

2. 다른 이유로 인한 높은 CPU

기본적으로 분석 아이디어는 SQL 이유(SQL로 인한 문제는 주로 CPU나 IO에 집중되고, 높은 IO는 간접적으로 높은 CPU로 연결됨)를 제외하고는 위와 유사하며, 다른 이유로 인해 높은 CPU가 표시될 수 있음 mysql show processlist + show Status + kill Id를 통해 찾습니다.

(1) 먼저 SHOW PROCESSLIST를 통해 mysql 스레드 상태를 쿼리하려면 State 열의 다양한 상태의 의미를 이해하는 데 집중해야 합니다.

Checking table
 正在检查数据表(这是自动的)。
Closing tables
 正在将表中修改的数据刷新到磁盘中,同时正在关闭已经用完的表。这是一个很快的操作,如果不是这样的话,就应该确认磁盘空间是否已经满了或者磁盘是否正处于重负中。
Connect Out
 复制从服务器正在连接主服务器。
Copying to tmp table on disk
 由于临时结果集大于tmp_table_size,正在将临时表从内存存储转为磁盘存储以此节省内存(如果临时表过大会导致mysql将临时表写入硬盘的时间过长,会影响整体性能)。
Creating tmp table
 正在创建临时表以存放部分查询结果。
deleting from main table
 服务器正在执行多表删除中的第一部分,刚删除第一个表。
deleting from reference tables
 服务器正在执行多表删除中的第二部分,正在删除其他表的记录。
Flushing tables
 正在执行FLUSH TABLES,等待其他线程关闭数据表。
Killed
 发送了一个kill请求给某线程,那么这个线程将会检查kill标志位,同时会放弃下一个kill请求。MySQL会在每次的主循环中检查 kill标志位,不过有些情况下该线程可能会过一小段才能死掉。如果该线程程被其他线程锁住了,那么kill请求会在锁释放时马上生效。
Locked
 被其他查询锁住了。
Sending data
 正在处理SELECT查询的记录,同时正在把结果发送给客户端。
Sorting for group
 正在为GROUP BY做排序。
 Sorting for order
 正在为ORDER BY做排序。
Opening tables
 这个过程应该会很快,除非受到其他因素的干扰。例如,在执ALTER TABLE或LOCK TABLE语句行完以前,数据表无法被其他线程打开。正尝试打开一个表。
Removing duplicates
 正在执行一个SELECT DISTINCT方式的查询,但是MySQL无法在前一个阶段优化掉那些重复的记录。因此,MySQL需要再次去掉重复的记录,然后再把结果发送给客户端。
Reopen table
 获得了对一个表的锁,但是必须在表结构修改之后才能获得这个锁。已经释放锁,关闭数据表,正尝试重新打开数据表。
Repair by sorting
 修复指令正在排序以创建索引。
Repair with keycache
 修复指令正在利用索引缓存一个一个地创建新索引。它会比Repair by sorting慢些。
Searching rows for update
 正在讲符合条件的记录找出来以备更新。它必须在UPDATE要修改相关的记录之前就完成了。
Sleeping
 正在等待客户端发送新请求.(Sleeping过多也是问题,比如wait_timeout设置过大,导致MySQL里大量的SLEEP进程无法及时释放,拖累系统性能,不过也不能把它设置的过小,否则你可能会遭遇到“MySQL has gone away”之类的问题)。
System lock
 正在等待取得一个外部的系统锁。如果当前没有运行多个mysqld服务器同时请求同一个表,那么可以通过增加--skip-external-locking参数来禁止外部系统锁。
Upgrading lock
 INSERT DELAYED正在尝试取得一个锁表以插入新记录。
Updating
 正在搜索匹配的记录,并且修改它们。
User Lock
 正在等待GET_LOCK()。
Waiting for tables
 该线程得到通知,数据表结构已经被修改了,需要重新打开数据表以取得新的结构。然后,为了能的重新打开数据表,必须等到所有其他线程关闭这个 表。以下几种情况下会产生这个通知:FLUSH TABLES tbl_name, ALTER TABLE, RENAME TABLE, REPAIR TABLE, ANALYZE TABLE,或OPTIMIZE TABLE。
waiting for handler insert
 INSERT DELAYED已经处理完了所有待处理的插入操作,正在等待新的请求。

        위의 상태들은 대부분 매우 빠른 동작에 해당하며, 한 쓰레드가 몇 초 동안 같은 상태를 유지하고 있는 한 문제가 있을 수 있으므로 확인이 필요합니다.
     위에 나열되지 않은 다른 상태도 있지만 대부분은 서버에 오류가 있는지 확인하는 데만 유용합니다.

(2) 둘째 , 상태 표시를 통해 MySQL의 현재 실행 상태를 쿼리합니다.

다음 상태 값과 그 의미를 이해하고 일상적인 운영 및 유지 보수 과정에서 이러한 측면의 기록이 있는 경우 시스템의 성능이 비정상적일 때 상태 값을 비교할 수 있습니다. 너무 크면주의를 기울여야합니다.이 매개 변수 값은 다음과 같이 작동 및 유지 보수 모니터링 시스템에 우려 지표로 추가됩니다.

Aborted_clients 由于客户没有正确关闭连接已经死掉,已经放弃的连接数量。
Aborted_connects 尝试已经失败的MySQL服务器的连接的次数。
Connections 试图连接MySQL服务器的次数。
Created_tmp_tables 当执行语句时,已经被创造了的隐含临时表的数量。
Delayed_insert_threads 正在使用的延迟插入处理器线程的数量。
Delayed_writes 用INSERT DELAYED写入的行数。
Delayed_errors 用INSERT DELAYED写入的发生某些错误(可能重复键值)的行数。
Flush_commands 执行FLUSH命令的次数。
Handler_delete 请求从一张表中删除行的次数。
Handler_read_first 请求读入表中第一行的次数。
Handler_read_key 请求数字基于键读行。
Handler_read_next 请求读入基于一个键的一行的次数。
Handler_read_rnd 请求读入基于一个固定位置的一行的次数。
Handler_update 请求更新表中一行的次数。
Handler_write 请求向表中插入一行的次数。
Key_blocks_used 用于关键字缓存的块的数量。
Key_read_requests 请求从缓存读入一个键值的次数。
Key_reads 从磁盘物理读入一个键值的次数。
Key_write_requests 请求将一个关键字块写入缓存次数。
Key_writes 将一个键值块物理写入磁盘的次数。
Max_used_connections 服务器启动后同时使用的连接的最大数目。
Not_flushed_key_blocks 在键缓存中已经改变但是还没被清空到磁盘上的键块。
Not_flushed_delayed_rows 在INSERT DELAY队列中等待写入的行的数量。
Open_tables 当前打开表的数量。
Open_files 打开文件的数量。
Open_streams 打开流的数量(主要用于日志记载)
Opened_tables 已经打开的表的数量。
Questions 发往服务器的查询的数量。
Slow_queries 要花超过long_query_time时间的查询数量。
Threads_connected 当前打开的连接的数量。
Threads_running 不在睡眠(激活)的线程数量。
Uptime 服务器工作了多少秒。
Uptime_since_flush_status 最近一次使用FLUSH STATUS 的时间(以秒为单位)

( 3) 마지막으로 id를 죽이고(ID는 SHOW PROCESSLIST에 표시됨) CPU를 많이 차지한다고 의심되는 스레드를 꺼서 CPU를 낮출 수 있는지 확인할 수 있습니다.

mysql의 경우 느린 SQL과 교착 상태 이외의 CPU 문제는 찾기가 정말 어렵습니다. 데이터베이스 시스템과 성능에 대한 충분한 이해가 필요합니다. 성능 테스트를 위해 우리가 할 수 있는 것은 계층별로 분석하고 범위를 좁히는 것입니다. 문제는 실제로 실현 가능하지 않습니다. , 시행 착오 문제 해결을 위해서만 kill id 방법을 사용할 수 있습니다.

3. 높은 IO 포지셔닝 분석

       실제로 디스크 I/O가 상대적으로 느려 CPU가 디스크 I/O 요청을 기다리게 하기 때문에 IO가 높으면 CPU도 높아집니다. 데이터베이스 IO 분석은 기본 기술입니다(결국 대부분의 데이터베이스는 극도로 최적화되어 있으며 최종 병목 현상도 IO일 수 있으며 IO 조정은 더 어려울 것입니다).

(1) 먼저 allmighty top 명령을 사용하여 프로세스를 봅니다.

[root@localhost ~]# top
top - 11:53:04 up 702 days, 56 min,  1 user,  load average: 7.18, 6.70, 6.47
Tasks: 576 total,   1 running, 575 sleeping,   0 stopped,   0 zombie
Cpu(s):  7.7%us,  3.4%sy,  0.0%ni, 77.6%id, 11.0%wa,  0.0%hi,  0.3%si,  0.0%st
Mem:  49374024k total, 32018844k used, 17355180k free,   115416k buffers
Swap: 16777208k total,   117612k used, 16659596k free,  5689020k cached
 
  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
14165 mysql     20   0 8822m 3.1g 4672 S 162.3  6.6  89839:59 mysqld
40610 mysql     20   0 25.6g  14g 8336 S 121.7 31.5 282809:08 mysqld
49023 mysql     20   0 16.9g 5.1g 4772 S  4.6 10.8   34940:09 mysqld

분명히 처음 두 개의 mysqld 프로세스로 인해 전체 로드가 높아졌습니다. 또한 Cpu(s) 라인의 통계 결과에서 %us 및 %wa의 높은 값은 현재 병목 현상이 사용자 프로세스 및 디스크 I/O 대기에 의해 소비된 CPU일 수 있음을 나타냅니다. .
(2) 먼저 디스크 I/O의 상황을 분석합니다.

sar -d 1 또는 (iostat -d -x -k 1) 명령(1초마다 새로 고침)을 실행하여 디스크 I/O가 실제로 큰지 확인합니다.

[root@localhost ~]# sar -d 1
Linux 2.6.32-431.el6.x86_64 (localhost.localdomain)     06/05/2020  _x86_64_        (8 CPU)
11:54:31 AM     DEV      tps    rd_sec/s    wr_sec/s    avgrq-sz   avgqu-sz  await     svctm   %util
11:54:32 AM    dev8-0   5338.00 162784.00   1394.00     30.76      5.24      0.98      0.19    100.00
11:54:33 AM    dev8-0   5134.00 148032.00  32365.00     35.14      6.93      1.34      0.19    100.10
11:54:34 AM    dev8-0   5233.00 161376.00    996.00     31.03      9.77      1.88      0.19    100.00
11:54:35 AM    dev8-0   4566.00 139232.00   1166.00     30.75      5.37      1.18      0.22    100.00
11:54:36 AM    dev8-0   4665.00 145920.00    630.00     31.41      5.94      1.27      0.21    100.00
11:54:37 AM    dev8-0   4994.00 156544.00    546.00     31.46      7.07      1.42      0.20    100.00

%util이 100%에 가까우면 너무 많은 I/O 요청이 생성되고 qvgqu-sz도 높으며 I/O 시스템이 완전히 로드되었음을 의미합니다. 

높은 IO에 대한 판단 기준 : % util 이 100%에 가깝고(디스크 IO 사용량, 많은 IO 요청을 나타냄) , await 가 svctm 보다 훨씬 큽니다 (평균 IO 대기 시간이 평균 서비스 시간보다 훨씬 큼, 느린 IO 응답 을 나타냄) , avgqu -sz 가 상대적으로 큽니다 (IO 평균 대기열 길이 ).

(3) iotop을 재사용하여 가장 많은 디스크 I/O 리소스를 사용하는 프로세스를 확인합니다.

[root@localhost ~]# iotop
Total DISK READ: 59.52 M/s | Total DISK WRITE: 598.63 K/s
TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN     IO>    COMMAND
16397 be/4 mysql       7.98 M/s    0.00 B/s  0.00 % 95.67 % mysqld --basedir=/usr/local/mysql5.7 --datadir=/usr/local/mysql5.7/data --port=3306
 7295 be/4 mysql      9.95 M/s    0.00 B/s  0.00 % 93.72 % mysqld --basedir=/usr/local/mysql5.7 --datadir=/usr/local/mysql5.7/data --port=3306
14295 be/4 mysql      9.86 M/s    0.00 B/s  0.00 % 94.53 % mysqld --basedir=/usr/local/mysql5.7 --datadir=/usr/local/mysql5.7/data --port=3306
14288 be/4 mysql      13.38 M/s    0.00 B/s  0.00 % 92.21 % mysqld --basedir=/usr/local/mysql5.7 --datadir=/usr/local/mysql5.7/data --port=3306
14292 be/4 mysql      13.54 M/s    0.00 B/s  0.00 % 91.96 % mysqld --basedir=/usr/local/mysql5.7 --datadir=/usr/local/mysql5.7/data --port=3306

포트 번호가 3306인 인스턴스가 더 많은 디스크 I/O 리소스를 사용하는 것을 볼 수 있으므로 이 인스턴스에서 실행 중인 쿼리를 살펴보겠습니다.

(4) 위에서 언급한 SHOW PROCESSLIST 방법을 사용하거나 mysqladmin 명령 도구를 사용할 수 있습니다.

현재 실행 중인 SQL을 확인해야 합니다.

(아래에서 mysqladmin 메소드가 사용됩니다. 이 명령은 mysql과 함께 제공되며, 이는 쉬운 호출을 위한 소프트 링크를 생성할 수 있습니다. ln -s /usr/local/mysql/bin/mysqladmin /usr/bin):

[root@localhost ~]# mysqladmin -uroot -p123456 pr|grep -v Sleep
+----+----+----------+----+-------+-----+--------------+-----------------------------------------------------------------------------------------------+
| Id |User| Host     | db |Command|Time | State        | Info                                                                                          |
+----+----+----------+----+-------+-----+--------------+-----------------------------------------------------------------------------------------------+
| 25 |root| 172.16.1.133:45921 | db | Query | 68  | Sending data | select max(Fvideoid) from (select Fvideoid from t where Fvideoid>404612 order by Fvideoid) t1 |
| 26 |root| 172.16.1.133:45923 | db | Query | 65  | Sending data | select max(Fvideoid) from (select Fvideoid from t where Fvideoid>484915 order by Fvideoid) t1 |
| 28 |root| 172.16.1.133:45928 | db | Query | 130 | Sending data | select max(Fvideoid) from (select Fvideoid from t where Fvideoid>404641 order by Fvideoid) t1 |
| 27 |root| 172.16.1.133:45930 | db | Query | 167 | Sending data | select max(Fvideoid) from (select Fvideoid from t where Fvideoid>324157 order by Fvideoid) t1 |
| 36 |root| 172.16.1.133:45937 | db | Query | 174 | Sending data | select max(Fvideoid) from (select Fvideoid from t where Fvideoid>324346 order by Fvideoid) t1 |
+----+----+----------+----+-------+-----+--------------+-----------------------------------------------------------------------------------------------+

느린 쿼리가 많이 완료되지 않은 것을 볼 수 있는데, 느린 쿼리 로그를 보면 이러한 SQL이 자주 발생함을 알 수 있습니다.
이것은 매우 비효율적인 SQL 쓰기 방법으로 전체 기본 키를 스캔해야 하지만 실제로는 느린 쿼리 로그에서 볼 수 있는 최대값만 얻으면 됩니다.

Rows_sent: 1  Rows_examined: 5413058

매번 5백만 행 이상의 데이터를 스캔해야 하지만 최대값만 읽어야 하므로 매우 비효율적입니다.

분석 후 이 SQL은 N의 거듭제곱으로 개선된 간단한 수정으로 한 자리 밀리초 내에 완료될 수 있습니다.
변환 방법은 쿼리 결과를 역순으로 정렬하고 첫 번째 레코드를 가져오는 것입니다. 원래 방법은 결과를 양수 순서로 정렬하고 마지막 레코드를 가져오는 것입니다.

요약: mysql의 IO 분석 아이디어는 매우 간단합니다.먼저, %wa(CPU가 디스크 쓰기가 완료될 때까지 CPU가 기다리는 시간을 말함, 일반적으로 0, 높을수록 disk is busy) to top은 변동이 큰지 여부, 두 번째로 디스크 I/O 상황을 분석하여 가장 많은 IO 리소스를 차지하는 프로세스를 파악하고 마지막으로 SHOW PROCESSLIST 또는 mysqladmin을 사용하여 IO를 사용하여 자주 호출되는 명령문을 확인합니다. .

넷째, 높은 메모리 위치 분석

       리눅스에서는 MemFree, 가상메모리(Swap), Buffers, Cached 등의 개념을 이해해야 하고 직관적인 부분이 윈도우만큼 좋지 않기 때문에 초보자가 메모리 사용량 수준을 분석하는 것은 쉬운 일이 아니다. mysql 자체는 기본적으로 메모리/캐시를 열지 않으며 모니터링(performance_schema에만 메모리 오버헤드 통계가 있음)과 관련하여 일반적인 mysql 모니터링 소프트웨어에서도 직관적으로 문제를 노출하기 어렵습니다.

(1) 우선, 범용 TOP 명령을 사용하여 메모리를 분석하여 mysql 프로세스가 많은 양의 메모리를 차지하는지 확인할 수 있습니다.

       위의 그림에서 free, buffer, cached가 높지 않아 사용된 메모리의 대부분을 프로세스가 점유하고 있고 Swap이 발생하지 않기 때문에 메모리 사용량이 높은 것을 쉽게 알 수 있다. 상황), 스왑 스왑 파티션의 사용 값이 지속적으로 변경되는 경우 커널이 메모리와 스왑 간에 지속적으로 데이터를 교환하고 있다는 의미이며, 이는 아마도 메모리가 충분하지 않기 때문일 수 있음), 이제 MySQL이 63.3%를 차지하므로 메모리가 정말 높다고 판단할 수 있습니다.

(2) 많은 수의 SQL 연산이 높은 메모리를 차지하는지 확인

mysql의 스레드를 확인하여 장기 실행 또는 차단된 SQL이 있는지 확인하고 범용 show full processlist를 사용하십시오. 란) 그 사유를 배제할 수 있다.

(3) mysql 연결이 모두 소진된 후 메모리가 실제로 해제되지 않는지 확인하기 위해 mysql 메모리/캐시 관련 구성을 확인합니다.

MySQL의 메모리 소비는 일반적으로 글로벌 수준의 공유 메모리와 세션 수준의 개인 메모리의 두 가지 유형으로 나뉩니다.

다음 명령을 실행하여 전역 수준 공유 메모리 할당을 쿼리합니다.

show variables where variable_name in (
'innodb_buffer_pool_size','innodb_log_buffer_size','innodb_additional_mem_pool_size','query_cache_size','key_buffer_size'
);

세션 수준 개인 메모리는 주로 데이터베이스 연결 개인 메모리에 사용되며 쿼리 명령은 다음과 같습니다. 

show variables where variable_name in (
'tmp_table_size','sort_buffer_size','read_buffer_size','read_rnd_buffer_size','join_buffer_size','thread_stack', 'binlog_cache_size'
);

현재 메모리나 캐시 사용량을 알아보기 위해 mysql query 명령어를 사용하는 것이 합리적이지만 mysql은 기본적으로 메모리 모니터링을 활성화하지 않는다.

SELECT * FROM performance_schema.setup_instruments
       WHERE  NAME LIKE '%memory%' and NAME not LIKE '%memory/performance_schema%';

update 문을 사용하여 일괄적으로 켤 수 있습니다(임시 켜짐이며 mysql을 다시 시작한 후 꺼집니다).

mysql> update performance_schema.setup_instruments set enabled = 'yes'
       WHERE  NAME LIKE '%memory%' and NAME not LIKE '%memory/performance_schema%';
> Affected rows: 310
> 时间: 0.002s

그런 다음 다음 명령문으로 mysql의 모든 메모리 사용량을 찾을 수 있습니다.

SELECT SUBSTRING_INDEX(event_name,'/',2) AS
       code_area, sys.format_bytes(SUM(current_alloc))
       AS current_alloc
       FROM sys.x$memory_global_by_current_bytes
       GROUP BY SUBSTRING_INDEX(event_name,'/',2)
       ORDER BY SUM(current_alloc) DESC;

다음과 같이 memory/innodb가 가장 높은 메모리를 차지하는 것으로 나타났습니다.

 메모리/innodb 쿼리를 더 세분화할 수 있습니다.

SELECT * FROM sys.memory_global_by_current_bytes WHERE event_name LIKE 'memory/innodb%';

메모리 사용량 추정을 위해 인터넷에서 누군가가 메모리 계산기를 추천했고 통계 사이트는 http://www.mysqlcalculator.com/ 입니다. 

(참고: 위 그림의 왼쪽 열은 mysql의 기본 구성이고 오른쪽 열은 현재 데이터베이스의 구성입니다   [변수 표시를 통해 찾을 수 있음] . 최대 메모리 사용량을 추정할 수 있습니다. 위의 그림에서 구성을 약간 늘리면 7119MB에 도달할 수 있습니다. 예상 결과가 요구 사항을 충족하지 않으면 현재 구성이 비합리적이며 조정해야 함을 의미합니다. 

mysql 데이터베이스 메모리/캐시 최적화에 대한 경험이 없습니다.다음은 인터넷에서 제공되는 최적화 프로세스 구성 항목입니다(실제 설정은 컴퓨터의 메모리 양에 따라 다르며 메모리 계산기로 계산하여 확인 가능) 기준을 초과함):

key_buffer_size = 32M //key_buffer_size指定索引缓冲区的大小,它决定索引处理的速度,尤其是索引读的速度。只对MyISAM表起作用。即使你不使用MyISAM表,但是内部的临时磁盘表是MyISAM表,也要使用该值。由于我的数据库引擎为innodb,大部分表均为innodb,此处取默认值一半32M。
query_cache_size = 64M //查询缓存大小,当打开时候,执行查询语句会进行缓存,读写都会带来额外的内存消耗,下次再次查询若命中该缓存会立刻返回结果。默认改选项为关闭,打开则需要调整参数项query_cache_type=ON。此处采用默认值64M。
tmp_table_size = 64M //范围设置为64-256M最佳,当需要做类似group by操作生成的临时表大小,提高联接查询速度的效果,调整该值直到created_tmp_disk_tables / created_tmp_tables * 100% <= 25%,处于这样一个状态之下,效果较好,如果网站大部分为静态内容,可设置为64M,如果为动态页面,则设置为100M以上,不宜过大,导致内存不足I/O堵塞。此处我们设置为64M。
innodb_buffer_pool_size = 8196M //这个参数主要作用是缓存innodb表的索引,数据,插入数据时的缓冲。专用mysql服务器设置的大小: 操作系统内存的70%-80%最佳。由于我们的服务器还部署有其他应用,估此处设置为8G。此外,这个参数是非动态的,要修改这个值,需要重启mysqld服务。设置的过大,会导致system的swap空间被占用,导致操作系统变慢,从而减低sql查询的效率。
innodb_additional_mem_pool_size = 16M //用来存放Innodb的内部目录,这个值不用分配太大,系统可以自动调。不用设置太高。通常比较大数据设置16M够用了,如果表比较多,可以适当的增大。如果这个值自动增加,会在error log有中显示的。此处我们设置为16M。
innodb_log_buffer_size = 8M //InnoDB的写操作,将数据写入到内存中的日志缓存中,由于InnoDB在事务提交前,并不将改变的日志写入到磁盘中,因此在大事务中,可以减轻磁盘I/O的压力。通常情况下,如果不是写入大量的超大二进制数据(a lot of huge blobs),4MB-8MB已经足够了。此处我们设置为8M。
max_connections = 800 //最大连接数,根据同时在线人数设置一个比较综合的数字,最大不超过16384。此处我们根据系统使用量综合评估,设置为800。
sort_buffer_size = 2M //是一个connection级参数,在每个connection第一次需要使用这个buffer的时候,一次性分配设置的内存。并不是越大越好,由于是connection级的参数,过大的设置+高并发可能会耗尽系统内存资源。官方文档推荐范围为256KB~2MB,这里我们设置为2M。
read_buffer_size = 2M //(数据文件存储顺序)是MySQL读入缓冲区的大小,将对表进行顺序扫描的请求将分配一个读入缓冲区,MySQL会为它分配一段内存缓冲区,read_buffer_size变量控制这一缓冲区的大小,如果对表的顺序扫描非常频繁,并你认为频繁扫描进行的太慢,可以通过增加该变量值以及内存缓冲区大小提高其性能,read_buffer_size变量控制这一提高表的顺序扫描的效率 数据文件顺序。此处我们设置得比默认值大一点,为2M。
read_rnd_buffer_size = 250K //是MySQL的随机读缓冲区大小,当按任意顺序读取行时(列如按照排序顺序)将分配一个随机读取缓冲区,进行排序查询时,MySQL会首先扫描一遍该缓冲,以避免磁盘搜索,提高查询速度,如果需要大量数据可适当的调整该值,但MySQL会为每个客户连接分配该缓冲区所以尽量适当设置该值,以免内存开销过大。表的随机的顺序缓冲 提高读取的效率。此处设置为跟默认值相似,250KB。
join_buffer_size = 250K //多表参与join操作时的分配缓存,适当分配,降低内存消耗,此处我们设置为250KB。
thread_stack = 256K //每个连接线程被创建时,MySQL给它分配的内存大小。当MySQL创建一个新的连接线程时,需要给它分配一定大小的内存堆栈空间,以便存放客户端的请求的Query及自身的各种状态和处理信息。Thread Cache 命中率:Thread_Cache_Hit = (Connections - Threads_created) / Connections * 100%;命中率处于90%才算正常配置,当出现“mysql-debug: Thread stack overrun”的错误提示的时候需要增加该值。此处我们配置为256K。
binlog_cache_size = 250K // 为每个session 分配的内存,在事务过程中用来存储二进制日志的缓存。作用是提高记录bin-log的效率。没有什么大事务,dml也不是很频繁的情况下可以设置小一点,如果事务大而且多,dml操作也频繁,则可以适当的调大一点。前者建议是1048576 –1M;后者建议是: 2097152 – 4194304 即 2–4M。此处我们根据系统实际,配置为250KB。
table_definition_cache = 400 // 开发模式:从1400设置为400,内存从150M降到90M;服务模式:从1400设置为400,内存从324M降到227M

요약: 기본적으로 기본 구성에서는 메모리 부족 문제가 거의 발생하지 않습니다. 사용 과정에서 많은 사람들이 무리한 매개변수를 설정하거나(고성능을 추구하기 위해 구성과 하드웨어 간의 관계가 균형이 잘 맞지 않음) 실행 중인 인스턴스가 비정상적이어서 메모리가 폭발합니다.

Supongo que te gusta

Origin blog.csdn.net/smooth00/article/details/106614578
Recomendado
Clasificación