溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

線程簡介和MySQL調(diào)試環(huán)境的搭建

發(fā)布時(shí)間:2021-09-10 15:30:14 來源:億速云 閱讀:116 作者:chen 欄目:MySQL數(shù)據(jù)庫

本篇內(nèi)容介紹了“線程簡介和MySQL調(diào)試環(huán)境的搭建”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

一、線程簡介

我們知道MySQLD是一個(gè)單進(jìn)程多線程的用戶程序,因此我們有必要了解一下什么線程。實(shí)際上MySQL中的線程都是POSIX線程,比如我們的會(huì)話線程、DUMP線程、IO線程以及其他一些Innodb線程都是POSIX線程。

進(jìn)程實(shí)際上就是運(yùn)行中的程序,一個(gè)進(jìn)程中可以包含多個(gè)線程也可以只包含一個(gè)線程。在Linux中線程也叫輕量級進(jìn)程(light-weight process)簡稱為LWP,進(jìn)程的第一個(gè)線程通常稱為主控線程。進(jìn)程是內(nèi)存分配的最小單位,線程是CPU調(diào)度的最小單位,也就是說如果CPU有足夠多核,那么多個(gè)線程可以達(dá)到并行處理的效果,內(nèi)核直接調(diào)度線程。下面是我學(xué)習(xí)Linux線程的時(shí)候看到的一張我認(rèn)為比較好理解的圖,我重新畫了一下放在下面供大家參考(圖29-1,高清原圖包含在文末原圖中):
線程簡介和MySQL調(diào)試環(huán)境的搭建

一個(gè)進(jìn)程內(nèi)部的所有線程都擁有相同的代碼程序、堆、全局變量、共享庫等,但是每個(gè)線程擁有自己獨(dú)立??臻g和寄存器,他們共享進(jìn)程的虛擬內(nèi)存地址空間。下面我們假定是32位操作系統(tǒng)下的MySQLD進(jìn)程,它的進(jìn)程虛擬內(nèi)存地址示意圖如下,實(shí)際上這個(gè)圖也是參考《Linux UNIX系統(tǒng)編程手冊》畫的(圖29-2,高清原圖包含在文末原圖中):

線程簡介和MySQL調(diào)試環(huán)境的搭建

我們發(fā)現(xiàn)線程的堆內(nèi)存和全局變量是共享的,因此線程之間數(shù)據(jù)共享很輕松,但是要控制好這些共享內(nèi)存就需要引入我們的線程同步技術(shù),比如我們常說的Mutex。

如果想了解線程到底共享了哪些資源,線程和進(jìn)程到底各有什么優(yōu)勢和劣勢,可執(zhí)行參考上面我給出的書籍。

二、PID、LWP ID、Thread TID

如果要進(jìn)行調(diào)試就需要了解這三種ID,其中PID和LWP ID是比較重要,因?yàn)椴还苁钦{(diào)試和運(yùn)維都會(huì)遇到它們,而Thread TID不做多線程開發(fā)一般很少用到,下面是我對它們的總結(jié):

  • PID:內(nèi)核分配,用于識(shí)別各個(gè)進(jìn)程的ID。這個(gè)應(yīng)該是大家最熟悉的。

  • LWP ID:內(nèi)核分配,用于識(shí)別各個(gè)線程的ID,它就像是線程是‘PID’一樣。同一個(gè)進(jìn)程下的所有線程有相同的PID,但是LWP ID卻不一樣,主控線程的LWP ID就是進(jìn)程PID。

  • Thread TID:進(jìn)程內(nèi)部用于識(shí)別各個(gè)線程的內(nèi)部ID,這個(gè)ID用得不多。

下面我寫了一個(gè)簡單的C測試程序僅僅用于觀察這些ID,它是通過主控線程再創(chuàng)建一個(gè)線程,也就是說這個(gè)進(jìn)程包含了兩個(gè)線程。它們分別打印自己的PID、LWP ID、Thread TID,然后做一個(gè)循環(huán)自加操作引起高CPU消耗現(xiàn)象便于觀察。然后我們使用Linux的top -H和ps -eLlf命令分別進(jìn)行觀察。

  1. 程序輸出

下面是程序的輸出:

# ./gaopengtest 
main thread: pid 13188 tid 2010470144 lwp 13188
new thread:  pid 13188 tid 2010461952 lwp 13189

我們可以看到兩個(gè)線程的PID都是13188,但是主控線程的LWP ID是13188,新建立的一個(gè)線程LWP ID是13189。然后就是Thread TID了,不過多解釋。

  1. top -H 觀察如下:

我們可以看到這兩個(gè)線程都是高耗CPU的線程,CPU已經(jīng)處于飽和狀態(tài)。這里我們看到top -H命令的輸出中‘PID’就是LWP ID。這里我們還能看它們的內(nèi)存信息完全一致,內(nèi)存信息實(shí)際上是整個(gè)進(jìn)程的內(nèi)存信息,因?yàn)檫M(jìn)程是內(nèi)存分配的最小單位嘛。

