当前位置:   article > 正文

mysql调优-mysql参数和状态概览_mysql threads

mysql threads

mysql参数和状态

执行状态

列出MySQL服务器运行各种状态值

show global status;
  • 1

常用变量名如下:

  • 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 服务器工作了多长时间,单位秒。

配置参数

查询MySQL服务器配置信息语句

show variables;
  • 1

连接数

最大连接数

允许客户端的最大连接数

MySQL [(none)]> show variables like 'max_connections';
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id:    99652
Current database: *** NONE ***

+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| max_connections | 10000 |
+-----------------+-------+
1 row in set (0.00 sec)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

这台MySQL服务器最大连接数是10000,然后查询一下服务器响应的最大连接数:

MySQL [(none)]> show global status like 'Max_used_connections';
+----------------------+-------+
| Variable_name        | Value |
+----------------------+-------+
| Max_used_connections | 220   |
+----------------------+-------+
1 row in set (0.00 sec)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

MySQL服务器过去的最大连接数是220,没有达到服务器连接数上限10000,应该没有出现1040错误(Too many connections提示意思是说连接过多),比较理想的设置是:
  Max_used_connections / max_connections * 100% ≈ 85%
  最大连接数占上限连接数的85%左右最优,如果发现比例在10%以下,MySQL服务器连接数上限设置的过高了。

查看连接

查看目前所有的连接

show processlist
  • 1

show processlist的Info过长时会截取(100字符),且也不支持按属性过滤,不利于分析。

show full processlist会显示所有信息,但展现太占空间、不利于批量分析。

processlist表结构

MySQL [(none)]> desc information_schema.processlist;
+---------+---------------------+------+-----+---------+-------+
| Field   | Type                | Null | Key | Default | Extra |
+---------+---------------------+------+-----+---------+-------+
| ID      | bigint(21) unsigned | NO   |     | 0       |       |
| USER    | varchar(32)         | NO   |     |         |       |
| HOST    | varchar(64)         | NO   |     |         |       |
| DB      | varchar(64)         | YES  |     | NULL    |       |
| COMMAND | varchar(16)         | NO   |     |         |       |
| TIME    | int(7)              | NO   |     | 0       |       |
| STATE   | varchar(64)         | YES  |     | NULL    |       |
| INFO    | longtext            | YES  |     | NULL    |       |
+---------+---------------------+------+-----+---------+-------+
8 rows in set (0.00 sec)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

属性说明

名词约定:MySQL中一般用Thread(线程)表示一个执行计划/任务,本文为了便于从应用角度理解,用连接代替线程作为表述。 官方依据:Each connection to mysqld runs in a separate thread. MySQL版本:5.6/5.7/8.x

  • ID ID不难理解,就是mysql连接的唯一标识。该标识用途有两个:

操作或过滤特定连接,比如使用kill命令时 定位问题连接。比如查看事务、锁等时,其中都会有一个thread_id,这个就是对应的processlist.ID;通过这个关系,在分析复杂问题时可定位到具体连接 在下一篇博文的《事务/锁相关统计》中有提到

  • USER 字面意思,创建数据库连接的用户。 即可用于用户行为追踪,也可用于用户行为统计。

HOST 创建连接的服务器,一般由服务器IP+端口组成; 实际使用中,往往只有IP部分有用,比如按请求来源进行统计,使用时可以截取:substring(host, 1, instr(host, “:”)-1)。

  • DB 该连接执行SQL的数据库。 有些连接创建时可能未指定数据库,因此该项可能为空。
  • COMMAND 最早以为是“命令”,用的时候才发现表示连接状态。

该项值常见的有Sleep(休眠)、Query(查询)、Connect(连接),其他值一般也不常见/用,有兴趣可参考官方文档说明2。

  • TIME 连接的在当前状态(STATE)的持续时间,单位为秒。 注意是“当前状态的持续时间”。

官方文档释义1:

