免责声明: 如果你看完之后觉得太简单了,那你就是我的大佬,如果发现我的错误是请斧正! 如果你看完之后觉得太难了,那你就是我在其他领域的大佬,比如建模领域?我有好多建模方面的盲区需要请教你! 如果你看完之后感叹:“这不就是我想的那样吗!”,对,你就是我要找的那个人!跟我半斤八两的人~ 😃 我的邮箱是:[email protected],随时联系我! 文中
...表示省略部分代码,但有的地方省略了也没有用...代替。😃
比如说,我要给show processlist加上连接创建的时间 CreateTime ,那它就是这样的:

要想加点东西,搞清楚原理是第一要务。除掉一些杂七杂八的过程,show processlist 的处理入口就是在Sql_cmd_show_processlist::execute_inner(THD *thd),直接来看这个函数(删节):
sql_show.cc:
bool Sql_cmd_show_processlist::execute_inner(THD *thd) {
if (use_pfs()) {
return Sql_cmd_show::execute_inner(thd); <- 采用pfs的方式查询processlist,实际上查询的是performance_schema.processlist
} else {
mysqld_list_processes(thd,
thd->security_context()->check_access(PROCESS_ACL)
? NullS
: thd->security_context()->priv_user().str,
m_verbose, true); <- 传统的方式,实时搜集所有THD的信息
return false;
}
}
这部分先看传统的方式怎么搞的,然后试图修改一些东西达到我的目的。 显然 mysqld_list_processes(xxx) 是重点函数,要怎么搞需从此下手。其内容如下(删节):
sql_show.cc:
void mysqld_list_processes(THD *thd, const char *user, bool verbose,
bool has_cursor) {
field_list.push_back(
new Item_int(NAME_STRING("Id"), 0, MY_INT64_NUM_DECIMAL_DIGITS)); <- 这后面一排都是发送“表头”的,定义了每个字段的类型信息,因此这部分先将查询结构发送到客户端了
field_list.push_back(new Item_empty_string("User", USERNAME_CHAR_LENGTH));
field_list.push_back(new Item_empty_string("Host", HOSTNAME_LENGTH));
field_list.push_back(field = new Item_empty_string("db", NAME_CHAR_LEN));
field->set_nullable(true);
field_list.push_back(new Item_empty_string("Command", 16));
field_list.push_back(field = new Item_return_int("Time", 7, MYSQL_TYPE_LONG));
field->unsigned_flag = false;
field_list.push_back(field = new Item_empty_string("State", 30));
field->set_nullable(true);
field_list.push_back(field = new Item_empty_string("Info", max_query_length));
field->set_nullable(true);
if (thd->send_result_metadata(field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))。 <- 将结构信息发送到客户端
return;
if (!thd->killed) {
thread_infos.reserve(Global_THD_manager::get_instance()->get_thd_count()); <- 这段代码是拿到当前所有的线程上下文THD,拷贝内容到thread_infos结构中
List_process_list list_process_list(user, &thread_infos, thd,
max_query_length); <- List_process_list 是一个helper类,实现拷贝的动作
Global_THD_manager::get_instance()->do_for_all_thd_copy(&list_process_list); <- 拷贝THD内容到thread_infos,Global_THD_manager是管理THD的一个结构
}
const time_t now = time(nullptr);
for (size_t ix = 0; ix < thread_infos.size(); ++ix) { <- 这里整了一套for循环,将thread_infos的内容(也就是thd给它的内容)发送到客户端
thread_info *thd_info = thread_infos.at(ix); <- 拿到单个thread_info,这个结构里已经包含了某个THD的信息
protocol->start_row();
protocol->store((ulonglong)thd_info->thread_id);
protocol->store(thd_info->user, system_charset_info);
protocol->store(thd_info->host, system_charset_info);
protocol->store(thd_info->db, system_charset_info);
if (thd_info->proc_info)
protocol->store(thd_info->proc_info, system_charset_info);
else
protocol->store(Command_names::str_session(thd_info->command).c_str(),
system_charset_info);
if (thd_info->start_time_in_secs)
protocol->store_long((longlong)(now - thd_info->start_time_in_secs));
else
protocol->store_null();
protocol->store(thd_info->state_info, system_charset_info);
protocol->store(thd_info->query_string.str(),
thd_info->query_string.charset());
if (protocol->end_row()) break; /* purecov: inspected */
}
}
其实到这里就知道该怎么改了,但是下一步开始先看看 thread_info 是咋回事(删节):
sql_show.cc:
class thread_info {
public:
thread_info()
: thread_id(0),
start_time_in_secs(0),
command(0),
user(nullptr),
host(nullptr),
db(nullptr),
proc_info(nullptr),
state_info(nullptr) {}
my_thread_id thread_id;
time_t start_time_in_secs;
uint command;
const char *user, *host, *db, *proc_info, *state_info;
CSET_STRING query_string;
};
无趣,这个类没啥东西,就是一堆结构,这些结构应该是用来保存显示的信息的。那再翻回去看看,发现一个类 List_process_list ,它有一个有趣的重载(删节):
sql_show.cc:
void operator()(THD *inspect_thd) override {
thread_info *thd_info = nullptr;
{
Security_context *inspect_sctx = inspect_thd->security_context(); <- 从传入的thd中拿到上下文结构
const LEX_CSTRING inspect_sctx_user = inspect_sctx->user();
const LEX_CSTRING inspect_sctx_host = inspect_sctx->host();
const LEX_CSTRING inspect_sctx_host_or_ip = inspect_sctx->host_or_ip();
thd_info = new (m_client_thd->mem_root) thread_info;
/* ID */
thd_info->thread_id = inspect_thd->thread_id(); <- 正式复制了,这里拷贝的是thread id
/* USER */
if (inspect_sctx_user.str) <- 这里拷贝user,后面的代码差不多的意思,就不贴出来了
thd_info->user = m_client_thd->mem_strdup(inspect_sctx_user.str);
else if (inspect_thd->system_thread)
thd_info->user = "system user";
else
thd_info->user = "unauthenticated user";
/* HOST */
...
} // We've copied the security context, so release the lock.
/* DB */
...
/* COMMAND */
...
/* STATE */
...
/* INFO */
...
/* MYSQL_TIME */
...
m_thread_infos->push_back(thd_info); <- 最后插入数组中
}
到这里就清晰明了了,就是把THD的内容拷贝到一个thread_info的结构中,然后发送到客户端就完事儿了。
有个前提,一个MySQL的线程是包含一个THD结构的。因此我们在THD创建时初始化一个时间就可以了,这就是线程创建的时间。
所以我们现在THD中加一个时间结构,并且加上配套的操作:
sql_class.h:
class THD {
...
// 1. 定义结构保存时间
struct timeval m_create_time;
// 4. 获取时间
time_t create_time_in_secs() const { return m_create_time.tv_sec; }
// 2. 设置时间函数
void set_create_time();
...
};
sql_class.cc:
// 2. 设置时间函数
void THD::set_create_time() {
ulonglong login_utime = my_micro_time();
my_micro_time_to_timeval(login_utime, &m_create_time);
}
现在结构有了,那就丢到THD的构造函数里去初始化一下时间吧: