MySQL使用了稱之為psi/pfs的一系列檔案和結構來進行performance監控。Psi全稱為performance schema interface,pfs全稱為performance storage。
該機制使用pthead來進行操作,其首先定義了pthread的執行緒儲存變數(pfs.cc):
thread_local_key_t THR_PFS;
thread_local_key_t THR_PFS_VG; // global_variables
thread_local_key_t THR_PFS_SV; // session_variables
thread_local_key_t THR_PFS_VBT; // variables_by_thread
thread_local_key_t THR_PFS_SG; // global_status
thread_local_key_t THR_PFS_SS; // session_status
thread_local_key_t THR_PFS_SBT; // status_by_thread
thread_local_key_t THR_PFS_SBU; // status_by_user
thread_local_key_t THR_PFS_SBH; // status_by_host
thread_local_key_t THR_PFS_SBA; // status_by_account
bool THR_PFS_initialized= false;
這裡的thread_local_key_t
實際上是pthread_key_t
,即pthread
執行緒儲存變數。pthread_key_t
的使用就像一個全域性變數,哪個執行緒都可以用,但是實際上對應了執行緒內部的變數值,可以參見該例:http://www.jianshu.com/p/d52c…。pthread規定,執行緒儲存變數thread_local_key_t
必須要先初始化。MySQL在pfs_server.cc
中對這些變數統一初始化:
void pre_initialize_performance_schema()
{
pfs_initialized= false;
init_all_builtin_memory_class();
PFS_table_stat::g_reset_template.reset();
global_idle_stat.reset();
global_table_io_stat.reset();
global_table_lock_stat.reset();
if (my_create_thread_local_key(&THR_PFS, destroy_pfs_thread))
return;
if (my_create_thread_local_key(&THR_PFS_VG, NULL)) // global_variables
return;
if (my_create_thread_local_key(&THR_PFS_SV, NULL)) // session_variables
return;
if (my_create_thread_local_key(&THR_PFS_VBT, NULL)) // variables_by_thread
return;
if (my_create_thread_local_key(&THR_PFS_SG, NULL)) // global_status
return;
if (my_create_thread_local_key(&THR_PFS_SS, NULL)) // session_status
return;
if (my_create_thread_local_key(&THR_PFS_SBT, NULL)) // status_by_thread
return;
if (my_create_thread_local_key(&THR_PFS_SBU, NULL)) // status_by_user
return;
if (my_create_thread_local_key(&THR_PFS_SBH, NULL)) // status_by_host
return;
if (my_create_thread_local_key(&THR_PFS_SBA, NULL)) // status_by_account
return;
THR_PFS_initialized= true;
}
注意,這個初始化只做一次,以後建立執行緒時直接使用即可。上的第一個變數THR_PFS就是我們要使用的。
如何使用
使用上的方式初始化,首先要set相應的value:
/**
@brief Execute the JOIN generated by parallel
@param join [in] JOIN structure
*/
void execute_join(parallel_execution_thread_arg* parallel_arg) {
/*
* Get the parameter:
* 1. JOIN
* 2. pfs
*/
/// TODO: do we need to handle error?
std::cout << "****************I am in worker thread*****************" << std::endl;
/// Get join
JOIN* join= parallel_arg->join;
/// Get and Set pfs
PSI_thread* pfs= parallel_arg->pfs;
pfs_set_thread_v1(pfs);
/// Delete
delete parallel_arg;
/// Set the new thread context
my_thread_set_THR_THD(join->thd);
/// Execute
join->exec();
}
上面的函式是我在MySQL中新加入的程式碼,其中使用pfs_set_thread_v1進行set操作,即把當前THR_PFS對應的值設定為pfs。
get操作。由於我們加入了boost
執行緒庫,所以當啟動一個執行緒時需要把JOIN
結構和pfs
結構傳入。思路是首先通過THR_PFS
獲得pfs
執行緒控制程式碼,作為引數傳入到新的執行緒中。再新執行緒執行函式中,把pfs
執行緒控制程式碼set進去。具體在sql_select.cc中,我們加入瞭如下程式碼:
/**
Parallel execution.
@details When a JOIN is parallel, its JOINs will execute parallelly.
Put all JOINs into thread pool to execute.
*/
void JOIN::parallel_exec_joins() {
for (uint i= 0; i < m_parallel_joins.size(); i ++) {
/// Delete it in join->exec
parallel_execution_thread_arg* parallel_arg= new parallel_execution_thread_arg();
/// Set join
JOIN* join= m_parallel_joins[i];
parallel_arg->join= join;
/// Set pfs
PSI_thread* pfs= pfs_get_thread_v1();
parallel_arg->pfs= pfs;
/// Thread pool
generic_thread_pool.SubmitTask(execute_join, (parallel_execution_thread_arg* &&)parallel_arg);
}
}
可以看到,我們通過MySQL的pfs_get_thread_v1
獲得pfs執行緒控制程式碼傳入到新的執行緒。
上面的例子是針對pfs的執行緒。對於MySQL普通執行緒的例子在上面的execute_join也能找到。注意裡面有一行code:
/// Set the new thread context
my_thread_set_THR_THD(join->thd);
這裡就是把當前的thd設定到pthread中。所以我們看到,在MySQL中的很多地方都用到了這個東西,用法也已經明確了。