The time in seconds that the thread has been in its current state. For a slave SQL thread, the value is the number of seconds between the timestamp of the last replicated event and the real time of the slave machine.

一般状态变化非常快、在每个状态持续时间很短,如果持续多秒,说明就出现了问题。所以这个设定,让时间成为判断SQL是否正常的关键要素。如果TIME是连接存在的时间,那么就失去了这个意义了。

有朋友在主从模式下,遇到过时间为负数的情况,可参考这篇文章:https://www.jianshu.com/p/9f180c37d983

  • STATE SQL执行的状态。

An action, event, or state that indicates what the thread is doing. 1

该项非常重要,往往会指出问题所在。 STATE要结合TIME来使用,即持续的时间比较长,则有问题的概率越大。

Most states correspond to very quick operations. If a thread stays in a given state for many seconds, there might be a problem that needs to be investigated.1

STATE的值比较多,建议阅读完本文后,阅读《MySQL性能分析 - (三) processlist的state属性详解》来深入了解

  • INFO 正在执行的完整SQL语句。

在实际分析中,该项也是很重要的信息。

定位到SQL,具体定位业务代码、彻底解决问题也就不远了 可通过提取多个SQL特征,进行合并统计

更加详细内容,请参考:https://www.yisu.com/zixun/448114.html

超时参数

show variables  like '%timeout%'
+-----------------------------+----------+
| connect_timeout             | 10       |    ##连接超时,10秒
| delayed_insert_timeout      | 300      |    ##延迟插入超时时间,300秒
| have_statement_timeout      | YES      |    ##
| innodb_flush_log_at_timeout | 1        |    ##刷新redo log buffer超时时间,1秒
| innodb_lock_wait_timeout    | 120      |    ##事务等待获取资源等待的最长时间,超过这个时间还未分配到资源则会返回应用失败,120秒
| innodb_rollback_on_timeout  | ON       |
| interactive_timeout         | 28800    |    ##mysql客户端交互连接超时时间,默认8小时,用于控制sleep超时
| lock_wait_timeout           | 31536000 |    ##主要针对DDL产生的metadata locks超时时间
| net_read_timeout            | 60       |    ##网络读取数据超时时间,60秒
| net_write_timeout           | 60       |    ##为网络写入数据超时间60秒
| rpl_stop_slave_timeout      | 31536000 |    ##停止从库服务超时时间
| slave_net_timeout           | 60       |    ##slave网络超时时间
| thread_pool_idle_timeout    | 60       |
| wait_timeout                | 28800    |    ##jdbc/odbc连接超时时间,默认8小时,用于控制sleep超时
+-----------------------------+----------+
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

线程

MySQL在架构上分为Server层和存储引擎层,它们的线程模型略有不同。在Server层每个连接对应一个线程,几乎没有线程并发控制,只要还可以创建连接,就会创建对应的线程,这些线程之间并发执行。而在InnoDB存储引擎层,为了防止并发线程过多,线程切换开销过大,可以限制并发线程数,从而提高性能

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VUWgQELE-1643101235968)(http://media-hk01.oss-cn-hongkong.aliyuncs.com/images%2Fliaomin%40jieztech.com%2Fmysql%E4%BC%98%E5%8C%96.md%2F5ac8ad10-e5c4-40f9-935e-c4a09a2f2b12_image.png?Expires=1800154570&OSSAccessKeyId=LTAIXyAB35iFpHM0&Signature=%2BCjvMB2RMoX%2FlFeC6HDY8WiK8ro%3D&x-oss-process=image%2Fformat%2Cpng)]

Server层线程模型

MySQL的工作模式是为每个连接分配一个线程,当客户端和MySQL服务器建立TCP连接之后,MySQL服务器就会给这个连接分配一个线程,当这个连接收到SQL时,对应的线程就执行这个SQL,而当SQL执行结束后,这个线程就去Sleep,等待客户端的新请求。这个线程会一直存活,直到客户端退出登录,并关闭连接,这个线程才会退出(或者进入MySQL的ThreadCache)

InnoDB 线程模型

通过前面的叙述可以我们知道,MySQL在Server层根本就没有对并发线程的数量进行控制,当MySQL的并发连接数增加时,就会导致对应的线程数量增加。这样可能把机器的大量CPU资源都耗费在线程切换上了,导致性能急剧下降。InnoDB为了缓解这种情况,通过设置系统变量set global innodb_thread_concurrency = x可以控制内部并发线程的数量,也就是最多允许innodb_thread_concurrency个线程同时在InnoDB内部运行。也就是说在Server层可以创建很多线程,但是到了InnoDB内部,只会有少量线程并发执行,其他线程都处于sleep状态。示意图如下:

Thread Pool

Thread Pool在MySQL官方是以Enterprise版plugin的形式出现的,而在MariaDB和Percona都是侵入MySQL源码的,它们在实现上大同小异,并没有本质区别。这里主要分析Percona的Thread Pool实现。如前文所述,MySQL Server在每当有一个客户端连接进来时,都会创建一个线程来服务这个连接。而Thread Pool的思想是将连接和线程解绑,连接和线程之间不再是一对一的关系,而是m对n的关系。具体做法是通过epoll监听网络端口,一旦有客户端数据需要处理,就把这个连接放入队列,让后台线程池从队列中取出连接信息,并服务这个连接。也就是说MySQL的连接数可以非常高,但是线程数量可以只有几十个。通过Thread Pool彻底解决线程数量过多,导致性能下降的问题,示意图如下:

具体源码分析参考(内容参考):http://www.wangyulong.cn/blog/1534473143/

线程相关语句

参数和状态

查看线程参数(线程池大小):

MySQL [(none)]> show variables like '%thread_cach%'
    -> ;
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| thread_cache_size | 200   |
+-------------------+-------+
1 row in set (0.00 sec)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

其他参数

  • thread_handling 运用Thread_Cache处理连接的方式
    no-threads:服务器使用一个线程,
    one-thread-per-connection: 服务器为每个客户端请求使用一个线程.
  • thread_stack 每个连接被创建的时候,mysql分配给它的内存.这个值一般认为默认就可以应用于大部分场景了,除非必要非则不要动它.
MySQL [(none)]> show variables like 'thread_%';
+-------------------+---------------------------+
| Variable_name     | Value                     |
+-------------------+---------------------------+
| thread_cache_size | 200                       |
| thread_handling   | one-thread-per-connection |
| thread_stack      | 196608                    |
+-------------------+---------------------------+
3 rows in set (0.00 sec)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

查看当前线程状态。

MySQL [(none)]> show global status like 'Thread%'
    -> ;
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| Threads_cached    | 92    |
| Threads_connected | 157   |
| Threads_created   | 249   |
| Threads_running   | 7     |
+-------------------+-------+
4 rows in set (0.00 sec)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • Thread_cached表示当前线程池空余的线程数。
  • Threads_connected表示已经和连接绑定的线程。
  • Threads_created表示已经创建的线程数。
  • Threads_running表示正在运行(执行sql等)的线程数

注意:thread_cache_size是连接池的大小,实际能创建的连接数是由max_connections决定的。

show global status like ‘Max_used_connections’;状态应该和Thread_created值是一致的。

show processlist个数与Threads_connected值一致,show processlist中的id就是线程id。

<font color=red>连接数多少决定了线程数多少,导致频道的线程切换导致性能低下,尽量使用Thread pool模式,连接数可以尽量多,线程数=cpu*2个数。</font>

kill线程
mysql>show proccesslist ;
mysql>kill progress_id; #proccess_id 即show proccesslist 查询出来的id
  • 1
  • 2

但对于锁线程来说,其状态为sleep,所以找其id 用proccesslist看不出来,
但可用sys系统库里的performance_schema (MySQL5.6开始默认启用,相比于设置为 off 会有 10% 左右的性能损失)

通过 sys.schema_table_lock_waits 查表锁 ,找出造成阻塞的 process id ,把这个连接用 kill 命令断开即可:

mysql>select blocking_pid from sys.schema_table_lock_waits ;
mysql>kill blocking_pid;
  • 1
  • 2

通过 sys.innodb_lock_waits 查行锁
可以通过 sys.innodb_lock_waits 表查到 # MySQL 5.7

mysql>select * from sys.innodb_lock_waits where locked_table=`'test'.'t'`\G
mysql>kill blocking_pid;
  • 1
  • 2

tablecache

table文件

存储引擎是InnoDB, 在data目录下会看到2类文件:.frm、.ibd

  1. *.frm–表结构的文件。
  2. *.ibd–表数据和索引的文件。该表的索引(B+树)的每个非叶子节点存储索引,叶子节点存储索引和索引对应的数据。
    *.MYD–"D"数据信息文件,是表的数据文件。
    *.MYI–"I"索引信息文件,是表数据文件中任何索引的数据树

当某一连接访问一个表时,MySQL会检查当前已缓存表(注意是表结构不是数据)的数量。如果该表已经在缓存中打开,则会直接访问缓存中的表已加快查询速度;如果该表未被缓存,则会将当前的表添加进缓存并进行查询。

在执行缓存操作之前,table_open_cache用于限制缓存表的最大数目:如果当前已经缓存的表未达到table_open_cache,则会将新表添加进来;若已经达到此值,MySQL将根据缓存表的最后查询时间、查询率等规则释放之前的缓存。

数据结构

  • table:  MySQL为每一个查询sql中的表建一个TABLE对象(结构,这个对象每个连接一个)
  • table_share: MySQL为每一张表建立一个table_share对象,与frm文件对应(多个连接共享一个)
  • handler:  对应于每个TABLE对象,innodb引擎创建一个handler
  • dict_table_t: innodb为每一个ibd表load一个数据字典对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-byyDPk2P-1643101235970)(http://media-hk01.oss-cn-hongkong.aliyuncs.com/images%2Fliaomin%40jieztech.com%2Fmysql%E4%BC%98%E5%8C%96.md%2F8d085efb-d7bf-4229-9330-8cfc08f86944_image.png?Expires=1800176555&OSSAccessKeyId=LTAIXyAB35iFpHM0&Signature=elmOax5Owkirba2MHMR6B9gj7zA%3D&x-oss-process=image%2Fformat%2Cpng)]

参数和状态

查看参数中两个关于表的cache值

mysql> show variables like 'table%cache%';
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| table_definition_cache     | 1400  |  就是上面table_share的cache数
| table_open_cache           | 2000  | 就是上面table的cache数
| table_open_cache_instances | 16    |
+----------------------------+-------+
3 rows in set (0.00 sec)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

这两个cache值建议设置为:
table_open_cache=最大连接数*表的个数(因为每个连接线程都会创建一个)
table_definition_cache=表的个数(因为所有线程共享)

查看当前打开的表以及历史打开表数量

mysql>  show global status like 'open%tables%'; 
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_tables   | 102   |
| Opened_tables | 109   |
+---------------+-------+
2 rows in set (0.00 sec)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • Open_tables:表示打开表的数量。
  • Opened_tables:表示打开过的表数量,如果Opened_tables数量过大,说明配置中table_cache(5.1.3之后这个值叫做table_open_cache)值可能太小

比较合适的值为:
  Open_tables / Opened_tables * 100% >= 85%
  Open_tables / table_open_cache* 100% <= 95%

查看打开的所有表

MySQL [(none)]> show open tables where in_use >0;
+----------------+--------------------+--------+-------------+
| Database       | Table              | In_use | Name_locked |
+----------------+--------------------+--------+-------------+
| apos_order     | order              |      2 |           0 |
| apos_commodity | skusale            |      2 |           0 |
| apos_commodity | mall_agent_product |      2 |           0 |
| apos_stock     | batchstockbusiitem |      1 |           0 |
| apos_order     | orderconsignee     |      2 |           0 |
| apos_order     | orderdrawee        |      2 |           0 |
| apos_mall      | mall_mobile_page   |      1 |           0 |
| apos_commodity | product            |      2 |           0 |
| apos_commodity | mallproductsale    |      2 |           0 |
| apos_commodity | skusalestock       |      2 |           0 |
| apos_order     | orderexpress       |      1 |           0 |
+----------------+--------------------+--------+-------------+
11 rows in set (0.00 sec)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

其他参数:

  • table_open_cache

Server层参数。

这个参数表示针对所有threads的table cache总和,5.6.7之前默认是400,5.6.8之后是2000。

这是个server层的参数,mysql不支持并行查询,mysql的会话也没有PGA的概念,一个thread引用myisam表时需要在server层上创建一个table对象(索引也需要创建一个但是是共享的,self join会创建2个,分区表每个分区按单表对待),如果同时多个会话引用一个表也会创建多个表对象,虽然会加大内存使用量,但是却极大的减少了内部表锁的争用。

这个值的数目建议设置为max_connections*你的表数目,当然你可能也需要为一些临时表等对象预留,但是这个数目已经足够大啦。

那么mysql什么时候释放这些表对象呢?

  1. 当缓冲已满,而连接想要打开一个不在缓冲中的表时。
  2. 当缓冲数目已经超过了table_open_cache设置的值,mysql开始使用LRU算法释放表对象。
  3. 当你用flush tables;语句时。
  • open_files_limit

引擎层参数。

这个参数表示mysqld可用的最大文件描述符数目,如果你遇到“Too many open files”的错误,应当考虑加大它。这个参数的默认值是0表示无限制(大于5.6.7后默认值不再为0,参考官网),但其实他的值是与操作系统相关的,在Unix系统下这个值的数目不能大于ulimit -n。

这个参数应当大于等于table_open_cache。

目前不清楚当设置了innodb_open_files后此参数是指所有存储引擎的句柄数限制,还是非innodb的句柄数限制,需要研究源码才可以理清,这里暂且认为是前者。

  • innodb_open_files

引擎层参数。

这个参数只对InnoDB存储引擎有效,它指定了mysql可以同时打开的最大.ibd文件的数目。这个参数即不影响table_open_cache也不受open_files_limit影响,是独立的只对InnoDB有效的。所以在默认为InnoDB存储引擎时可以不考虑open_files_limit只去设innodb_open_files。

这3个参数的关系可以总结如下,为保证性能,你应当设置为如下值:

max_connections*你的表数目 = table_open_cache <=open_files_limit< ulimit -n

innodb_open_files<ulimit -n

命中测试

测试tablecache

  1. 创建一张表test,然后插入一条数据库。
  2. 重启数据库让cache释放。
  3. 使用strace监控mysqld的进程
[root@sonarqube ~]# strace -f -ttt -e file -y -p 17050
strace: Process 17050 attached with 27 threads
strace: Process 17149 attached
[pid 17149] 1642495912.743274 open("/sys/devices/system/cpu/online", O_RDONLY|O_CLOEXEC) = 43</sys/devices/system/cpu/online>
[pid 17149] 1642495927.389855 open("./test/test.frm", O_RDONLY) = 43</var/lib/mysql/test/test.frm>
  • 1
  • 2
  • 3
  • 4
  • 5
  1. 编写查询语句,然后查看是否命中缓存,发现hit是0,同时发现strace中出现open(test.frm)动作在读取表结构文件。
show session status like 'Table_open_cache%';
  • 1
  1. 编写查询语句,查看是否命中缓存,发现hit是1,同时发现strace没有open,说明使用缓存

    show session status like 'Table_open_cache%';
    
    • 1
  2. 在开启一个会话,执行查询语句发现strace没有open但是也没有命中缓存 hit=0,说明使用了table_share缓存。

慢查询

MySQL的慢查询,全名是 慢查询日志 ,是MySQL提供的一种日志记录,用来记录在MySQL中响应时间超过阀值的语句。

参数和状态

MySQL 慢查询的相关参数解释:

  • slow_query_log:是否开启慢查询日志,1表示开启,0表示关闭。
  • slow-query-log-file:新版(5.6及以上版本)MySQL数据库慢查询日志存储路径。可以不设置该参数,系统则会默认给一个缺省的文件host_name-slow.log
  • long_query_time:慢查询阈值,当查询时间多于设定的阈值时,记录日志,默认是:10s。
  • log_queries_not_using_indexes:未使用索引的查询也被记录到慢查询日志中(可选项),默认是FALSE。
  • log_output:日志存储方式。log_output=‘FILE’表示将日志存入文件,默认值是’FILE’。log_output='TABLE’表示将日志存入数据库。
MySQL [(none)]> show variables like 'slow%';
+---------------------+-------------------------------+
| Variable_name       | Value                         |
+---------------------+-------------------------------+
| slow_launch_time    | 2                             |
| slow_query_log      | ON                            |
| slow_query_log_file | /data/mysql/3306/log/slow.log |
+---------------------+-----
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

如果你想查询有多少条慢查询记录,可以使用 Slow_queries系统变量。

MySQL [(none)]> show global status like '%Slow_queries%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Slow_queries  | 550   |
+---------------+-------+
1 row in set (0.00 sec)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

分析工具

在生产环境中,如果要手工分析日志,查找、分析SQL,显然是个体力活。

MySQL提供了日志分析工具 mysqldumpslow

查看 mysqldumpslow的帮助信息:

[root@DB-Server ~]# mysqldumpslow --help
 Usage: mysqldumpslow [ OPTS... ] [ LOGS... ]
 
Parse and summarize the MySQL slow query log. Options are
 
  --verbose    verbose
  --debug      debug
  --help       write this text to standard output
 
  -v           verbose
  -d           debug
  -s ORDER     what to sort by (al, at, ar, c, l, r, t), 'at' is default(排序方式)
                 al: average lock time(平均锁定时间)
                 ar: average rows sent(平均返回记录数)
                 at: average query time(平均查询时间)
                  c: count(访问计数)
                  l: lock time(锁定时间)
                  r: rows sent(返回记录)
                  t: query time(查询时间)
   -r           reverse the sort order (largest last instead of first)
   -t NUM       just show the top n queries(返回前面n条数据)
   -a           don't abstract all numbers to N and strings to 'S'
   -n NUM       abstract numbers with at least n digits within names
   -g PATTERN   grep: only consider stmts that include this string(正则匹配模式,大小写不敏感)
   -h HOSTNAME  hostname of db server for *-slow.log filename (can be wildcard),
                default is '*', i.e. match all
   -i NAME      name of server instance (if using mysql.server startup script)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

首先获取到慢日志所属位置

show variables like '%slow_query_log_file%';
  • 1

比如,得到返回记录集最多的10个SQL。

mysqldumpslow -s r -t 10 /data/mysql/3306/log/slow.log
  • 1

得到访问次数最多的10个SQL

mysqldumpslow -s c -t 10 /data/mysql/3306/log/slow.log
  • 1

得到按照时间排序的前10条里面含有左连接的查询语句。

mysqldumpslow -s t -t 10 -g "left join" /data/mysql/3306/log/slow.log
  • 1

另外建议在使用这些命令时结合 | 和more 使用 ,否则有可能出现刷屏的情况。

mysqldumpslow -s r -t 20 /data/mysql/3306/log/slow.log | more
  • 1

临时表

如果 SQL 在执行过程中读到的数据无法直接得到结果,那么就需要额外的内存来保存中间结果,得出最终结果,这个额外的内存就是内部临时表,哪些操作会产生临时表呢,

  • 1、UNION查询;
  • 2、用到TEMPTABLE算法或者是UNION查询中的视图;
  • 3、ORDER BY和GROUP BY的子句不一样时;
  • 4、表连接中,ORDER BY的列不是驱动表中的;
  • 5、DISTINCT查询并且加上ORDER BY时;
  • 6、SQL中用到SQL_SMALL_RESULT选项时;
  • 7、FROM中的子查询;
  • 8、子查询或者semi-join时创建的表

参数

与临时表相关的两个参数

1.tmp_table_size
tmp_table_size规定了内部内存临时表的最大值,单位是字节,每个线程都要分配。如果内存临时表超出了限制,MySQL就会自动地把它转化为基于磁盘的MyISAM表,存储在指定的tmpdir目录下,默认:

mysql> show variables where Variable_name in ('tmp_table_size', 'max_heap_table_size'); 
mysql> show variables like "tmpdir"; 
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| tmpdir        | /tmp/ |
+---------------+-------+
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

优化查询语句的时候,要避免使用临时表,如果实在避免不了的话,要保证这些临时表是存在内存中的。如果需要的话并且你有很多group by语句,并且你有很多内存,增大tmp_table_size(和max_heap_table_size)的值。这个变量不适用与用户创建的内存表(memory table).
你可以比较内部基于磁盘的临时表的总数和创建在内存中的临时表的总数(Created_tmp_disk_tables和Created_tmp_tables),一般的比例关系是:Created_tmp_disk_tables/Created_tmp_tables<5%
2.max_heap_table_size
这个变量定义了用户可以创建的内存表(memory table)的大小.这个值用来计算内存表的最大行数值。这个变量支持动态改变,
即set @max_heap_table_size=N。但对于已经存在的内存表就没有什么用了,除非这个表被重新创建(create table)或者修改(alter table)或者truncate table。服务重启也会设置已经存在的内存表为全局max_heap_table_size的值。这个变量和tmp_table_size一起限制了内部内存表的大小。

状态

mysql> show global status like 'created_tmp%';
  +-------------------------+---------+** **
  | Variable_name | Value |** **
  +-------------------------+---------+** **
  | Created_tmp_disk_tables | 21197 |** **
  | Created_tmp_files | 58 |** **
  | Created_tmp_tables | 1771587 |** **
  +-------------------------+---------+
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

每次创建临时表,Created_tmp_tables增加,如果是在磁盘上创建临时表,Created_tmp_disk_tables也增加,Created_tmp_files表示MySQL服务创建的临时文件文件数,比较理想的配置是:

Created_tmp_disk_tables / Created_tmp_tables * 100% <= 25%
  比如上面的服务器Created_tmp_disk_tables / Created_tmp_tables * 100% = 1.20%,应该相当好了。我们再看一下MySQL服务器对临时表的配置:** **

查询缓存

查询缓存的失效非常频繁,只要有对一个表的更新,这个表上的所有的查询缓存都会被清空。
因此很可能你费劲地把结果存起来,还没使用呢,就被一个更新全清空了。对于更新压力大的数据库来说,查询缓存的命中率会非常低。除非你的业务有一张静态表,很长时间更新一次,比如系统配置表,那么这张表的查询才适合做查询缓存。

大多数应用都把缓存做到了应用逻辑层,简单的如一个map的mybatis,复杂的可以用redis或者memcache,直接操作内存远远比走网络访问快,所以mysql直接抛弃了查询缓存

尽管MySQL Query Cache旨在提高性能,但它存在严重的可伸缩性问题,并且很容易成为严重的瓶颈。
自MySQL 5.6(2013)以来,默认情况下已禁用查询缓存,因为众所周知,它不能与多核计算机上在高吞吐量工作负载情况下进行扩展。

表锁情况

mysql> show global status like 'table_locks%';
  +-----------------------+-----------+** **
  | Variable_name | Value |** **
  +-----------------------+-----------+** **
  | Table_locks_immediate | 490206328 |** **
  | Table_locks_waited | 2084912 |** **
  +-----------------------+-----------+
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • Table_locks_immediate:表示立即释放表锁数。
  • Table_locks_waited:表示需要等待的表锁数,如果Table_locks_immediate / Table_locks_waited > 5000,最好采用InnoDB引擎,因为InnoDB是行锁而MyISAM是表锁,对于高并发写入的应用InnoDB效果会好些。示例中的服务器Table_locks_immediate / Table_locks_waited = 235,MyISAM就足够了。

查看正在锁的事务

select * from information_schema.innodb_locks;
  • 1

查看等待锁的事务

select * from information_schema.innodb_locks_waits;
  • 1

使用系统表进行锁查询:

select r.trx_isolation_level, r.trx_id waiting_trx_id,r.trx_mysql_thread_id waiting_trx_thread, r.trx_state waiting_trx_state,lr.lock_mode waiting_trx_lock_mode,lr.lock_type waiting_trx_lock_type, lr.lock_table waiting_trx_lock_table,lr.lock_index waiting_trx_lock_index,r.trx_query waiting_trx_query, b.trx_id blocking_trx_id,b.trx_mysql_thread_id blocking_trx_thread,b.trx_state blocking_trx_state, lb.lock_mode blocking_trx_lock_mode,lb.lock_type blocking_trx_lock_type,lb.lock_table blocking_trx_lock_table, lb.lock_index blocking_trx_lock_index,b.trx_query blocking_query from information_schema.innodb_lock_waits w inner join information_schema.innodb_trx b on b.trx_id=w.blocking_trx_id inner join information_schema.innodb_trx r on r.trx_id=w.requesting_trx_id inner join information_schema.innodb_locks lb on lb.lock_trx_id=w.blocking_trx_id inner join information_schema.innodb_locks lr on lr.lock_trx_id=w.requesting_trx_id G
  • 1

涉及的3张表说明:

information_shcema下的三张表(通过这三张表可以更新监控当前事物并且分析存在的锁问题)

  • innodb_trx ( 打印innodb内核中的当前活跃(ACTIVE)事务)
  • innodb_locks ( 打印当前状态产生的innodb锁 仅在有锁等待时打印)
  • innodb_lock_waits (打印当前状态产生的innodb锁等待 仅在有锁等待时打印)

常用诊断问题

执行时间最长sql

通过pidstat找到当前进程中占用cpu最大的线程·

  1. pidstat -t -p <mysqld_pid> 1: 找出CPU高的 THREAD_OS_ID ;
  1. 根据 THREAD_OS_ID 查找到SQL文本;
    select * from threads where thread_os_id=1487;
    select * from performance_schema.threads;

查看进程io占用

^Croot@iZwz9e8jq6kptfkfpyo2pxZ:~# pidstat -d -p 1316
Linux 4.19.0-16-amd64 (iZwz9e8jq6kptfkfpyo2pxZ)         01/24/2022      _x86_64_        (8 CPU)

04:27:53 PM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
04:27:53 PM  1000      1316      8.82    386.00      0.00       1  mysqld
  • 1
  • 2
  • 3
  • 4
  • 5

IO情况统计(-d)
使用-d选项,我们可以查看进程IO的统计信息:
kB_rd/s: 每秒进程从磁盘读取的数据量(以kB为单位)
kB_wr/s: 每秒进程向磁盘写的数据量(以kB为单位)
Command: 拉起进程对应的命令

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/793620
推荐阅读
相关标签
  

闽ICP备14008679号