線程簡介和MySQL調(diào)試環(huán)境的搭建

注意如果這個(gè)時(shí)候查看這個(gè)進(jìn)程占用的%cpu就超過了100%,接近200%,如下:

線程簡介和MySQL調(diào)試環(huán)境的搭建

  1. ps -eLlf 觀察如下:

我們可以看到這里包含了PID和LWP ID,不過多解釋了,大家可以自己去試試。

線程簡介和MySQL調(diào)試環(huán)境的搭建

三、如何將MySQL的線程和LWP ID進(jìn)行對應(yīng)

在5.7中我們已經(jīng)可以通過MySQL語句和LWP ID進(jìn)行對應(yīng)了,這讓性能診斷變得更加便捷。比如我上面的列子如果兩個(gè)高耗CPU的線程是MySQL的線程那么我們就拿到了線程的LWP ID,然后可以通過語句找到這兩個(gè)線程到底是MySQL的什么線程。語句如下:

mysql> select a.thd_id,b.THREAD_OS_ID,a.user 
,b.TYPE from  sys.processlist 
a,performance_schema.threads  b where b.thread_id=a.thd_id;
+--------+--------------+---------------------------+---------+------------
| thd_id | THREAD_OS_ID | user                      | conn_id | TYPE       
+--------+--------------+---------------------------+---------+------------
|      1 |        16370 | sql/main                  |    NULL | BACKGROUND 
|      2 |        17202 | sql/thread_timer_notifier |    NULL | BACKGROUND 
|      3 |        17207 | innodb/io_ibuf_thread     |    NULL | BACKGROUND 
|      4 |        17208 | innodb/io_log_thread      |    NULL | BACKGROUND 
|      5 |        17209 | innodb/io_read_thread     |    NULL | BACKGROUND 
|      6 |        17210 | innodb/io_read_thread     |    NULL | BACKGROUND 
|      7 |        17211 | innodb/io_read_thread     |    NULL | BACKGROUND 
|      8 |        17212 | innodb/io_read_thread     |    NULL | BACKGROUND 
|      9 |        17213 | innodb/io_read_thread     |    NULL | BACKGROUND 
|     10 |        17214 | innodb/io_read_thread     |    NULL | BACKGROUND 
|     11 |        17215 | innodb/io_read_thread     |    NULL | BACKGROUND 
|     12 |        17216 | innodb/io_read_thread     |    NULL | BACKGROUND 
|     13 |        17217 | innodb/io_write_thread    |    NULL | BACKGROUND 
|     14 |        17218 | innodb/io_write_thread    |    NULL | BACKGROUND 
|     15 |        17219 | innodb/io_write_thread    |    NULL | BACKGROUND 
|     16 |        17220 | innodb/io_write_thread    |    NULL | BACKGROUND 
|     17 |        17221 | innodb/io_write_thread    |    NULL | BACKGROUND 
......

這里的THREAD_OS_ID就是線程的LWP ID。然后我們使用剛才的ps -eLlf命令再看一下,如下:

線程簡介和MySQL調(diào)試環(huán)境的搭建

我們可以發(fā)現(xiàn)他們是可以對應(yīng)上的,這個(gè)最好自己實(shí)際試試就知道了。

四、調(diào)試環(huán)境搭建

調(diào)試環(huán)境的搭建我認(rèn)為不管使用什么方法只要能夠起到調(diào)試的作用就可以了。這里介紹一下我的方法。我是在Linux下面直接使用gdb調(diào)試的,我覺得這個(gè)方法搭建非常簡單并且很奏效,基本上只要會(huì)源碼安裝就能完成調(diào)試環(huán)境的搭建。下面來看看步驟:

1. 第一步下載MySQL源碼包,解壓。

我特意重新下載了官方版的5.7.26源碼。

2. 使用源碼安裝的方法安裝MySQL,注意需要開啟debug選項(xiàng)

下面是我使用的選項(xiàng):

cmake -DCMAKE_INSTALL_PREFIX=/root/sf/mysql3312/ -DMYSQL_DATADIR=/root/sf/mysql3312/data/ -DSYSCONFDIR=/root/sf/mysql3312/ -DWITH_INNOBASE_STORAGE_ENGINE=1 -DWITH_ARCHIVE_STORAGE_ENGINE=1 -DWITH_BLACKHOLE_STORAGE_ENGINE=1 -DWITH_FEDERATED_STORAGE_ENGINE=1 -DWITH_PARTITION_STORAGE_ENGINE=1  -DMYSQL_UNIX_ADDR=/root/sf/mysql3312/mysql3312.sock -DMYSQL_TCP_PORT=3306 -DENABLED_LOCAL_INFILE=1 -DEXTRA_CHARSETS=all -DDEFAULT_CHARSET=utf8  -DDEFAULT_COLLATION=utf8_general_ci  -DMYSQL_USER=mysql  -DWITH_BINLOG_PREALLOC=ON   -DWITH_BOOST=/root/sf/mysql-5.7.26/boost/boost_1_59_0 -DWITH_DEBUG=1

注意最后的-DWITH_DEBUG=1必須開啟。

3. make&&make install

編譯和安裝。

4. 準(zhǔn)備參數(shù)文件和初始化MySQL數(shù)據(jù)庫,并且確保能成功啟動(dòng)

這一步自己處理,注意權(quán)限。我的環(huán)境啟動(dòng)成功了如下:

[root@gp1 support-files]# ./mysql.server start
Starting MySQL....... SUCCESS! 
[root@gp1 support-files]# ./mysql.server stop
Shutting down MySQL.. SUCCESS!

5. 準(zhǔn)備gdb命令文件

如下是我準(zhǔn)備的命令文件:

[root@gp1 ~]# more debug.file 
break main
run --defaults-file=/root/sf/mysql3312/my.cnf --user=mysql --gdb

第一行是在main函數(shù)處打一個(gè)斷點(diǎn)。第二行就是gdb調(diào)用MySQLD的時(shí)候,MySQLD加什么參數(shù)了,注意run不要寫掉了。

6.使用gdb啟動(dòng)MySQL

使用如下命令啟動(dòng)調(diào)試環(huán)境:

gdb -x /root/debug.file /root/sf/mysql3312/bin/mysqld

下面就是我啟動(dòng)調(diào)試環(huán)境成功的記錄:

# gdb -x /root/debug.file /root/sf/mysql3312/bin/mysqld
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-92.el6)
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /root/sf/mysql3312/bin/mysqld...done.
Breakpoint 1 at 0xec7c53: file /root/sf/mysql-5.7.26/sql/main.cc, line 25.
[Thread debugging using libthread_db enabled]
Breakpoint 1, main (argc=5, argv=0x7fffffffe3b8) at /root/sf/mysql-5.7.26/sql/main.cc:25
25        return mysqld_main(argc, argv);
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.212.el6.x86_64 libaio-0.3.107-10.el6.x86_64 libgcc-4.4.7-18.el6.x86_64 libstdc++-4.4.7-18.el6.x86_64 nss-softokn-freebl-3.14.3-23.3.el6_8.x86_64
(gdb) c
Continuing.
[New Thread 0x7fffee883700 (LWP 29375)]
[New Thread 0x7fff9a9f3700 (LWP 29376)]
[New Thread 0x7fff99ff2700 (LWP 29377)]
[New Thread 0x7fff995f1700 (LWP 29378)]
[New Thread 0x7fff98bf0700 (LWP 29379)]
[New Thread 0x7fff981ef700 (LWP 29380)]
[New Thread 0x7fff977ee700 (LWP 29381)]
[New Thread 0x7fff96ded700 (LWP 29382)]
[New Thread 0x7fff963ec700 (LWP 29383)]
.....

