MySQL慢查詢記錄原理和內容解析

gaopengtttt發表於2018-06-05

MySQL慢查詢記錄原理和內容解析


我的學習記錄,可能有誤請諒解,也提供了一些原始碼介面供有興趣的朋友除錯。
原始碼版本:percona 5.7.14


本文並不準備說明如何開啟記錄慢查詢,只是將一些重要的部分進行解析。如何記錄慢查詢可以自行參考官方文件:

  • 5.4.5 The Slow Query Log

本文使用了Percona 版本開啟來了引數log_slow_verbosity,得到了更詳細的慢查詢資訊。通常情況下資訊沒有這麼多,但是一定是包含關係,本文也會使用Percona的引數解釋說明一下這個引數的含義。

一、慢查詢中的時間

實際上慢查詢中的時間就是時鐘時間,是通過作業系統的命令獲得的時間,如下是Linux中獲取時間的方式

 while (gettimeofday(&t, NULL) != 0)
  {}
  newtime= (ulonglong)t.tv_sec * 1000000 + t.tv_usec; return newtime; 

實際上就是通過OS的API gettimeofday函式獲得的時間。

二、慢查詢記錄的依據

  1. long_query_time:如果執行時間超過本引數設定記錄慢查詢。
  2. log_queries_not_using_indexes:如果語句未使用索引記錄慢查詢。
  3. log_slow_admin_statements:是否記錄管理語句。(如ALTER TABLE,ANALYZE TABLE, CHECK TABLE, CREATE INDEX, DROP INDEX, OPTIMIZE TABLE, and REPAIR TABLE.)

本文主要討論long_query_time引數的含義。

三、long_query_time引數的具體含義

如果我們將語句的執行時間定義為如下:

實際消耗時間 = 實際執行時間+鎖等待消耗時間 

那麼long_query_time實際上界定的是實際執行時間,所以有些情況下雖然語句實際消耗的時間很長但是是因為鎖等待時間較長而引起的,那麼實際上這種語句也不會記錄到慢查詢。

我們看一下log_slow_applicable函式的程式碼片段:

res= cur_utime - thd->utime_after_lock; if (res > thd->variables.long_query_time)
    thd->server_status|= SERVER_QUERY_WAS_SLOW; else thd->server_status&= ~SERVER_QUERY_WAS_SLOW; 

這裡實際上清楚的說明了上面的觀點,是不是慢查詢就是通過這個函式進行的判斷的,非常重要。我可以清晰的看到如下公式:

  • res (實際執行時間 ) = cur_utime(實際消耗時間) - thd->utime_after_lock( 鎖等待消耗時間)

實際上在慢查詢中記錄的正是

  • Query_time:實際執行時間
  • Lock_time:鎖等待消耗時間

但是是否是慢查詢其評判標準卻是實際執行時間及Query_time - Lock_time

其中鎖等待消耗時間( Lock_time)我現在已經知道的包括:

  1. MySQL層 MDL LOCK等待消耗的時間。(Waiting for table metadata lock)
  2. MySQL層 MyISAM表鎖消耗的時間。 (Waiting for table level lock)
  3. InnoDB層 行鎖消耗的時間。

四、MySQL是如何記錄鎖時間

我們可以看到在公式中utime_after_lock( 鎖等待消耗時間Lock_time)的記錄也就成了整個公式的關鍵,那麼我們試著進行debug。

1、MySQL層utime_after_lock的記錄方式

不管是 MDL LOCK等待消耗的時間還是 MyISAM表鎖消耗的時間都是在MySQL層記錄的,實際上它只是記錄在函式mysql_lock_tables的末尾會呼叫的THD::set_time_after_lock進行的記錄時間而已如下:

void set_time_after_lock() {
    utime_after_lock= my_micro_time();
    MYSQL_SET_STATEMENT_LOCK_TIME(m_statement_psi, (utime_after_lock - start_utime));
  } 

那麼這裡可以解析為程式碼執行到mysql_lock_tables函式的末尾之前的所有的時間都記錄到utime_after_lock時間中,實際上並不精確。但是實際上MDL LOCK的獲取和MyISAM表鎖的獲取都包含在裡面。所以即便是select語句也會看到Lock_time並不為0。下面是棧幀:

#0  THD::set_time_after_lock (this=0x7fff28012820) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_class.h:3414 #1  0x0000000001760d6d in mysql_lock_tables (thd=0x7fff28012820, tables=0x7fff28c16b58, count=1, flags=0) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/lock.cc:366 #2  0x000000000151dc1a in lock_tables (thd=0x7fff28012820, tables=0x7fff28c165b0, count=1, flags=0) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_base.cc:6700 #3  0x00000000017c4234 in Sql_cmd_delete::mysql_delete (this=0x7fff28c16b50, thd=0x7fff28012820, limit=18446744073709551615) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_delete.cc:136 #4  0x00000000017c84ba in Sql_cmd_delete::execute (this=0x7fff28c16b50, thd=0x7fff28012820) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_delete.cc:1389 #5  0x00000000015a7814 in mysql_execute_command (thd=0x7fff28012820, first_level=true) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_parse.cc:3729 #6  0x00000000015adcd6 in mysql_parse (thd=0x7fff28012820, parser_state=0x7ffff035b600) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_parse.cc:5836 #7  0x00000000015a1b95 in dispatch_command (thd=0x7fff28012820, com_data=0x7ffff035bd70, command=COM_QUERY) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_parse.cc:1447 #8  0x00000000015a09c6 in do_command (thd=0x7fff28012820) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_parse.cc:1010 
2、InnoDB層的行鎖的utime_after_lock記錄方式

InnoDB引擎層呼叫通過thd_set_lock_wait_time呼叫thd_storage_lock_wait函式完成的棧幀如下:

#0  thd_storage_lock_wait (thd=0x7fff2c000bc0, value=9503561) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_class.cc:798 #1  0x00000000019a4b2a in thd_set_lock_wait_time (thd=0x7fff2c000bc0, value=9503561) at /root/mysql5.7.14/percona-server-5.7.14-7/storage/innobase/handler/ha_innodb.cc:1784 #2  0x0000000001a4b50f in lock_wait_suspend_thread (thr=0x7fff2c088200) at /root/mysql5.7.14/percona-server-5.7.14-7/storage/innobase/lock/lock0wait.cc:363 #3  0x0000000001b0ec9b in row_mysql_handle_errors (new_err=0x7ffff0317d54, trx=0x7ffff2f2e5d0, thr=0x7fff2c088200, savept=0x0) at /root/mysql5.7.14/percona-server-5.7.14-7/storage/innobase/row/row0mysql.cc:772 #4  0x0000000001b4fe61 in row_search_mvcc (buf=0x7fff2c087640 "\377", mode=PAGE_CUR_G, prebuilt=0x7fff2c087ac0, match_mode=0, direction=0) at /root/mysql5.7.14/percona-server-5.7.14-7/storage/innobase/row/row0sel.cc:5940 #5  0x00000000019b3051 in ha_innobase::index_read (this=0x7fff2c087100, buf=0x7fff2c087640 "\377", key_ptr=0x0, key_len=0, find_flag=HA_READ_AFTER_KEY) at /root/mysql5.7.14/percona-server-5.7.14-7/storage/innobase/handler/ha_innodb.cc:9104 #6  0x00000000019b4374 in ha_innobase::index_first (this=0x7fff2c087100, buf=0x7fff2c087640 "\377") at /root/mysql5.7.14/percona-server-5.7.14-7/storage/innobase/handler/ha_innodb.cc:9551 #7  0x00000000019b462c in ha_innobase::rnd_next (this=0x7fff2c087100, buf=0x7fff2c087640 "\377") at /root/mysql5.7.14/percona-server-5.7.14-7/storage/innobase/handler/ha_innodb.cc:9656 #8  0x0000000000f66f1b in handler::ha_rnd_next (this=0x7fff2c087100, buf=0x7fff2c087640 "\377") at /root/mysql5.7.14/percona-server-5.7.14-7/sql/handler.cc:3099 #9  0x00000000014c61b6 in rr_sequential (info=0x7ffff03189e0) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/records.cc:520 #10 0x00000000017c56c3 in Sql_cmd_delete::mysql_delete (this=0x7fff2c006ae8, thd=0x7fff2c000bc0, limit=1) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_delete.cc:454 #11 0x00000000017c84ba in Sql_cmd_delete::execute (this=0x7fff2c006ae8, thd=0x7fff2c000bc0) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_delete.cc:1389 

函式本身還是很簡單自己看看就知道了就是相加而已如下:

void thd_storage_lock_wait(THD *thd, long long value) {
  thd->utime_after_lock+= value;
} 

五、Percona中的log_slow_verbosity引數

這是Percona的解釋:

Specifies how much information to include in your slow log. The value is a comma-delimited string, and can contain any combination of the following values:

  • microtime: Log queries with microsecond precision (mandatory).
  • query_plan: Log information about the query’s execution plan (optional).
  • innodb: Log InnoDB statistics (optional).
  • minimal: Equivalent to enabling just microtime.
  • standard: Equivalent to enabling microtime,innodb.
  • full: Equivalent to all other values OR’ed together.

總之在Percona中可以修改這個引數獲得更加詳細的資訊大概的格式如下:

# Time: 2018-05-30T09:30:12.039775Z # User@Host: root[root] @ localhost []  Id:    10 # Schema: test  Last_errno: 1317  Killed: 0 # Query_time: 19.254508  Lock_time: 0.001043  Rows_sent: 0  Rows_examined: 0  Rows_affected: 0 # Bytes_sent: 44  Tmp_tables: 0  Tmp_disk_tables: 0  Tmp_table_sizes: 0 # InnoDB_trx_id: 0 # QC_Hit: No  Full_scan: No  Full_join: No  Tmp_table: No  Tmp_table_on_disk: No # Filesort: No  Filesort_on_disk: No  Merge_passes: 0 #   InnoDB_IO_r_ops: 0  InnoDB_IO_r_bytes: 0  InnoDB_IO_r_wait: 0.000000 #   InnoDB_rec_lock_wait: 0.000000  InnoDB_queue_wait: 0.000000 #   InnoDB_pages_distinct: 0 SET timestamp=1527672612;
select count(*) from z1 limit 1; 

六、輸出的詳細解釋

本節將會進行詳細的解釋,全部的慢查詢的輸出都來自於函式File_query_log::write_slow ,有興趣的同學可以自己看看,我這裡也會給出輸出的位置和含義,其中含義部分可能給出的是原始碼中的註釋。

1、第一部分時間
# Time: 2018-05-30T09:30:12.039775Z 

對應的程式碼:

my_snprintf(buff, sizeof buff,"# Time: %s\n", my_timestamp); 

其中my_timestamp取值來自於

thd->current_utime(); 

實際上就是:

 while (gettimeofday(&t, NULL) != 0)
  {}
  newtime= (ulonglong)t.tv_sec * 1000000 + t.tv_usec; return newtime; 

可以看到實際就是呼叫gettimeofday系統呼叫得到的系統當前時間。

注意:
對於5.6來講還有一句判斷

if (current_time != last_time) 

如果兩次列印的時間秒鐘一致則不會輸出時間,只有通過後面介紹的

SET timestamp=1527753496; 

來判斷時間,5.7.14沒有看到這樣的程式碼。

2、第二部分使用者資訊
# User@Host: root[root] @ localhost []  Id:    10 

對應的程式碼:

 buff_len= my_snprintf(buff, 32, "%5u", thd->thread_id()); if (my_b_printf(&log_file, "# User@Host: %s  Id: %s\n", user_host, buff)
        == (uint) -1) goto err;
  } 

user_host是一串字串,參考程式碼:

size_t user_host_len= (strxnmov(user_host_buff, MAX_USER_HOST_SIZE,
                                  sctx->priv_user().str
                                  ? sctx->priv_user().str : "", "[", sctx_user.length ? sctx_user.str :
                                  (thd->slave_thread ? "SQL_SLAVE" : ""), "] @ ",
                                  sctx_host.length ? sctx_host.str : "", " [",
                                  sctx_ip.length ? sctx_ip.str : "", "]",
                                  NullS) - user_host_buff); 

解釋如下:

  • root: m_priv_user - The user privilege we are using. May be "" for anonymous user。
  • [root]: m_user - user of the client, set to NULL until the user has been read from the connection。
  • localhost: m_host - host of the client。
  • []:client IP m_ip - client IP。
  • Id: 10 thd->thread_id()實際上就是show processlist出來的id。
3、第三部分schema等資訊
# Schema: test  Last_errno: 1317  Killed: 0 

對應的程式碼:

 "# Schema: %s  Last_errno: %u  Killed: %u\n" (thd->db().str ? thd->db().str : ""),
  thd->last_errno, (uint) thd->killed, 
  • Schema:
    m_db Name of the current (default) database.If there is the current (default) database, "db" contains its name. If there is no current (default) database, "db" is NULL and "db_length" is 0. In other words, "db", "db_length" must either be NULL, or contain a valid database name.

  • Last_errno:
    Variable last_errno contains the last error/warning acquired during query execution.

  • Killed: 這裡代表的是終止的錯誤碼。原始碼中如下:
    enum killed_state
    {
    NOT_KILLED=0,
    KILL_BAD_DATA=1,
    KILL_CONNECTION=ER_SERVER_SHUTDOWN,
    KILL_QUERY=ER_QUERY_INTERRUPTED,
    KILL_TIMEOUT=ER_QUERY_TIMEOUT,
    KILLED_NO_VALUE /* means neither of the states */
    };
    在錯誤碼中代表如下:
    { "ER_SERVER_SHUTDOWN", 1053, "Server shutdown in progress" },
    { "ER_QUERY_INTERRUPTED", 1317, "Query execution was interrupted" },
    { "ER_QUERY_TIMEOUT", 1886, "Query execution was interrupted, max_statement_time exceeded" },

4、第四部分執行資訊

這部分可能是大家最關心的部分,很多資訊也是預設輸出都會輸出的。

# Query_time: 19.254508  Lock_time: 0.001043  Rows_sent: 0  Rows_examined: 0  Rows_affected: 0 # Bytes_sent: 44  Tmp_tables: 0  Tmp_disk_tables: 0  Tmp_table_sizes: 0 # InnoDB_trx_id: 0 

對應程式碼:

my_b_printf(&log_file, "# Schema: %s  Last_errno: %u  Killed: %u\n" "# Query_time: %s  Lock_time: %s  Rows_sent: %llu" "  Rows_examined: %llu  Rows_affected: %llu\n" "# Bytes_sent: %lu",
                  (thd->db().str ? thd->db().str : ""),
                  thd->last_errno, (uint) thd->killed,
                  query_time_buff, lock_time_buff,
                  (ulonglong) thd->get_sent_row_count(),
                  (ulonglong) thd->get_examined_row_count(),
                  (thd->get_row_count_func() > 0)
                  ? (ulonglong) thd->get_row_count_func() : 0,
                  (ulong) (thd->status_var.bytes_sent - thd->bytes_sent_old)
my_b_printf(&log_file, "  Tmp_tables: %lu  Tmp_disk_tables: %lu  " "Tmp_table_sizes: %llu",
                    thd->tmp_tables_used, thd->tmp_tables_disk_used,
                    thd->tmp_tables_size)
snprintf(buf, 20, "%llX", thd->innodb_trx_id);及thd->innodb_trx_id 
  • Query_time:語句執行的時間及實際消耗時間 。
  • Lock_time:包含MDL lock和InnoDB row lock和MyISAM表鎖消耗時間的總和及鎖等待消耗時間。前面已經進行了描述(實際上也並不全是鎖等待的時間只是鎖等待包含在其中)。
我們來看看Query_time和Lock_time的原始碼來源,它們來自於Query_logger::slow_log_write函式如下:

    query_utime= (current_utime > thd->start_utime) ?
      (current_utime - thd->start_utime) : 0;
    lock_utime=  (thd->utime_after_lock > thd->start_utime) ?
      (thd->utime_after_lock - thd->start_utime) : 0;

下面是資料current_utime 的來源,

current_utime= thd->current_utime();
實際上就是: while (gettimeofday(&t, NULL) != 0)
  {}
  newtime= (ulonglong)t.tv_sec * 1000000 + t.tv_usec; return newtime;
獲取當前時間而已

對於thd->utime_after_lock的獲取我已經在前文進行了描述,不再解釋。 
  • Rows_sent:傳送給mysql客戶端的行數,下面是原始碼中的解釋
    Number of rows we actually sent to the client

  • Rows_examined:InnoDB引擎層掃描的行數,下面是原始碼中的解釋。(備註棧幀1)
    Number of rows read and/or evaluated for a statement. Used for slow log reporting.
    An examined row is defined as a row that is read and/or evaluated
    according to a statement condition, including increate_sort_index(). Rows may be counted more than once, e.g., a statement including ORDER BY could possibly evaluate the row in filesort() before reading it for e.g. update.

  • Rows_affected:涉及到修改的話(比如DML語句)這是受影響的行數。
    for DML statements: to the number of affected rows;
    for DDL statements: to 0.

  • Bytes_sent:傳送給客戶端的實際資料的位元組數,它來自於
    (ulong) (thd->status_var.bytes_sent - thd->bytes_sent_old)

  • Tmp_tables:臨時表的個數。

  • Tmp_disk_tables:磁碟臨時表的個數。

  • Tmp_table_sizes:臨時表的大小。

以上三個指標來自於:

thd->tmp_tables_used
thd->tmp_tables_disk_used
thd->tmp_tables_size 

這三個指標增加的位置對應在free_tmp_table函式中如下:

 thd->tmp_tables_used++; if (entry->file)
  {
      thd->tmp_tables_size += entry->file->stats.data_file_length; if (entry->file->ht->db_type != DB_TYPE_HEAP)
          thd->tmp_tables_disk_used++;
  } 
  • InnoDB_trx_id:事物ID,也就是trx->id,/*!< transaction id */
5、第五部分優化器相關資訊
# QC_Hit: No  Full_scan: No  Full_join: No  Tmp_table: No  Tmp_table_on_disk: No # Filesort: No  Filesort_on_disk: No  Merge_passes: 0 

這一行來自於如下程式碼:

 my_b_printf(&log_file, "# QC_Hit: %s  Full_scan: %s  Full_join: %s  Tmp_table: %s  " "Tmp_table_on_disk: %s\n" \ "# Filesort: %s  Filesort_on_disk: %s  Merge_passes: %lu\n",
                  ((thd->query_plan_flags & QPLAN_QC) ? "Yes" : "No"),
                  ((thd->query_plan_flags & QPLAN_FULL_SCAN) ? "Yes" : "No"),
                  ((thd->query_plan_flags & QPLAN_FULL_JOIN) ? "Yes" : "No"),
                  ((thd->query_plan_flags & QPLAN_TMP_TABLE) ? "Yes" : "No"),
                  ((thd->query_plan_flags & QPLAN_TMP_DISK) ? "Yes" : "No"),
                  ((thd->query_plan_flags & QPLAN_FILESORT) ? "Yes" : "No"),
                  ((thd->query_plan_flags & QPLAN_FILESORT_DISK) ? "Yes" : "No"), 

這裡注意一個處理的技巧,這裡query_plan_flags中每一位都代表一個含義,這樣儲存既能儲存足夠多的資訊同時儲存空間也很小,是C/C++中常用的方式。

  • QC_Hit: No:是否query cache命中。
  • Full_scan: 此處相當於Select_scan 的含義,是否進行了全掃描包括using index。
  • Full_join: 此處相當於Select_full_join 的含義,是否被驅動表使用到了索引,如果沒有使用到索引則為YES。

考慮如下的執行計劃

mysql> desc select *,sleep(1) from testuin a,testuin1 b where a.id1=b.id1;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| 1 | SIMPLE      | a | NULL       | ALL | NULL          | NULL | NULL    | NULL |    5 | 100.00 | NULL                                               | |  1 | SIMPLE | b     | NULL | ALL  | NULL | NULL | NULL | NULL | 5 |    20.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
2 rows in set, 1 warning (0.00 sec) 

如此輸出如下:

# QC_Hit: No  Full_scan: Yes  Full_join: Yes 
  • Tmp_table:是否使用了臨時表,在函式create_tmp_table中設定。
  • Tmp_table_on_disk:是否使用了磁碟臨時表,如果時候innodb引擎則在create_innodb_tmp_table函式中設定。
  • Filesort:是否進行了排序,在函式filesort中設定。
  • Filesort_on_disk:是否使用了磁碟排序,同樣在函式filesort中設定,但是設定之前會進行是否需要磁碟排序檔案的判斷。
  • Merge_passes: 進行多路歸併排序,歸併的次數。
    Variable query_plan_fsort_passes collects information about file sort passes
    acquired during query execution.
6、第六部分InnoDB相關資訊
#   InnoDB_IO_r_ops: 0  InnoDB_IO_r_bytes: 0  InnoDB_IO_r_wait: 0.000000 #   InnoDB_rec_lock_wait: 0.000000  InnoDB_queue_wait: 0.000000 #   InnoDB_pages_distinct: 0 

這一行來自於如下程式碼:

 char buf[3][20];
    snprintf(buf[0], 20, "%.6f", thd->innodb_io_reads_wait_timer / 1000000.0);
    snprintf(buf[1], 20, "%.6f", thd->innodb_lock_que_wait_timer / 1000000.0);
    snprintf(buf[2], 20, "%.6f", thd->innodb_innodb_que_wait_timer / 1000000.0); if (my_b_printf(&log_file, "#   InnoDB_IO_r_ops: %lu  InnoDB_IO_r_bytes: %llu  " "InnoDB_IO_r_wait: %s\n" "#   InnoDB_rec_lock_wait: %s  InnoDB_queue_wait: %s\n" "#   InnoDB_pages_distinct: %lu\n",
                    thd->innodb_io_reads, thd->innodb_io_read,
                    buf[0], buf[1], buf[2], thd->innodb_page_access)
        == (uint) -1) 
  • InnoDB_IO_r_ops:物理IO讀取次數。
  • InnoDB_IO_r_bytes:物理IO讀取的總位元組數。
  • InnoDB_IO_r_wait:物理IO讀取等待的時間。innodb 使用 BUF_IO_READ標記為物理io讀取繁忙,參考函式buf_wait_for_read。
  • InnoDB_rec_lock_wait:等待行鎖消耗的時間。在函式que_thr_end_lock_wait中設定。
  • InnoDB_queue_wait: 等待進入innodb引擎消耗的時間,在函式srv_conc_enter_innodb_with_atomics中設定。(參考http://blog.itpub.net/7728585/viewspace-2140446/)
  • InnoDB_pages_distinct: innodb訪問的頁數,包含物理和邏輯IO,在函式buf_page_get_gen的末尾通過_increment_page_get_statistics函式設定。

7、第七部分set timestamp

SET timestamp=1527753496; 

這一句來自原始碼,注意原始碼註釋解釋就是獲取的伺服器的當前的時間(current_utime)。

/*
    This info used to show up randomly, depending on whether the query
    checked the query start time or not. now we always write current
    timestamp to the slow log
  */ end= my_stpcpy(end, ",timestamp=");
  end= int10_to_str((long) (current_utime / 1000000), end, 10); if (end != buff)
  {
    *end++=';';
    *end='\n'; if (my_b_write(&log_file, (uchar*) "SET ", 4) ||
        my_b_write(&log_file, (uchar*) buff + 1, (uint) (end-buff))) goto err;
  } 

七、總結
本文通過查詢原始碼解釋了一些關於MySQL慢查詢的相關的知識,主要解釋了慢查詢是基於什麼標準進行記錄的,同時輸出中各個指標的含義,當然這僅僅是我自己得出的結果,如果有不同意見可以一起討論。

備註棧幀1:
本棧幀主要跟蹤Rows_examined的變化及 join->examined_rows++;的變化

(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000ebd5f3 in main(int, char**) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/main.cc:25
        breakpoint already hit 1 time
4       breakpoint     keep y   0x000000000155b94f in do_select(JOIN*) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_executor.cc:872
        breakpoint already hit 5 times 5       breakpoint     keep y   0x000000000155ca39 in evaluate_join_record(JOIN*, QEP_TAB*) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_executor.cc:1473
        breakpoint already hit 20 times 6       breakpoint     keep y   0x00000000019b4313 in ha_innobase::index_first(uchar*)
                                               at /root/mysql5.7.14/percona-server-5.7.14-7/storage/innobase/handler/ha_innodb.cc:9547
        breakpoint already hit 4 times 7       breakpoint     keep y   0x00000000019b45cd in ha_innobase::rnd_next(uchar*)
                                               at /root/mysql5.7.14/percona-server-5.7.14-7/storage/innobase/handler/ha_innodb.cc:9651
8       breakpoint     keep y   0x00000000019b2ba6 in ha_innobase::index_read(uchar*, uchar const*, uint, ha_rkey_function)
                                               at /root/mysql5.7.14/percona-server-5.7.14-7/storage/innobase/handler/ha_innodb.cc:9004
        breakpoint already hit 3 times 9       breakpoint     keep y   0x00000000019b4233 in ha_innobase::index_next(uchar*)
                                               at /root/mysql5.7.14/percona-server-5.7.14-7/storage/innobase/handler/ha_innodb.cc:9501
        breakpoint already hit 5 times #0  ha_innobase::index_next (this=0x7fff2cbc6b40, buf=0x7fff2cbc7080 "\375\n") at /root/mysql5.7.14/percona-server-5.7.14-7/storage/innobase/handler/ha_innodb.cc:9501 #1  0x0000000000f680d8 in handler::ha_index_next (this=0x7fff2cbc6b40, buf=0x7fff2cbc7080 "\375\n") at /root/mysql5.7.14/percona-server-5.7.14-7/sql/handler.cc:3269 #2  0x000000000155fa02 in join_read_next (info=0x7fff2c007750) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_executor.cc:2660 #3  0x000000000155c397 in sub_select (join=0x7fff2c007020, qep_tab=0x7fff2c007700, end_of_records=false) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_executor.cc:1274 #4  0x000000000155bd06 in do_select (join=0x7fff2c007020) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_executor.cc:944 #5  0x0000000001559bdc in JOIN::exec (this=0x7fff2c007020) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_executor.cc:199 #6  0x00000000015f9ea6 in handle_query (thd=0x7fff2c000b70, lex=0x7fff2c003150, result=0x7fff2c006cd0, added_options=0, removed_options=0) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_select.cc:184 #7  0x00000000015acd05 in execute_sqlcom_select (thd=0x7fff2c000b70, all_tables=0x7fff2c006688) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_parse.cc:5391 #8  0x00000000015a5320 in mysql_execute_command (thd=0x7fff2c000b70, first_level=true) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_parse.cc:2889 #9  0x00000000015adcd6 in mysql_parse (thd=0x7fff2c000b70, parser_state=0x7ffff035b600) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_parse.cc:5836 #10 0x00000000015a1b95 in dispatch_command (thd=0x7fff2c000b70, com_data=0x7ffff035bd70, command=COM_QUERY) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_parse.cc:1447 #11 0x00000000015a09c6 in do_command (thd=0x7fff2c000b70) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_parse.cc:1010 #12 0x00000000016e29d0 in handle_connection (arg=0x3859ae0) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/conn_handler/connection_handler_per_thread.cc:312 #13 0x0000000001d7bfdc in pfs_spawn_thread (arg=0x38607b0) at /root/mysql5.7.14/percona-server-5.7.14-7/storage/perfschema/pfs.cc:2188 #14 0x0000003f74807aa1 in start_thread () from /lib64/libpthread.so.0 #15 0x0000003f740e8bcd in clone () from /lib64/libc.so.6 

作者微信:


微信.jpg

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/7728585/viewspace-2155643/,如需轉載,請註明出處,否則將追究法律責任。

相關文章