CategoryResourceRepost/极客时间专栏/MySQL 必知必会/优化篇/26 | 如何充分利用系统资源?.md
louzefeng d3828a7aee mod
2024-07-11 05:50:32 +00:00

289 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<audio id="audio" title="26 | 如何充分利用系统资源?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/9d/7b/9d01c6892b26ab1305ba12b63ca6607b.mp3"></audio>
你好,我是朱晓峰。
内存和CPU都是有限的资源因此把它们的作用发挥到极致对提高应用的承载能力来说至关重要。
磁盘读写需要计算位置、发出读写指令等这些都要消耗CPU资源很容易成为提升系统效能的瓶颈。
如果采取“先把数据放在内存然后集中写入磁盘”的办法可以节省CPU资源和磁盘读取的时间但是也会面临系统故障时会丢失数据的风险相反如果每次都写入磁盘数据最安全但是频繁的磁盘读写会导致系统效率低下。这就需要我们提升优化资源配置的能力。
今天,我就给你介绍一下优化系统配置的方法,同时还会讲解系统自带的监控工具,从而帮助你合理配置系统资源,精准发现系统资源的瓶颈,进一步提升你处理大并发、大数据的能力。
## 优化系统资源配置
对CPU资源的掌控关系到系统整体的成败。因为CPU资源是系统最核心的资源无可替代而且获取成本高。如果应用无法控制CPU的使用率就有可能是失败的不管你的界面多么人性化功能多么强大。
因此,我们需要管理好系统配置,把资源效率提升到极致。**系统参数控制着资源的配置,调整系统参数的值,可以帮助我们提升资源的利用效率**。
我来借助一个小例子,给你介绍下怎么通过对系统变量进行调优,来提升系统的整体效率。
我曾参与过一个点餐系统应用的开发其实就是一个为客户提供点餐服务的应用类似于美团。商家购买服务入住平台开通之后商家可以在系统中录入自己能够提供的各类餐食品种客户通过手机App、微信小程序等点餐商家接到订单以后进行制作并根据客户需求提供堂食或者送餐服务。
系统中包括订单表orderlist、客户信息表clientlist和客户储值表clientdeposit
订单表:
<img src="https://static001.geekbang.org/resource/image/94/da/946066e2e0124ba52788bb4cb31261da.jpeg" alt="">
客户信息表:
<img src="https://static001.geekbang.org/resource/image/31/72/31f03880ac8f5c3d4e048828c7240672.jpeg" alt="">
客户储值表:
<img src="https://static001.geekbang.org/resource/image/2d/5f/2dcbe35e96f520673018826ba590cd5f.jpeg" alt="">
刚刚上线的时候系统运行状态良好。但是随着入住的商家不断增多使用系统的用户量越来越多每天的订单数据达到了2万条以上。这个时候系统开始出现问题CPU使用率不断飙升。终于有一天午餐高峰的时候CPU使用率达到99%,这实际上就意味着,系统的计算资源已经耗尽,再也无法处理任何新的订单了。换句话说,系统已经崩溃了。
这个时候我们想到了对系统参数进行调整因为参数的值决定了资源配置的方式和投放的程度。为了解决这个问题我们一共调整了3个系统参数分别是**InnoDB_flush_log_at_trx_commit、InnoDB_buffer_pool_size**、**InnoDB_buffer_pool_instances。**
接下来,我就给你讲一讲怎么对这三个参数进行调整。
### 1. 调整系统参数InnoDB_flush_log_at_trx_commit
这个参数适用于InnoDB存储引擎。因为刚刚的表用的存储引擎都是InnoDB因此这个参数对我们系统的效能就有影响。需要注意的是如果你用的存储引擎不是InnoDB调整这个参数对系统性能的提升就没有什么用了。
这个参数存储在MySQL的配置文件my.ini里面默认的值是1意思是每次提交事务的时候都把数据写入日志并把日志写入磁盘。**这样做的好处是数据安全性最佳,不足之处在于每次提交事务,都要进行磁盘写入的操作**。在大并发的场景下过于频繁的磁盘读写会导致CPU资源浪费系统效率变低。
这个参数的值还有2个可能的选项分别是0和2。其中0表示每隔1秒将数据写入日志并将日志写入磁盘2表示每次提交事务的时候都将数据写入日志但是日志每间隔1秒写入磁盘。
最后我们把这个参数的值改成了2。这样一来就不用每次提交事务的时候都启动磁盘读写了在大并发的场景下可以改善系统效率降低CPU使用率。即便出现故障损失的数据也比较小。0虽然效率更高一些但是数据安全性方面不如2。
### 2. 调整系统参数InnoDB_buffer_pool_size
这个参数的意思是InnoDB存储引擎使用缓存来存储索引和数据。这个值越大可以加载到缓存区的索引和数据量就越多需要的磁盘读写就越少。
因为我们的MySQL服务器是数据库专属服务器只用来运行MySQL数据库服务没有其他应用了而我们的计算机是64位机内存也有128G。于是我们把这个参数的值调整为64G。这样一来磁盘读写次数可以大幅降低我们就可以充分利用内存释放出一些CPU的资源。
### 3. 调整系统参数InnoDB_buffer_pool_instances
这个参数的意思是将InnoDB的缓存区分成几个部分这样一来就可以提高系统的并行处理能力因为可以允许多个进程同时处理不同部分的缓存区。这就好比买电影票如果大家都挤在一个窗口、一个接一个地进行交易效率肯定是很慢的。如果一次开很多售票窗口多笔交易同时进行那速度就快得多了。
我们把InnoDB_buffer_pool_instances的值修改为64意思就是把InnoDB的缓存区分成64个分区这样就可以同时有多个进程进行数据操作CPU的效率就高多了。
修改好了系统参数的值我们需要重新保存MySQL的配置文件my.ini并且重启MySQL数据库服务器。
这里有个坑你要注意由于my.ini文件是文本格式文件你完全可以用记事本对文件进行修改操作。但是如果你只是简单地进行保存就会发现MySQL服务器停止之后再次启动时没有响应服务器起不来了。其实这就是文件的码制出了问题。
**记事本保存文件默认的码制是UTF-8但配置文件的码制必须是ANSI才行**。所以,当**你修改完MySQL的配置文件my.ini之后保存的时候记得用ANSI的格式**。如下图所示:
<img src="https://static001.geekbang.org/resource/image/d3/9e/d315593c7d73ccc706fd0ccbc2977a9e.png" alt="">
经过我们对系统参数的调整重启MySQL服务器之后系统效率提高了CPU资源的使用率下来了系统得以正常运行。
咱们来小结下。CPU资源是系统的核心资源获取成本非常高。CPU的特点就是阻塞只要CPU一开始计算就意味着等待。遇到CPU资源不足的问题可以从2个思路去解决
1. 疏通拥堵路段,消除瓶颈,让等待的时间更短;
1. 开拓新的通道,增加并行处理能力。
刚刚的调优思路其实就是围绕着这2个点展开的。如果遇到CPU资源不足的问题我建议你也从这2个角度出发去思考解决办法。
## 如何利用系统资源来诊断问题?
在刚刚的例子中我提到了解决CPU资源不足需要消除瓶颈。而消除瓶颈的第一步就是要发现它。如何发现呢幸运的是MySQL提供了很好的工具**Performance Schema**。
这是一种专门用来监控服务器执行情况的存储引擎它会把监控服务器执行情况的数据记录在系统自带的数据库performance_schema中。我们可以利用监控的数据对服务器中执行查询的问题进行诊断。
我还是以刚刚的那个点餐系统为例,来解释一下。
当我们调整完系统参数之后系统恢复了运行。可是随着数据量的不断增大单日订单量超过20万我们再次遇到了问题CPU飙升到99%系统无法工作了。这个时候我们就可以利用performance_schema记录的监控数据来发现问题。
我先讲一讲怎么让Performance Schema监控查询执行事件并且把我们需要的监控数据记录下来。
### 如何启用系统监控?
系统数据库performance_schema中的表setup_instruments和setup_consumers中的数据是启用监控的关键。
setup_instruments保存的数据表示哪些对象发生的事件可以被系统捕获在MySQL中把这些事件称作信息生产者
我们可以通过下面的代码来查看一下当前MySQL会监控哪些事件的信息
```
mysql&gt; SELECT NAME,ENABLED,TIMED
-&gt; FROM performance_schema.setup_instruments
-&gt; LIMIT 1,10;
+---------------------------------------------------------+---------+-------+
| NAME | ENABLED | TIMED |
+---------------------------------------------------------+---------+-------+
| wait/synch/mutex/sql/TC_LOG_MMAP::LOCK_tc | YES | YES |
| wait/synch/mutex/sql/MYSQL_BIN_LOG::LOCK_commit | YES | YES |
| wait/synch/mutex/sql/MYSQL_BIN_LOG::LOCK_commit_queue | YES | YES |
| wait/synch/mutex/sql/MYSQL_BIN_LOG::LOCK_done | YES | YES |
| wait/synch/mutex/sql/MYSQL_BIN_LOG::LOCK_flush_queue | YES | YES |
| wait/synch/mutex/sql/MYSQL_BIN_LOG::LOCK_index | YES | YES |
| wait/synch/mutex/sql/MYSQL_BIN_LOG::LOCK_log | YES | YES |
| wait/synch/mutex/sql/MYSQL_BIN_LOG::LOCK_binlog_end_pos | YES | YES |
| wait/synch/mutex/sql/MYSQL_BIN_LOG::LOCK_sync | YES | YES |
| wait/synch/mutex/sql/MYSQL_BIN_LOG::LOCK_sync_queue | YES | YES |
+---------------------------------------------------------+---------+-------+
10 rows in set (0.00 sec)
```
表的内容有很多为了方便演示这里我只查询了前10条数据。在这个表中你要注意3个字段。
- NAME表示事件的名称
- ENABLED表示是否启用了对这个事件的监控
- TIMED表示是否收集事件的时间信息。
这里我们启用所有事件的监控,以便诊断问题:
```
UPDATE performance_schema.setup_instruments
SET ENABLED = 'YES', TIMED = 'YES';
```
setup_instruments表中的数据指定了是否对某一事件进行监控而setup_consumers这个表保存的数据则指定了是否保存监控事件发生的信息。在MySQL中Performance Schema把监控到的事件信息存入performance_schema中的数据表中这些数据表保存了事件的监控信息扮演了事件监控信息消费者的角色被称为消费者。
下面我们来修改设置使系统保存所有事件信息并了解一下setup_consumers这个表里都能够控制保存哪些事件的信息。我在数据旁边加了注释来说明保存的事件信息的种类。
```
UPDATE performance_schema.setup_consumers
SET ENABLED = 'YES'
mysql&gt; SELECT *
-&gt; FROM performance_schema.setup_consumers
-&gt; ;
+----------------------------------+---------+
| NAME | ENABLED |
+----------------------------------+---------+
| events_stages_current | YES | -- 当前阶段
| events_stages_history | YES | -- 阶段历史
| events_stages_history_long | YES | -- 阶段长历史
| events_statements_current | YES | -- 当前语句
| events_statements_history | YES | -- 历史语句
| events_statements_history_long | YES | -- 长历史语句
| events_transactions_current | YES | -- 当前事务
| events_transactions_history | YES | -- 历史事务
| events_transactions_history_long | YES | -- 长历史事务
| events_waits_current | YES | -- 当前等待
| events_waits_history | YES | -- 等待历史
| events_waits_history_long | YES | -- 等待长历史
| global_instrumentation | YES |
| thread_instrumentation | YES |
| statements_digest | YES |
+----------------------------------+---------+
15 rows in set (0.01 sec)
```
更新之后,所有种类事件信息的保存都已经启用。这样一来,所有查询事件都能够被监控,事件的相关信息也都能够被记录下来了。如果查询中出现问题,那么,异常信息就会被记录下来。接下来,我们就利用系统监控到的信息来诊断一下问题。
### 利用监控信息诊断问题
为了利用保存下来的监控事件信息来诊断系统问题,我先介绍一下几个保存监控信息数据的系统数据表。
**第1个表是performance_schema.events_statements_current。**
这个表中记录的是当前系统中的查询事件。表中的每一行对应一个进程,一个进程只有一行数据,显示的是每个进程中被监控到的查询事件。
**第2个表是performance_schema.events_statements_history。**
这个表中记录了系统中所有进程中最近发生的查询事件。这个表中包含的查询事件都是已经完成了的。另外,表中可以为每个进程保存的最大记录数由系统变量决定。
下面的代码可以查询当前系统中可以为每个进程保存的最大记录数的值:
```
mysql&gt; show variables like '%performance_schema_events_statements_history_size%'
-&gt; ;
+---------------------------------------------------+-------+
| Variable_name | Value |
+---------------------------------------------------+-------+
| performance_schema_events_statements_history_size | 10 | -- 最多10条
+---------------------------------------------------+-------+
1 row in set, 1 warning (0.01 sec)
```
结果显示当前的设定是这个表中为每个进程保留最多10条记录。
**我要说的第3个表是performance_schema.events_statements_history_long。**
这个表中记录了系统中所有进程中最近发生的查询事件,表中包含的查询事件都是已经完成了的。同时,这个表中可以保存的记录数由系统变量决定。下面的代码用来查询当前系统设置的这个表可以保存的最大记录数。
```
mysql&gt; show variables like '%performance_schema_events_statements_history_long_size%'
-&gt; ;
+--------------------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------------------+-------+
| performance_schema_events_statements_history_long_size | 10000 |
+--------------------------------------------------------+-------+
1 row in set, 1 warning (0.00 sec)
```
结果显示当前系统的设定是这个表中最多保留10000条记录。
知道了这几个保存事件信息的系统数据表,我们重新回到项目中来,看看如何利用这几个系统数据表中的信息,来发现问题。
我们先用下面的语句来看一下哪些查询消耗的时间多。这个表的数据量很大为了方便你查看我只让它显示最初的2行数据。
```
mysql&gt; SELECT
-&gt; TRUNCATE(TIMER_WAIT / 1000000000000, 6) AS duration, -- 计算查询的时长,单位是微微秒,需要转换成秒
-&gt; sql_text,
-&gt; EVENT_ID
-&gt; FROM
-&gt; performance_schema.events_statements_history_long
-&gt; WHERE
-&gt; TRUNCATE(TIMER_WAIT / 1000000000000, 6) &lt;&gt; 0
-&gt; AND sql_text IS NOT NULL
-&gt; ORDER BY TRUNCATE(TIMER_WAIT / 1000000000000, 6) DESC
-&gt; LIMIT 1,2;
+----------+---------------------------------+----------+
| duration | sql_text | EVENT_ID |
+----------+---------------------------------+----------+
| 137.2529 | select count(*) from demo.trans | 17 |
| 137.2420 | select count(*) from demo.trans | 907 |
+----------+---------------------------------+----------+
2 rows in set (0.00 sec)
```
结果显示持续时间最长的2个事件分别是事件编号为17和907的事件。这样一来我们就可以发现到底是哪些查询消耗了最多的CPU资源就可以有针对性地进行优化了。
下面我具体给你解释一下这个查询。
1. 字段TIMER_WAIT表示这个查询消耗了多少时间单位是微微秒也就是万亿分之一秒。
1. TRUNCATE(X,D)函数表示给X保留D位小数注意这个函数是直接截取没有四舍五入。
1. 字段sql_text表示执行的SQL语句的内容。
1. EVENT_ID表示事件编号。
这个查询的意思是按照查询花费时间多少排序查询出花费时间最多的2个查询的事件编号、执行时长和查询的内容。
通过查询这个表的内容我们发现有大量的查询出现在执行时间较长的列表中这些查询都涉及数据表demo.clientdeposit。
为了支持顾客储值消费我们新加了这个表但是这个表的使用频率很高数据量较大我们却忘记了创建索引这就导致了CPU资源不足。确定了问题解决起来就很容易了创建了索引之后CPU资源消耗直接下降到了20%左右。
总之Performance Schema是一种很好的工具可以监控到MySQL服务器内部执行的信息。如果你遇到查询中出现难以解决的问题就可以调取数据库performance_shema中的监控数据分析服务器中的执行情况从而定位和解决问题。
## 总结
这节课,我们学习了通过系统参数来配置资源、提高查询效率的方法。
- 系统参数InnoDB_flush_log_at_trx_commit适用于InnoDB存储引擎。默认的值是1意思是每次提交事务的时候都把数据写入日志并把日志写入磁盘。0表示每隔1秒将数据写入日志并将日志写入磁盘。2表示每次事务提交的时候将数据写入日志但是日志每隔1秒写入磁盘。
- 系统参数InnoDB_buffer_pool_size表示InnoDB存储引擎使用多少缓存来存储索引和数据。这个值越大可以加载到缓存区的索引和数据量就越多需要的磁盘读写就越少。
- 系统参数InnoDB_buffer_pool_instances的意思是将InnoDB的缓存区分成几个部分可以提高系统的并行处理能力。
而且我还给你介绍了怎么通过使用Performance Schema来监控服务器运行中的事件。系统数据库performance_schema中的表setup_instruments和setup_consumers中的数据是启用监控的关键。
- setup_instruments保存的数据表示哪些对象发生的事件可以被系统捕获。
- setup_consumers保存的数据用来控制保存哪些事件的信息。
我们可以通过修改这两个表的内容来开启系统监控,并且把监控数据保存到相应的数据表中,方便我们对查询的执行情况进行诊断。
最后我还想额外给你一个建议如果你遇到了因为无法控制CPU的使用率而导致系统崩溃的情况首先应该想到的**是应用本身有缺陷**然后找到自身的问题并加以解决而不是增加系统资源的投入采购功能强大的CPU和扩大内存等。
原因有2个资源永远是有限的如何在有限的资源前提下提高系统的承载能力是我们应该首先考虑的资源的投入都是经过谨慎评估的除非你有充足的理由确信同等条件下你开发的应用承载能力已经超过了业界最高水平否则就说明应用本身还有提升的空间。
所以课下你一定要把今天的内容多学几遍一定要掌握通过MySQL自身的事件监控机制来诊断问题的方法它可以帮助你找到真正的瓶颈。
## 思考题
前面提到我把InnoDB_flush_log_at_trx_commit的值改成了2因为0虽然效率更高一些但是在数据安全性方面不如2。你知道为什么0的效率更高一些但是数据安全性却不如2吗
欢迎在留言区写下你的思考和答案,我们一起交流讨论。如果你觉得今天的内容对你有所帮助,也欢迎你分享给你的朋友或同事,我们下节课见。