SHOW PROCESSLIST 最多能顯示多長的 SQL?

iVictor發表於2024-06-17

在 MySQL 中,如果我們想檢視例項當前正在執行的 SQL,常用的命令是SHOW PROCESSLIST

但如果 SQL 過長的話,就會被截斷。這時,我們一般會用SHOW FULL PROCESSLIST來檢視完整的 SQL。

最近碰到一個 case,發現無論是使用 SHOW PROCESSLISTSHOW FULL PROCESSLIST,還是 performance_schema.processlist 都無法獲取完整的 SQL,反倒 information_schema.processlist 可以。

最後發現,SHOW FULL PROCESSLIST無法檢視完整的 SQL 竟然與 performance_schema_show_processlist 引數有關。

SHOW PROCESSLIST 的實現方式

SHOW PROCESSLIST預設是從執行緒管理器(Global_THD_manager)中獲取執行緒資訊。

這種實現方式會持有全域性互斥鎖。在負載高的場景,會對資料庫的效能有一定的負面影響。

所以一般都推薦使用 performance_schema.processlist,這種方式不會持有全域性鎖。

在 MySQL 8.0.22 中,引入了 performance_schema_show_processlist 引數,可設定SHOW PROCESSLIST的實現方式。

當該引數設定為 ON 時,會使用 performance_schema.processlist 的實現方式。

當引數設定為 OFF(預設值) 時,會使用傳統的實現方式。

三種不同查詢方式對 SQL 的長度限制

下面我們看看 SHOW PROCESSLIST, information_schema.processlist, performance_schema.processlist 這三種不同的查詢方式對 SQL 的長度限制。

因為 SQL 是在 INFO 列中儲存的,所以,SQL 的最大長度實際上是由 INFO 列決定的。

SHOW PROCESSLIST

SHOW PROCESSLIST的輸出中 INFO 列的最大長度與 performance_schema_show_processlist 有關。

當 performance_schema_show_processlist 等於 OFF 時,無論是SHOW PROCESSLIST,還是SHOW FULL PROCESSLIST,呼叫的都是 mysqld_list_processes 函式。

// sql/sql_show.cc
void mysqld_list_processes(THD *thd, const char *user, bool verbose,
bool has_cursor) {
Item *field;
mem_root_deque<Item *> field_list(thd->mem_root);
Thread_info_array thread_infos(thd->mem_root);
size_t max_query_length =
(verbose ? thd->variables.max_allowed_packet : PROCESS_LIST_WIDTH);
Protocol *protocol = thd->get_protocol();
DBUG_TRACE;

...
field_list.push_back(field = new Item_empty_string("Info", max_query_length));
...
}

可以看到,INFO 列的最大長度由 max_query_length 決定,而 max_query_length 的取值又與 verbose 有關。

當執行SHOW FULL PROCESSLIST時, verbose 為 true,此時 max_query_length 等於 max_allowed_packet。

當執行SHOW PROCESSLIST時,verbose 為 false,此時 max_query_length 等於 PROCESS_LIST_WIDTH,而 PROCESS_LIST_WIDTH 是個常量,大小為 100。

constexpr const size_t PROCESS_LIST_WIDTH{100};

當 performance_schema_show_processlist 等於 ON 時,無論是SHOW PROCESSLIST,還是SHOW FULL PROCESSLIST,呼叫的都是build_processlist_query 函式。

// sql/sql_show_processlist.cc
bool build_processlist_query(const POS &pos, THD *thd, bool verbose) {
LEX_STRING info_len;
/*
Default Info field length is 100. Verbose field length is limited to the
size of the INFO columns in the Performance Schema.
*/
assert(PROCESS_LIST_WIDTH == 100);
if (verbose) {
if (lex_string_strmake(thd->mem_root, &info_len, "1024", 4)) return true;
} else {
if (lex_string_strmake(thd->mem_root, &info_len, "100", 3)) return true;
}
...
/* Info length is either "100" or "1024" depending on verbose */
Item_int *item_info_len = new (thd->mem_root) Item_int(pos, info_len);
if (item_info_len == nullptr) return true;
...
}

當執行SHOW FULL PROCESSLIST時, verbose 為 true,此時 info_len 等於 1024。

當執行SHOW PROCESSLIST時,verbose 為 false,此時 info_len 等於 100。

information_schema.processlist

information_schema.processlist 表中 INFO 的長度是在Fill_process_list中限制的。

// sql/sql_show.cc
class Fill_process_list : public Do_THD_Impl {
...
void operator()(THD *inspect_thd) override {

/* INFO */
mysql_mutex_lock(&inspect_thd->LOCK_thd_query);
{
const char *query_str = nullptr;
size_t query_length = 0;
...
if (query_str) {
const size_t width = min<size_t>(PROCESS_LIST_INFO_WIDTH, query_length);
table->field[7]->store(query_str, width, inspect_thd->charset());
table->field[7]->set_notnull();
}
}
...
}
};

INFO 的長度取 PROCESS_LIST_INFO_WIDTH 和 query_length(SQL 的實際長度)的最小值。

所以,INFO 列的最大長度由 PROCESS_LIST_INFO_WIDTH 決定,而 PROCESS_LIST_INFO_WIDTH 也是個常量,值為 65535。

constexpr const size_t PROCESS_LIST_INFO_WIDTH{65535};

performance_schema.processlist

performance_schema.processlist 表中 INFO 的長度是在pfs_set_thread_info_vc中限制的。

// storage/perfschema/pfs.cc
void pfs_set_thread_info_vc(const char *info, uint info_len) {
pfs_dirty_state dirty_state;
PFS_thread *pfs = my_thread_get_THR_PFS();

if (likely(pfs != nullptr)) {
if (info_len > sizeof(pfs->m_processlist_info)) {
info_len = sizeof(pfs->m_processlist_info);
}
pfs->m_stmt_lock.allocated_to_dirty(&dirty_state);
if (info != nullptr && info_len > 0) {
memcpy(pfs->m_processlist_info, info, info_len);
}
pfs->m_processlist_info_length = info_len;
pfs->m_stmt_lock.dirty_to_allocated(&dirty_state);
}
}

可以看到,INFO 列的最大長度是由 pfs->m_processlist_info 決定的,而 m_processlist_info 是個字元陣列,它的最大長度由 COL_INFO_SIZE 決定。

而 COL_INFO_SIZE 又等於 COL_INFO_CHAR_SIZE * 1。其中,COL_INFO_CHAR_SIZE 是一個常量,值為 1024。

// storage/perfschema/pfs_instr.h
char m_processlist_info[COL_INFO_SIZE];

// storage/perfschema/pfs_column_types.h
#define COL_INFO_CHAR_SIZE 1024
/** Size of INFO columns, in bytes. */
#define COL_INFO_SIZE (COL_INFO_CHAR_SIZE * 1)

結論

  1. 如果查詢的是 information_schema.processlist, INFO 列的最大長度是 65535。

  2. 如果查詢的是 performance_schema.processlist,INFO 列的最大長度是 1024。

  3. 如果執行的是SHOW PROCESSLIST,無論 performance_schema_show_processlist 是 ON 還是 OFF, INFO 列的最大長度都是 100。

  4. 如果執行的是SHOW FULL PROCESSLIST

    • 當 performance_schema_show_processlist 等於 OFF 或 MySQL 8.0.22 之前的版本 , INFO 列的最大長度等於 max_allowed_packet。
    • 當 performance_schema_show_processlist 等於 ON 時,INFO 列的最大長度是 1024。

相關文章