MySQL:終於為OS層面的線程命名了
能力有限,如果有誤請諒解。
一、問題來源
最近在檢查某個數據庫性能的時候,通過top -Hu mysql看到了一個特別奇怪的現象,線程有了自己的名字,我開始以為是哪個大廠自己維護的版本,如下:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
8146 mysql 20 0 4164720 734540 26624 S 0.0 9.9 0:00.96 mysqld
8159 mysql 20 0 4164720 734540 26624 S 0.0 9.9 0:00.02 ib_io_ibuf
8160 mysql 20 0 4164720 734540 26624 S 0.0 9.9 0:00.02 ib_io_log
8161 mysql 20 0 4164720 734540 26624 S 0.0 9.9 0:00.04 ib_io_rd-1
8162 mysql 20 0 4164720 734540 26624 S 0.0 9.9 0:00.03 ib_io_rd-2
...
後來裝了一個8.0.28才發現確實是官方版本的新玩意。但是雖然能夠猜到一些線程的功能,可還是很陌生的樣子,因為這個名字和performance_schema.thread中的名字並不一樣。
這裏我們就來看看它的做法和對應關係。 不過這一小步,卻是DBA的一大步 ,我們以往在看Oracle的進程的時候都習慣了有命名的進程名字,這帶來的好處是直接從OS層面就能判斷大概哪個功能的壓力增高。
二、以往的對應方法
我們知道以前在獲取到線程的LWP號後需要到performance_schema.thread通過lwp和thread_os_id 對應,得到如下結果:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
5524 mysql 20 0 4052588 792400 11676 S 0.0 27.4 0:03.82 mysqld
5533 mysql 20 0 4052588 792400 11676 S 0.0 27.4 0:00.00 mysqld
5556 mysql 20 0 4052588 792400 11676 S 0.0 27.4 0:00.00 mysqld
5557 mysql 20 0 4052588 792400 11676 S 0.0 27.4 0:00.00 mysqld
...
----------------------------------------+--------------+
| name | thread_os_id |
+----------------------------------------+--------------+
| thread/sql/main | 5524 |
| thread/sql/thread_timer_notifier | 5533 |
| thread/innodb/io_ibuf_thread | 5556 |
| thread/innodb/io_read_thread | 5558 |
| thread/innodb/io_log_thread | 5557 |
...
如果某個線程的CPU高或者IO高我們就能夠知道是什麼線程。當然你也可以和information_schema.processlist做join得到process id和state等有用的信息。
三、簡單的實現方法討論
比如以innodb為例,所有的線程的OS thread name都放到了all_innodb_threads這個一個數組中,其中每個元素是一個結構體,結構體中包含了我們OS thread name這個元素給予了大量的代碼註釋,我就放一點我們容易看懂的:
typedef struct PSI_thread_info_v5 PSI_thread_info;
The thread name to advertise to the operating system.
This feature is optional, and improves
observability for platforms that support
a flavor of pthread_setname_np().
這裏我們也看到需要支持pthread_setname_np函數才行。在調用register_thread_class註冊所有的class的時候會將這些OS thread name放到一個叫做thread_class_array全局內存中,這樣再建立線程應該能輕鬆的從全局內存中拿到每個線程的OS thread name(當然我沒去細看了)。接着,在建立線程的時候我們調用my_thread_self_setname設置OS thread name就可以了,實際上就是調用pthread_setname_np。
四、新的Linux OS thread name和performance_schema.threads中name的對應
為了快速的得到對應的辦法,我稍微加了點輸出內容,這樣只要有線程啟動,就會打印到error日誌, 因為如果一個一個去看每個線程啟動的時候帶入的OS thread name實在太慢了,耗不起 ,。得到的結果如下:
cat mysql3380.err |grep -w 'init threads'
[init threads] os name:boot -- mysql name:thread/sql/bootstrap
[init threads] os name:ib_io_ibuf -- mysql name:thread/innodb/io_ibuf_thread
[init threads] os name:ib_io_log -- mysql name:thread/innodb/io_log_thread
[init threads] os name:ib_io_rd-1 -- mysql name:thread/innodb/io_read_thread
[init threads] os name:ib_io_rd-2 -- mysql name:thread/innodb/io_read_thread
[init threads] os name:ib_io_rd-3 -- mysql name:thread/innodb/io_read_thread
[init threads] os name:ib_io_rd-4 -- mysql name:thread/innodb/io_read_thread
[init threads] os name:ib_io_rd-5 -- mysql name:thread/innodb/io_read_thread
[init threads] os name:ib_io_rd-6 -- mysql name:thread/innodb/io_read_thread
[init threads] os name:ib_io_rd-7 -- mysql name:thread/innodb/io_read_thread
[init threads] os name:ib_io_rd-8 -- mysql name:thread/innodb/io_read_thread
[init threads] os name:ib_io_wr-1 -- mysql name:thread/innodb/io_write_thread
[init threads] os name:ib_io_wr-2 -- mysql name:thread/innodb/io_write_thread
[init threads] os name:ib_io_wr-3 -- mysql name:thread/innodb/io_write_thread
[init threads] os name:ib_io_wr-4 -- mysql name:thread/innodb/io_write_thread
[init threads] os name:ib_pg_flush_co -- mysql name:thread/innodb/page_flush_coordinator_thread
[init threads] os name:ib_pg_flush-1 -- mysql name:thread/innodb/page_flush_thread
[init threads] os name:ib_pg_flush-2 -- mysql name:thread/innodb/page_flush_thread
[init threads] os name:ib_pg_flush-3 -- mysql name:thread/innodb/page_flush_thread
[init threads] os name:ib_recv_write -- mysql name:thread/innodb/recv_writer_thread
[init threads] os name:ib_log_checkpt -- mysql name:thread/innodb/log_checkpointer_thread
[init threads] os name:ib_log_fl_notif -- mysql name:thread/innodb/log_flush_notifier_thread
[init threads] os name:ib_log_flush -- mysql name:thread/innodb/log_flusher_thread
[init threads] os name:ib_log_wr_notif -- mysql name:thread/innodb/log_write_notifier_thread
[init threads] os name:ib_log_writer -- mysql name:thread/innodb/log_writer_thread
[init threads] os name:ib_par_rseg-0 -- mysql name:thread/innodb/parallel_rseg_init_thread
[init threads] os name:ib_par_rseg-0 -- mysql name:thread/innodb/parallel_rseg_init_thread
[init threads] os name:ib_srv_lock_to -- mysql name:thread/innodb/srv_lock_timeout_thread
[init threads] os name:ib_srv_err_mon -- mysql name:thread/innodb/srv_error_monitor_thread
[init threads] os name:ib_srv_mon -- mysql name:thread/innodb/srv_monitor_thread
[init threads] os name:ib_buf_resize -- mysql name:thread/innodb/buf_resize_thread
[init threads] os name:ib_src_main -- mysql name:thread/innodb/srv_master_thread
[init threads] os name:ib_dict_stats -- mysql name:thread/innodb/dict_stats_thread
[init threads] os name:ib_fts_opt -- mysql name:thread/innodb/fts_optimize_thread
[init threads] os name:xpl_worker-1 -- mysql name:thread/mysqlx/worker
[init threads] os name:xpl_worker-2 -- mysql name:thread/mysqlx/worker
[init threads] os name:xpl_accept-1 -- mysql name:thread/mysqlx/acceptor_network
[init threads] os name:ib_buf_dump -- mysql name:thread/innodb/buf_dump_thread
[init threads] os name:ib_clone_gtid -- mysql name:thread/innodb/clone_gtid_thread
[init threads] os name:ib_srv_purge -- mysql name:thread/innodb/srv_purge_thread
[init threads] os name:ib_srv_wkr-1 -- mysql name:thread/innodb/srv_worker_thread
[init threads] os name:ib_srv_wkr-2 -- mysql name:thread/innodb/srv_worker_thread
[init threads] os name:ib_srv_wkr-3 -- mysql name:thread/innodb/srv_worker_thread
[init threads] os name:sig_handler -- mysql name:thread/sql/signal_handler
[init threads] os name:xpl_accept-2 -- mysql name:thread/mysqlx/acceptor_network
[init threads] os name:xpl_accept-3 -- mysql name:thread/mysqlx/acceptor_network
[init threads] os name:gtid_zip -- mysql name:thread/sql/compress_gtid_table
[init threads] os name:connection -- mysql name:thread/sql/one_connection
可以看到建立的線程非常的多,但是我們得到它們的對應關係這就夠了。這裏不一一討論每個線程的功能了,不過大部分我們都非常熟悉了,比如purge線程/cleaner線程 ,這裏我列出一些,其他的就自己看看吧。
-
purge線程 srv_purge_thread 主要用於清理delete flag和釋放undo表空間
-
clean線程 page_cleaner_thread DBWR 主要用於進行髒數據的刷盤和LRU鏈表的管理
-
異步IO線程 io_read_thread/io_write_thread 通常數據預讀和刷髒會使用到異步AIO,用於合併可能的散列IO為連續IO提高性能
-
字典收集線程 dict_stats_thread 數據修改的10%後會觸發統計數據的收集
-
鎖超時監控線程srv_lock_timeout_thread 用於監控innodb行鎖的超時,超時進行事務回退
-
GTID壓縮線程compress_gtid_table 將歷史的gtid壓縮為範圍,避免gtid_executed表過長
-
slave io線程 slave_io 從庫接收來自DUMP線程的binlog Event。將這些Event寫入到relay log。
-
slave dump線程 這是前台線程 主庫監控binlog的變化,發送binlog Event
-
slave sql線程 slave_sql 從庫負責執行binlog Event
-
用户線程one_connection 一個session就是一個用户線程,對於用户線程而言。可以通過processlist_id和show processlist
-
srv_error_monitor_thread 所謂的信號量監控線程,注意這個信號量不是OS的信號量。是Innodb內部的rw lock和mutex
-
srv_monitor_thread 這個線程當參數innodb_status_output打開的情況下,每15秒輸出一個show engine innodb status信息到日誌文件。也會自動開啟比如Innodb內存不足會自動開啟。
-
log_writer線程:將redo寫到redo文件
-
log_write_notifier線程:通知用户會話寫入redo文件結束
-
log_flusher線程:將redo fsync到redo文件
-
log_flush_notifier線程:通知用户會話fsync結束
-
log_checkpointer線程:定期檢查髒數據寫盤的redo 位置。
五、用pthread_setname_np為線程命名
這裏我就隨便寫了4個循環的線程調用這個函數為我的線程命名為D-GPWK,需要耗用較高的CPU,看到的結果如下:
![](http://mdimg.wxwenku.com/getimg/6b990ce30fa9193e296dd37902816f4b413e91acd65575e1d554f1a5a6f5581f5ac994c690c977ecdff5b2ca53126c38.jpg)
如果這個線程是MySQL的線程,當看到這個結果,我們就能明白大概的方向了(卧槽,慢SQL吧^_^)。
如果覺得本文有用,感謝各位朋友的轉發
- 技術分享 | orchestrator--運維--配置集羣自動切換&測試
- AIOPS的莫拉維克悖論
- 詳談 MySQL 8.0 原子 DDL 原理
- 為什麼不建議用 from xxx import *
- 最近解決的兩個拖延數年的問題
- Oracle數據庫解決方案集錦
- 新一代雲原生數據庫暢想
- MySQL8.0賬户system_user權限,你瞭解嗎?
- Data Fabric,下一個風口?
- 帶着孩子做開學準備清單
- 十多年前的入職第一天
- 技術分享 | MySQL 編寫腳本時避免煩人的警吿
- GoldenGate案例一則:抽取進程無法捕獲數據
- 技術分享 | MySQL 設置管理員密碼無法生效一例
- PG數據庫的鎖咋弄得這麼複雜呢
- 金融業分佈式數據庫選型及HTAP場景實踐
- 我們的企業為什麼寫不好文檔
- 新數據庫時代,DBA 發展之路該如何選擇
- MySQL:修改系統時鐘會導致數據庫hang住嗎?
- 從程序員的盡頭是業務説起