- pt-osc(pt-online-schema-change)
- MySQL Online DDL
- Linux性能调优指南
- MySQL 利用rename删除数据的一个小Tips
- MySQL kill 连接
- vmstat
- Referece
MySQL 8.0 Study 010 Tips
学习系列
- pt-osc(pt-online-schema-change)
- MySQL Online DDL
- Linux性能调优指南
- MySQL 利用rename删除数据的一个小Tips
- MySQL kill 连接
- vmstat
pt-osc(pt-online-schema-change)
pt-osc的工作原理
pt-osc的工作原理
1. 创建一个和源表一样表结构的新表
2. 在新表执行DDL语句
3. 在源表创建三个触发器分别对应insert、update、delete操作
4. 从源表拷贝数据到新表,拷贝过程中源表通过触发器把新的DML操作更新到新表中
5. rename源表到old表中,把新表rename为源表,默认最后删除源表
pt-osc工具的限制
pt-osc工具的限制
1. 源表不能有触发器存在(insert、update、delete)
2. 源表必须要有主键或唯一索引,如果没有工具将停止工作
3. 源表有外键,必须使用–alter-foreign-keys-method指定特定的值
4. 如果线上的复制环境过滤器操作过于复杂,工具将无法工作
5. 如果开启复制延迟检查,但主从延迟时,工具将暂停数据拷贝工作
6. 如果开启主服务器负载检查,但主服务器负载较高时,工具将暂停操作
7. 只支持Innodb存储引擎表,且要求服务器上有该表1倍以上的空闲空间。
8. 修改索引、外键、列名时,优先采用online ddl,并指定 ALGORITHM=INPLACE
下载地址:
https://www.percona.com/downloads/percona-toolkit/LATEST/
参数说明
示例:
pt-online-schema-change \
--host="192.168.56.130" \
--port=8032 \
--user="lin" \
--password="mysql" \
--charset="utf8mb4" \
--max-lag=10 \
--recursion-method="hosts" \
--check-interval=2 \
D="test",t="emp" \
--alter="add column newcolumn int(10) default 0" \
--dry-run \
--print \
--execute
--check-slave-log="192.168.56.110" \
pt-online-schema-change --user=lin --password=mysql --host=localhost --port=8032 --alter "add column age int(4) default 0" D=test,t=emp --print --dry-run
mysql> show create table emp\G
*************************** 1. row ***************************
Table: emp
Create Table: CREATE TABLE `emp` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`empno` mediumint unsigned NOT NULL DEFAULT '0',
`ename` varchar(20) NOT NULL DEFAULT '',
`job` varchar(9) NOT NULL DEFAULT '',
`mgr` mediumint unsigned NOT NULL DEFAULT '0',
`hiredate` date NOT NULL,
`sal` decimal(7,2) NOT NULL,
`comn` decimal(7,2) NOT NULL,
`deptno` mediumint unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `idx_emp_empno` (`empno`)
) ENGINE=InnoDB AUTO_INCREMENT=1001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.01 sec)
mysql> select * from emp limit 10;
+----+-------+--------+---------+-----+------------+---------+--------+--------+
| id | empno | ename | job | mgr | hiredate | sal | comn | deptno |
+----+-------+--------+---------+-----+------------+---------+--------+--------+
| 1 | 11 | xFzEoQ | SALEMAN | 1 | 2023-01-29 | 2000.00 | 400.00 | 107 |
| 2 | 12 | OaiMPH | SALEMAN | 1 | 2023-01-29 | 2000.00 | 400.00 | 106 |
| 3 | 13 | OsFR6N | SALEMAN | 1 | 2023-01-29 | 2000.00 | 400.00 | 103 |
| 4 | 14 | YdXZWA | SALEMAN | 1 | 2023-01-29 | 2000.00 | 400.00 | 107 |
| 5 | 15 | GviO2y | SALEMAN | 1 | 2023-01-29 | 2000.00 | 400.00 | 103 |
| 6 | 16 | DwBecb | SALEMAN | 1 | 2023-01-29 | 2000.00 | 400.00 | 109 |
| 7 | 17 | V7DEdX | SALEMAN | 1 | 2023-01-29 | 2000.00 | 400.00 | 107 |
| 8 | 18 | LMsNrE | SALEMAN | 1 | 2023-01-29 | 2000.00 | 400.00 | 106 |
| 9 | 19 | Lj8zjI | SALEMAN | 1 | 2023-01-29 | 2000.00 | 400.00 | 102 |
| 10 | 20 | SUJNEG | SALEMAN | 1 | 2023-01-29 | 2000.00 | 400.00 | 101 |
+----+-------+--------+---------+-----+------------+---------+--------+--------+
10 rows in set (0.00 sec)
mysql>
[root@ol8mysql01 ~]# pt-online-schema-change --user=lin --password=mysql --host=localhost --port=8032 --alter "add column age int(4) default 0" D=test,t=emp --print --dry-run
Operation, tries, wait:
analyze_table, 10, 1
copy_rows, 10, 0.25
create_triggers, 10, 1
drop_triggers, 10, 1
swap_tables, 10, 1
update_foreign_keys, 10, 1
Starting a dry run. `test`.`emp` will not be altered. Specify --execute instead of --dry-run to alter the table.
Creating new table...
CREATE TABLE `test`.`_emp_new` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`empno` mediumint unsigned NOT NULL DEFAULT '0',
`ename` varchar(20) NOT NULL DEFAULT '',
`job` varchar(9) NOT NULL DEFAULT '',
`mgr` mediumint unsigned NOT NULL DEFAULT '0',
`hiredate` date NOT NULL,
`sal` decimal(7,2) NOT NULL,
`comn` decimal(7,2) NOT NULL,
`deptno` mediumint unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `idx_emp_empno` (`empno`)
) ENGINE=InnoDB AUTO_INCREMENT=1001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
Created new table test._emp_new OK.
Altering new table...
ALTER TABLE `test`.`_emp_new` add column age int(4) default 0
Altered `test`.`_emp_new` OK.
Not creating triggers because this is a dry run.
-----------------------------------------------------------
Skipped trigger creation:
Event : DELETE
Name : pt_osc_test_emp_del
SQL : CREATE TRIGGER `pt_osc_test_emp_del` AFTER DELETE ON `test`.`emp` FOR EACH ROW BEGIN DECLARE CONTINUE HANDLER FOR 1146 begin end; DELETE IGNORE FROM `test`.`_emp_new` WHERE `test`.`_emp_new`.`id` <=> OLD.`id`; END
Suffix: del
Time : AFTER
-----------------------------------------------------------
-----------------------------------------------------------
Skipped trigger creation:
Event : UPDATE
Name : pt_osc_test_emp_upd
SQL : CREATE TRIGGER `pt_osc_test_emp_upd` AFTER UPDATE ON `test`.`emp` FOR EACH ROW BEGIN DECLARE CONTINUE HANDLER FOR 1146 begin end; DELETE IGNORE FROM `test`.`_emp_new` WHERE !(OLD.`id` <=> NEW.`id`) AND `test`.`_emp_new`.`id` <=> OLD.`id`; REPLACE INTO `test`.`_emp_new` (`id`, `empno`, `ename`, `job`, `mgr`, `hiredate`, `sal`, `comn`, `deptno`) VALUES (NEW.`id`, NEW.`empno`, NEW.`ename`, NEW.`job`, NEW.`mgr`, NEW.`hiredate`, NEW.`sal`, NEW.`comn`, NEW.`deptno`); END
Suffix: upd
Time : AFTER
-----------------------------------------------------------
-----------------------------------------------------------
Skipped trigger creation:
Event : INSERT
Name : pt_osc_test_emp_ins
SQL : CREATE TRIGGER `pt_osc_test_emp_ins` AFTER INSERT ON `test`.`emp` FOR EACH ROW BEGIN DECLARE CONTINUE HANDLER FOR 1146 begin end; REPLACE INTO `test`.`_emp_new` (`id`, `empno`, `ename`, `job`, `mgr`, `hiredate`, `sal`, `comn`, `deptno`) VALUES (NEW.`id`, NEW.`empno`, NEW.`ename`, NEW.`job`, NEW.`mgr`, NEW.`hiredate`, NEW.`sal`, NEW.`comn`, NEW.`deptno`);END
Suffix: ins
Time : AFTER
-----------------------------------------------------------
Not copying rows because this is a dry run.
INSERT LOW_PRIORITY IGNORE INTO `test`.`_emp_new` (`id`, `empno`, `ename`, `job`, `mgr`, `hiredate`, `sal`, `comn`, `deptno`) SELECT `id`, `empno`, `ename`, `job`, `mgr`, `hiredate`, `sal`, `comn`, `deptno` FROM `test`.`emp` LOCK IN SHARE MODE /*pt-online-schema-change 24422 copy table*/
Not swapping tables because this is a dry run.
Not dropping old table because this is a dry run.
Not dropping triggers because this is a dry run.
DROP TRIGGER IF EXISTS `test`.`pt_osc_test_emp_del`
DROP TRIGGER IF EXISTS `test`.`pt_osc_test_emp_upd`
DROP TRIGGER IF EXISTS `test`.`pt_osc_test_emp_ins`
2023-02-12T23:56:25 Dropping new table...
DROP TABLE IF EXISTS `test`.`_emp_new`;
2023-02-12T23:56:25 Dropped new table OK.
Dry run complete. `test`.`emp` was not altered.
[root@ol8mysql01 ~]#
--execute 之后
mysql> show create table emp\G
*************************** 1. row ***************************
Table: emp
Create Table: CREATE TABLE `emp` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`empno` mediumint unsigned NOT NULL DEFAULT '0',
`ename` varchar(20) NOT NULL DEFAULT '',
`job` varchar(9) NOT NULL DEFAULT '',
`mgr` mediumint unsigned NOT NULL DEFAULT '0',
`hiredate` date NOT NULL,
`sal` decimal(7,2) NOT NULL,
`comn` decimal(7,2) NOT NULL,
`deptno` mediumint unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `idx_emp_empno` (`empno`)
) ENGINE=InnoDB AUTO_INCREMENT=1001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)
mysql> show create table emp\G
*************************** 1. row ***************************
Table: emp
Create Table: CREATE TABLE `emp` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`empno` mediumint unsigned NOT NULL DEFAULT '0',
`ename` varchar(20) NOT NULL DEFAULT '',
`job` varchar(9) NOT NULL DEFAULT '',
`mgr` mediumint unsigned NOT NULL DEFAULT '0',
`hiredate` date NOT NULL,
`sal` decimal(7,2) NOT NULL,
`comn` decimal(7,2) NOT NULL,
`deptno` mediumint unsigned NOT NULL DEFAULT '0',
`age` int DEFAULT '0', ######################------------------》 新增列
PRIMARY KEY (`id`),
KEY `idx_emp_empno` (`empno`)
) ENGINE=InnoDB AUTO_INCREMENT=1001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.01 sec)
mysql> select * from emp limit 10;
+----+-------+--------+---------+-----+------------+---------+--------+--------+------+
| id | empno | ename | job | mgr | hiredate | sal | comn | deptno | age |
+----+-------+--------+---------+-----+------------+---------+--------+--------+------+
| 1 | 11 | xFzEoQ | SALEMAN | 1 | 2023-01-29 | 2000.00 | 400.00 | 107 | 0 |
| 2 | 12 | OaiMPH | SALEMAN | 1 | 2023-01-29 | 2000.00 | 400.00 | 106 | 0 |
| 3 | 13 | OsFR6N | SALEMAN | 1 | 2023-01-29 | 2000.00 | 400.00 | 103 | 0 |
| 4 | 14 | YdXZWA | SALEMAN | 1 | 2023-01-29 | 2000.00 | 400.00 | 107 | 0 |
| 5 | 15 | GviO2y | SALEMAN | 1 | 2023-01-29 | 2000.00 | 400.00 | 103 | 0 |
| 6 | 16 | DwBecb | SALEMAN | 1 | 2023-01-29 | 2000.00 | 400.00 | 109 | 0 |
| 7 | 17 | V7DEdX | SALEMAN | 1 | 2023-01-29 | 2000.00 | 400.00 | 107 | 0 |
| 8 | 18 | LMsNrE | SALEMAN | 1 | 2023-01-29 | 2000.00 | 400.00 | 106 | 0 |
| 9 | 19 | Lj8zjI | SALEMAN | 1 | 2023-01-29 | 2000.00 | 400.00 | 102 | 0 |
| 10 | 20 | SUJNEG | SALEMAN | 1 | 2023-01-29 | 2000.00 | 400.00 | 101 | 0 |
+----+-------+--------+---------+-----+------------+---------+--------+--------+------+
10 rows in set (0.01 sec)
mysql>
可以详细研究下这个触发器的写法
MySQL Online DDL
遵守的一条黄金准则:DDL永远不要在业务高峰期间执行。
MySQL各版本,对于DDL的处理方式是不同的,主要有三种:
Copy Table方式
这是InnoDB最早支持的方式。顾名思义,通过临时表拷贝的方式实现的。新建一个带有新结构的临时表,将原表数据全部拷贝到临时表,然后Rename,完成创建操作。这个方式过程中,原表是可读的,不可写。但是会消耗一倍的存储空间。
Inplace方式
这是原生MySQL 5.5,以及innodb_plugin中提供的方式。所谓Inplace,也就是在原表上直接进行,不会拷贝临时表。相对于Copy Table方式,这比较高效率。原表同样可读的,但是不可写。
Online方式
这是MySQL 5.6以上版本中提供的方式,也是今天我们重点说明的方式。无论是Copy Table方式,还是Inplace方式,原表只能允许读取,不可写。对应用有较大的限制,因此MySQL最新版本中,InnoDB支持了所谓的Online方式DDL。与以上两种方式相比,online方式支持DDL时不仅可以读,还可以写,对于dba来说,这是一个非常棒的改进。
独占元数据锁(exclusive ):在线DDL操作可能必须等待持有表上元数据锁的并发事务提交或回滚。通过show processlist通常看到
State: Waiting for table metadata lock
Online DDL 失败条件
ALGORITHM子句指定了与特定类型的DDL操作或存储引擎不兼容的算法。
LOCK子句指定与特定类型的DDL操作不兼容的低级别锁定(SHARED或NONE)。
在等待表上的排他锁时发生超时,这可能在DDL操作的初始和最终阶段短暂需要。
tmpdir或innodb_tmpdir文件系统的磁盘空间耗尽
并发的DML修改了很多表,以至于临时在线日志的大小超过了innodb_online_alter_log_max_size配置选项的值。这种情况会导致DB_ONLINE_LOG_TOO_BIG错误。
并发DML对原始表定义允许的表进行更改,但新表定义不允许。当MySQL尝试应用并发DML语句的所有更改时,这个操作只会在最后失败。例如,在创建唯一索引时,可以在列中插入重复值,或者在列上创建主键索引时,可以在列中插入NULL值。并发DML所做的更改优先,并且ALTER TABLE操作被有效回滚。
在线DDL变更可能带来的风险,如果操作失败,回滚在线DDL操作的代价可能很高。
修改大表结构执行时间往往不能预估,一般时间较长,可能带来的风险有:修改表结构是表级锁,影响DML写入操作。
修改大表耗时较长,中途写入失败需要进行回滚,回滚这段时间也是不可写入。
修改大表结构容易导致数据库CPU、IO性能损耗,降低MySQL服务性能。
修改大表结构容易造成主从延迟加大,影响业务读取。
1.尽量不要在业务高峰期间进行DDL,即使是online DDL;
2.对于大表(G级别)DDL,最好在测试库上做一遍,预估下时间,不至于到线上执行时心慌手乱;(线上和测试环境数据量差不多)
Linux性能调优指南
Guider是一个免费且开源的,功能强大的全系统性能分析工具,主要以Python for Linux 操作系统编写。
它旨在衡量系统资源使用量并跟踪系统行为,从而使其可以有效分析系统性能问题或进行性能调整。
它显示了大量有关 CPU,内存,每个线程的磁盘使用率,进程,系统功能(用户/内核)的信息。 因此可以非常简单地了解导致系统性能异常或改善整体系统性能的问题。
MySQL 利用rename删除数据的一个小Tips
删除某张表数据
常用delete
方式(DML),数量大的话,时间会很久(redo的产生),可能产生长事务
delete执行时会加锁,默认涉及到的行加写锁和Gap锁,所有相关的行都会被锁定,
如果数据量大直接导致其他连接无法访问该表数据,导致业务无法使用。
执行大批量删除的时候最好使用limit,否则很有可能造成死锁。
如果delete的where语句不在索引上,可以先找主键,然后根据主键删除数据。
另外如果需要删除超大批量数据,可以先删除表中索引,删除数据,之后在重建索引,可以提高速度
而truncate
是ddl,清理很快,但对象是表,不是行记录,不能指定范围
提供rename
方式(DDL),也非常快
常用: 需要删除的数据远远大于不用删除的数据
1.先选择不需要删除的数据,并把它们存在一张相同结构的空表里
2.再重命名原始表,并给新表命名为原始表的原始表名
3.然后归档或者删掉原始表
mysql> show databases like 'test%';
+------------------+
| Database (test%) |
+------------------+
| test |
| test_arch |
+------------------+
2 rows in set (0.01 sec)
mysql>
mysql> create table test_arch.sbtest1 like test.sbtest1;
Query OK, 0 rows affected (0.22 sec)
mysql> rename table test.sbtest1 to test.sbtest1_bak,test_arch.sbtest1 to test.sbtest1,test.sbtest1_bak to test_arch.sbtest1;
Query OK, 0 rows affected (0.24 sec)
mysql> select count(*) from test.sbtest1;
+----------+
| count(*) |
+----------+
| 0 |
+----------+
1 row in set (0.01 sec)
mysql> select count(*) from test_arch.sbtest1;
+----------+
| count(*) |
+----------+
| 1000000 |
+----------+
1 row in set (0.24 sec)
mysql>
MySQL kill 连接
通过拼接语句:
select concat('KILL ',id,';') from information_schema.processlist where user='lin' into outfile '/tmp/kill_lin.txt';
示例:
mysql [localhost:8032] {msandbox} ((none)) > select * from information_schema.processlist;
+----+-----------------+----------------------+------+---------+-------+-----------------------------+----------------------------------------------+
| ID | USER | HOST | DB | COMMAND | TIME | STATE | INFO |
+----+-----------------+----------------------+------+---------+-------+-----------------------------+----------------------------------------------+
| 36 | lin | 192.168.56.120:49566 | test | Sleep | 1151 | | NULL |
| 5 | event_scheduler | localhost | NULL | Daemon | 76510 | Waiting for next activation | NULL |
| 38 | msandbox | localhost | NULL | Query | 0 | executing | select * from information_schema.processlist |
+----+-----------------+----------------------+------+---------+-------+-----------------------------+----------------------------------------------+
3 rows in set (0.00 sec)
mysql [localhost:8032] {msandbox} ((none)) > select concat('KILL ',id,';') from information_schema.processlist where user='lin';
+------------------------+
| concat('KILL ',id,';') |
+------------------------+
| KILL 36; |
+------------------------+
1 row in set (0.00 sec)
mysql [localhost:8032] {msandbox} ((none)) > select concat('KILL ',id,';') from information_schema.processlist where user='lin' into outfile '/tmp/kill_lin.txt';
Query OK, 1 row affected (0.00 sec)
mysql [localhost:8032] {msandbox} ((none)) > source /tmp/kill_lin.txt
Query OK, 0 rows affected (0.01 sec)
mysql [localhost:8032] {msandbox} ((none)) > select * from information_schema.processlist;
+----+-----------------+-----------+------+---------+-------+-----------------------------+----------------------------------------------+
| ID | USER | HOST | DB | COMMAND | TIME | STATE | INFO |
+----+-----------------+-----------+------+---------+-------+-----------------------------+----------------------------------------------+
| 5 | event_scheduler | localhost | NULL | Daemon | 76591 | Waiting for next activation | NULL |
| 38 | msandbox | localhost | NULL | Query | 0 | executing | select * from information_schema.processlist |
+----+-----------------+-----------+------+---------+-------+-----------------------------+----------------------------------------------+
2 rows in set (0.00 sec)
mysql [localhost:8032] {msandbox} ((none)) >
或者通过mysqladmin过滤出id去kill
[root@ol8mysql01 msb_8_0_32]# mysqladmin -ulin -pmysql processlist
mysqladmin: [Warning] Using a password on the command line interface can be insecure.
+----+-----------------+----------------------+------+---------+-------+-----------------------------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-----------------+----------------------+------+---------+-------+-----------------------------+------------------+
| 5 | event_scheduler | localhost | | Daemon | 78021 | Waiting for next activation | |
| 39 | lin | 192.168.56.120:59908 | test | Sleep | 1311 | | |
| 50 | lin | localhost | | Query | 0 | init | show processlist |
+----+-----------------+----------------------+------+---------+-------+-----------------------------+------------------+
[root@ol8mysql01 msb_8_0_32]# mysqladmin -ulin -pmysql processlist |awk -F "|" '{print $2}'|xargs -n 1
mysqladmin: [Warning] Using a password on the command line interface can be insecure.
Id
5
39
51
[root@ol8mysql01 msb_8_0_32]#
类似: #杀掉锁定的MySQL连接
for id in `mysqladmin processlist|grep -i locked|awk '{print $1}'`
do
mysqladmin kill ${id}
done
vmstat
每隔 5 秒输出 1 组数据
# vmstat 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 2316 389768 4204 962384 0 0 11 9 167 346 0 3 97 0 0
0 0 2316 389768 4204 962424 0 0 0 0 162 311 0 3 97 0 0
0 0 2316 389768 4204 962424 0 0 0 0 163 310 0 3 97 0 0
0 0 2316 389768 4204 962424 0 0 0 0 154 307 0 3 97 0 0
0 0 2316 389768 4204 962424 0 0 0 0 154 307 0 3 97 0 0
每列的含义:
• cs(context switch) 是每秒上下文切换的次数。
• in(interrupt) 则是每秒中断的次数。
• r(Running or Runnable) 是就绪队列的长度,也就是正在运行和等待 CPU 的进程数。
• b(Blocked) 则是处于不可中断睡眠状态的进程数。
vmstat
只给出了系统总体的上下文切换情况,要想查看每个进程的详细情况,
需要使用pidstat -w
选项,就可以查看每个进程上下文切换的情况了
[root@ol8mysql01 ~]# pidstat -w 5
Linux 5.4.17-2136.309.4.el8uek.x86_64 (ol8mysql01) 02/13/2023 _x86_64_ (1 CPU)
09:49:25 PM UID PID cswch/s nvcswch/s Command
09:49:30 PM 0 9 0.60 0.00 ksoftirqd/0
09:49:30 PM 0 10 1.40 0.00 rcu_sched
09:49:30 PM 0 11 0.40 0.00 migration/0
09:49:30 PM 0 23 0.20 0.00 khugepaged
09:49:30 PM 0 418 0.20 0.00 kworker/0:1H-kblockd
09:49:30 PM 81 759 0.20 0.00 dbus-daemon
09:49:30 PM 0 25038 6.80 0.20 kworker/0:1-events_power_efficient
09:49:30 PM 0 25079 0.20 0.40 pidstat
09:49:30 PM UID PID cswch/s nvcswch/s Command
09:49:35 PM 0 9 0.60 0.00 ksoftirqd/0
09:49:35 PM 0 10 1.40 0.00 rcu_sched
09:49:35 PM 0 11 0.20 0.00 migration/0
09:49:35 PM 81 759 0.20 0.00 dbus-daemon
09:49:35 PM 0 23633 28.40 0.00 kworker/u2:1-events_unbound
09:49:35 PM 0 24201 2.80 0.40 sshd
09:49:35 PM 0 25038 6.80 0.00 kworker/0:1-events
09:49:35 PM 0 25079 0.20 28.60 pidstat
每列的含义:
• PID: 进程号
• cswch/s: 表示每秒自愿上下文切换(voluntary context switches)的次数
• nvcswch/s: 表示每秒非自愿上下文切换(non voluntary context switches)的次数。
自愿上下文切换: 是指进程无法获取所需资源,导致的上下文切换。比如说,I/O、内存等系统资源不足时,就会发生自愿上下文切换。
非自愿上下文切换: 则是指进程由于时间片已到等原因,被系统强制调度,进而发生的上下文切换。比如说,大量进程都在争抢 CPU 时,就容易发生非自愿上下文切换。
平均负载问题排查与案例假设 通过使用iostat、mpstat、pidstat工具,找出平均负载升高的根源 准备
预先安装 stress 和 sysstat 包,如 apt install stress sysstat
• stress: 一个 Linux 系统压力测试工具,可以用作异常进程模拟平均负载升高的场景。
• sysstat: 包含了常用的 Linux 性能工具,用来监控和分析系统的性能。案例会用到这个包的两个命令 mpstat 和 pidstat。
• mpstat 一个常用的多核 CPU 性能分析工具,用来实时查看每个 CPU 的性能指标,以及所有 CPU 的平均指标。
• pidstat 一个常用的进程性能分析工具,用来实时查看进程的 CPU、内存、I/O 以及上下文切换等性能指标。
场景一:CPU 密集型进程
1. 在第一个终端运行 stress --cpu 1 --timeout 600 命令,模拟一个 CPU 使用率 100% 的场景 (不想模拟的可以忽略):
2. 在第二个终端运行 watch -d uptime 查看平均负载的变化情况 (-d 参数表示高亮显示变化的区域)
3. 在第三个终端运行 mpstat -P ALL 5 查看 CPU 使用率的变化情况 (-P ALL 表示监控所有 CPU,后面数字 5 表示间隔 5 秒后输出一组数据)
从 [2] 可以看到,1 分钟的平均负载会慢慢增加到 1.00,而从 [3] 中还可以看到,正好有一个 CPU 的使用率为 100%,但它的 iowait 只有 0。这说明,平均负载的升高正是由于 CPU 使用率为 100% 。
1. 使用 pidstat -u 5 1 来查找哪个进程导致了 CPU 使用率为 100% (间隔 5 秒后输出一组数据)
场景二:I/O 密集型进程
1. 运行 stress -i 1 --timeout 600 命令,但这次模拟 I/O 压力,即不停地执行 sync
2. 在第二个终端运行 watch -d uptime 查看平均负载的变化情况 (-d 参数表示高亮显示变化的区域)
3. 在第三个终端运行 mpstat -P ALL 5 1 查看 CPU 使用率的变化情况 (-P ALL 表示监控所有 CPU,后面数字 5 表示间隔 5 秒后输出一组数据)
4.
从 [3] 可以看到,1 分钟的平均负载会慢慢增加到 1.06,其中一个CPU的系统的CPU使用率升高到了 23.87,而 iowait 高达 67.53%。这说明,平均负载的升高是由于 iowait 的升高。
1. 使用 pidstat -u 5 1 来查找哪个进程导致 iowait 这么高 (间隔 5 秒后输出一组数据)
Referece
参考:
Have a good work&life! 2023/02 via LinHong