陈同学
微服务
Accelerator
About
# Mysql thread 与 OS thread > 参考资料 > [MySQL Threads](https://dev.mysql.com/doc/refman/5.7/en/mysql-threads.html) > [The threads Table](https://dev.mysql.com/doc/refman/5.7/en/threads-table.html) > [How MySQL Uses Threads for Client Connections](https://dev.mysql.com/doc/refman/5.7/en/connection-threads.html) > [MySQL terminology: processes, threads & connections](http://code.openark.org/blog/mysql/mysql-terminology-processes-threads-connections) > [Matching MariaDB internal thread ID with OS thread](https://www.miroch.ru/2017/04/05/matching-mariadb-internal-thread-id-with-os-thread/) > 《MySQL技术内幕:InnoDB存储引擎》 本文作为 [Mysql插入2.6亿条垃圾数据后会发生什么?](https://chenyongjun.vip/articles/42) 、[手工重现Mysql插入的”2.6亿”垃圾数据](https://chenyongjun.vip/articles/43) 的续篇,初始目的是想看看kill掉执行中的事务对应的os thread之后会发生什么,同时学习下mysql thread与os thread的相关知识。 测试环境信息如下: * **OS**:Ubuntu 16.04 LTS * **Mysql**:Mysql 5.7.18,使用docker images运行的实例 ## Mysql如何处理client请求 在Mysql中,连接管理线程(Connection manager threads)用于处理来自客户端的TCP/IP连接请求,它会将每个client connection关联到一个专门的mysql thread,这个thread负责处理通过connection发出的所有请求(也包含请求的安全认证)。 ## Mysql thread mysql thread并非操作系统上的真实线程,只是mysql中的一个对象,但是会与os thread(操作系统真实的线程)关联起来。下面了解下如果查看mysql thread。 最简单的方式是使用 `show processlist` 查看当前连接,这个命令与查看`information_schema.processlist`表效果一致。 **每一个连接进来时,都可以在看到一条新的记录**。可以通过不断打开与关闭连接来测试,每次打开一个连接后在 *processlist* 中将会多出一条记录,连接关闭后这条记录也会被移除。  如果当前连接正在进行事务处理,也可以通过`information_schema.innodb_trx` 事务表查看,其中`trx_mysql_thread_id`字段为mysql thread id,与processlist中id一致  如果启用了mysql的性能监控功能( 通过`SHOW VARIABLES LIKE 'performance_schema'`查看 ),可以通过`performance_schema.threads` 查看,其中有`PROCESSLIST_ID`,与上面的两个ID保持一致。  ## ## Mysql OS thread 虽然每次打开连接,可以看到一个新的 *mysql thread* 产生,但是OS并不一定会为每个连接创建一个新的os thread。 首先,看看**mysqld**进程对应的所有os线程。 执行命令 `ps -ef | grep mysql` 得到mysqld的PID(进程ID)为 **16286** ``` 999 16286 16267 4 22:39 ? 00:00:00 mysqld ubuntu 16362 9909 0 22:39 pts/1 00:00:00 grep --color=auto mysql ``` 通过`top -H -p 16286 ` 查看进程下的所有线程: **mysqld**下有27个线程,PID为每个线程的ID。mysql thread和os thread并不能直接匹配起来。  可以通过一个例子来看看mysql thread与os thread的关系。 运行两个事务,每个事务往 test表中插入1000W条记录,事务处理时间较长。 在 *processlist* 表中可以看到两个执行 *insert* 操作的mysql thread.  在os threads中将会使用两个thread来进行实际的处理,top命令可以看到mysqld进程下的线程占用的CPU和内存情况。  也可以进行如下测试,会发现一些有趣的事情: * 使用一个connection连接并按顺序执行几个事务,会使用不同的os thread来进行处理,当然,也可能是同一个os thread(如果只有一个os thread可用,所有事务会有同一个os thread处理;如果有多个os thread可用,将会轮换使用不同的os thread)。 * **如果kill掉任意一个os thread,mysql server将会shutdown**。因此,不要尝试kill掉某个事务对应的os thread,毫无意义,mysql重新启动后,原先的事务该干的事还是得一样干完,以保证事务的ACID特性 * 如果你kill mysql thread,将不会影响到实际的os thread,但是该mysql thread将被删除。假如你正在执行一个 *insert* 事务且已经插入了100W数据,如果kill mysql thread,事务对应的trx_mysql_thread_id会设置为0,然后事务开始执行rollback ## connection,mysql thread,os thread之间的关系 > 本小节的探讨基于 thread_handling=one-thread-per-connection,即线程模型是为每个连接分配一个mysql thread 三者的关联如下: * mysql会为每个connection创建一个对应mysql thread,连接关闭后,mysql thread生命周期也终止。这个mysql thread可以在processlist、threads表中查看 * 每个mysql threard将与一个os thread关联在一起,mysql thread销毁后,os thread不会被销毁,可以继续给其他mysql thread使用 * 如果所有os thread都被mysql thread用光了,下一个connection请求时将会创建新的os thread 下面做一个小测试: 假定`max_connections=151`, 使用以下语句应用中循环1000次不断获取连接并且不释放连接 ```java DriverManager.getConnection(url, user, password); ``` 可以观察到以下现象: * mysql将不断的创建mysql thread,由于没有闲置的os thread可用,也将不断创建os thread * 当连接达到max_connections后,会报错:`MySQLNonTransientConnectionException: Too many connections`,同时mysql将无法再创建连接 * 达到max_connections后,可以手工处理掉空闲时间很长的连接,也可以等待连接达到 **wait_timeout** 设置的时间后被自动close掉。wait_timeout默认是8小时 一般应用都会通过连接池与DB交互,同时会定期通过连接发送请求(mysql 可以发送`select 1`) 给DB以重置connection的空闲时间 ## 通过mysql thread找到os thread ### 通过查看innodb status 如果有事务正在执行,通过 `show engine innodb status` 查看事务信息,下面是该命令输出的部分信息: ``` ------------ TRANSACTIONS ------------ ... 1 lock struct(s), heap size 1136, 0 row lock(s), undo log entries 568226 MySQL thread id 65, OS thread handle 139656030103296, query id 229749097 218.104.153.55 test update INSERT INTO test (uid, uid2, uid3) VALUES (UUID(), UUID(), UUID()) ``` 可以看到mysql thread为65,OS thread为139656030103296 `MySQL thread id 65, OS thread handle 139656030103296` ### 通过查看threads表 > 题外话之mysql 官方说明:访问threads表对mysql没有什么性能影响,但访问processlist表或者show processlist对性能有一定影响,因为它们都需要mutex(互斥) *performance.threads* 表中有 **thread_os_id** 字段,存储了mysql thread和os thread的关系  关于 **thread_os_id**: thread_os_id 是操作系统定义的thread或task标识符: * 如果mysql thread在生命周期中与一个os thread关联,thread_os_id字段将包含os thread ID * 如果mysql thread在生命周期中没有和os thread关联,thread_os_id将为NULL 在windows下,thread_os_id可以在任务管理器中看到;在linux下,thread_os_id和gettid()方法对应,可以使用`perl` 、`ps -L`命令或者使用`proc`文件系统(/proc/*[pid]*/task/*[tid]*) 不过查阅许多资料后也没有结果,我也没有找到合适的方式将`thread_os_id与`os线程直接对应起来,只能侧面判断。 [有资料](https://www.miroch.ru/2017/04/05/matching-mariadb-internal-thread-id-with-os-thread/) 提供了一个偏方:通过`gdb attach ` 命令来调试正在运行的程序,但是这会导致mysqld进程被暂停,并没有实际意义,不过本身找到mysql thread对应的os thread也没什么意义。先贴一下通过gdb attach得到的数据: 先执行`gdb attach 11646` ,再执行`info threads` 命令,输出如下: 包含了线程ID和对应的16进制ID,资料显示:16进制ID在`show engine innodb status`输出信息中是可以看到的,不过我在mysql 5.7.18版本的输出信息中并未找到16进制的线程ID数据。  ## 总结 通过上述讲述和一些例子,可以了解到以下几点: * 连接管理线程会为每一个connection分配一个mysql thread来处理 * mysql thread实际会使用某个os thread来处理请求 * connection关闭或kill mysql thread时,mysql thread会销毁,但是os thread可以继续复用
本文由
cyj
创作,可自由转载、引用,但需署名作者且注明文章出处。
文章标题:
Mysql thread 与 OS thread
文章链接:
https://chenyongjun.vip/articles/45
扫码或搜索 cyjrun 关注微信公众号, 结伴学习, 一起努力