注意到了這里的LWP ID了嗎,前面我們已經(jīng)討論過了。這個(gè)時(shí)候MySQL客戶端程序已經(jīng)可以連接MySQLD了如下:

# /root/sf/mysql3312/bin/mysql -S'/root/sf/mysql3312/mysql3312.sock'
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.26-debug-log Source distribution
Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> select version() ;
+------------------+
| version()        |
+------------------+
| 5.7.26-debug-log |
+------------------+
1 row in set (0.00 sec)

好了到這里基本的調(diào)試環(huán)境就搭建起來了,我們可以發(fā)現(xiàn)很簡單。之后我們就可以進(jìn)行斷點(diǎn)調(diào)試了。我常用的gdb命令包含:

  • info threads:查看全部線程

  • thread n:指定某個(gè)線程

  • bt:查看某個(gè)線程棧幀

  • b:設(shè)置斷點(diǎn)

  • c:繼續(xù)執(zhí)行

  • s:執(zhí)行一行代碼,如果代碼函數(shù)調(diào)用,則進(jìn)入函數(shù)

  • n:執(zhí)行一行代碼,函數(shù)調(diào)用不進(jìn)入

  • p:打印某個(gè)變量值

  • list:打印代碼的文本信息

當(dāng)然gdb還有很多命令,可自行參考其他資料。

六、使用調(diào)試環(huán)境證明問題的一個(gè)列子

這里我們就用一個(gè)例子來看看調(diào)試環(huán)境的使用方法。我們前面第15節(jié)說過binlog cache是在order commit的flush階段才寫入到binary log的,調(diào)用的是函數(shù)binlog_cache_data::flush。好了我們可以將斷點(diǎn)打到這個(gè)函數(shù)如下:

(gdb) b binlog_cache_data::flush
Breakpoint 2 at 0x1846333: file /root/sf/mysql-5.7.26/sql/binlog.cc, line 1674.

然后我們在MySQL客戶端執(zhí)行一個(gè)事物如下,并且提交:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into gpdebug values(1);
Query OK, 1 row affected (0.03 sec)
mysql> commit;

commit的時(shí)候已經(jīng)卡主了,斷點(diǎn)觸發(fā)如下:

Breakpoint 2, binlog_cache_data::flush (this=0x7fff3c00df20...)
    at /root/sf/mysql-5.7.26/sql/binlog.cc:1674
1674      DBUG_ENTER("binlog_cache_data::flush");

我們使用bt命令查看棧幀發(fā)現(xiàn)如下:

#0  binlog_cache_data::flush
at /root/sf/mysql-5.7.26/sql/binlog.cc:1674
#1  0x0000000001861b41 in binlog_cache_mngr::flush 
at /root/sf/mysql-5.7.26/sql/binlog.cc:967
#2  0x00000000018574ce in MYSQL_BIN_LOG::flush_thread_caches 
at /root/sf/mysql-5.7.26/sql/binlog.cc:8894
#3  0x0000000001857712 in MYSQL_BIN_LOG::process_flush_stage_queue 
at /root/sf/mysql-5.7.26/sql/binlog.cc:8957
#4  0x0000000001858d19 in MYSQL_BIN_LOG::ordered_commit 
at /root/sf/mysql-5.7.26/sql/binlog.cc:9595
#5  0x00000000018573b4 in MYSQL_BIN_LOG::commit
at /root/sf/mysql-5.7.26/sql/binlog.cc:8851
#6  0x0000000000f58de9 in ha_commit_trans 
at /root/sf/mysql-5.7.26/sql/handler.cc:1799
#7  0x000000000169e02b in trans_commit 
at /root/sf/mysql-5.7.26/sql/transaction.cc:239
......

好了看到這個(gè)棧幀,就能證明我們的說法了,如果想深入學(xué)習(xí)代碼就可以從這個(gè)棧幀出發(fā)進(jìn)行學(xué)習(xí)。但是值得注意的是,這是建立在知道函數(shù)接口功能的前提下的,如果我們不知道寫入binary log會(huì)調(diào)用binlog_cache_data::flush函數(shù)那么調(diào)試也就不好進(jìn)行了,我就經(jīng)常遇到這樣的困境。因此整個(gè)系列我給出了很多這樣的接口,供有興趣的朋友調(diào)試和測試。

“線程簡介和MySQL調(diào)試環(huán)境的搭建”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI