Abstract 转载:performance_schema
performance schema 是一个存储引擎, 可以提供对mysql 所有指标的监控, 是一套非常详细而复杂的监控系统, 不同的指标,使用了不同的接口, 另外有几个特点:
它是运行时态, 因此是全内存存储, 重启后会丢失之前的数据
为了减少对运行时态的影响, 绝大部分资源都是提前申请好, 在performance_schema 初始化的时候,已经申请好了。涉及到2块内存, 一个是class 配置信息, 一个pfs state
不能增加sql 种类和语法
本文主要分 3块:
初始化
基本数据结构
使用过程
初始化 分为几个步骤
准备pfs 内部系统的内存监控的类
准备好pfs 配置的内存, pfs 配置主要用于设置pfs_xx_class
初始化pfs – 初始化的核心操作, 最主要的核心操作是准备好PFS需要的资源,尤其是内存申请, class, pfs 监控项的container, 以mutex 为例: 申请param->m_mutex_class_sizing 个PFS_mutex_class, 存储到PFS_mutex_class的mutex_class_array中, 另外会申请监控項的container 如global_mutex_container
设置好所有的service,
把所有的pfs 的key 注册到pfs 中, 方便后续使用
第一步, 初始化PFS_builtin_memory_class的一些类, 和一些全局状态跟踪
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 void pre_initialize_performance_schema() { pfs_initialized = false; init_all_builtin_memory_class(); // 初始化类似 builtin_memory_mutex/builtin_memory_rwlock/builtin_memory_mdl 等等 // 这些变量可以跟踪每种指标对应的内存消耗 // builtin_memory_mutex 类型为 PFS_builtin_memory_class PFS_table_stat::g_reset_template.reset(); // 对PFS_table_stat 类的静态变量g_reset_template 进行重设 // PFS_table_stat 主要成员是 // PFS_table_io_stat m_index_stat[MAX_INDEXES + 1], 跟进这个index 的fetch/insert/update/delete // PFS_table_lock_stat m_lock_stat; table 有9种锁, 每种锁的状态 global_idle_stat.reset(); // idle 的状态跟踪 global_table_io_stat.reset(); // table io 的状态跟踪, global_table_lock_stat.reset(); // table 锁的状态跟踪 g_histogram_pico_timers.init(); // PFS_histogram_timers 的状态跟踪 global_statements_histogram.reset(); //PFS_histogram /* There is no automatic cleanup. Please either use: - my_thread_end() - or PSI_server->delete_current_thread() in the instrumented code, to explicitly cleanup the instrumentation. */ THR_PFS = nullptr; // PFS_thread for (int i = 0; i < THR_PFS_NUM_KEYS; ++i) { THR_PFS_contexts[i] = nullptr; //PFS_table_context } }
init_pfs_instrument_array 申请内存存放, PFS_instr_config, 每個pfs 的监控项的开关放在这里, 后面每个pfs 监控项在register class时, 会从这个配置项中获取是否打开, 是否要进行timer
1 2 3 4 5 6 7 8 9 /** Initialize the dynamic array used to hold PFS_INSTRUMENT configuration options. */**** void init_pfs_instrument_array() { pfs_instr_config_array = new Pfs_instr_config_array(PSI_NOT_INSTRUMENTED); } typedef Prealloced_array<PFS_instr_config *, 10> Pfs_instr_config_array;
初始化 performance_schema storage
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 pfs_rc = initialize_performance_schema( &pfs_param, &psi_thread_hook, &psi_mutex_hook, &psi_rwlock_hook, &psi_cond_hook, &psi_file_hook, &psi_socket_hook, &psi_table_hook, &psi_mdl_hook, &psi_idle_hook, &psi_stage_hook, &psi_statement_hook, &psi_transaction_hook, &psi_memory_hook, &psi_error_hook, &psi_parallel_query_hook, &psi_parallel_operator_hook, &psi_data_lock_hook, &psi_system_hook); if ((pfs_rc != 0) && pfs_param.m_enabled) { pfs_param.m_enabled = false; LogErr(WARNING_LEVEL, ER_PERFSCHEMA_INIT_FAILED); } initialize_performance_schema () { pfs_automated_sizing(param); //把PFS_sizing_data large_data 设置到param 中, 主要是类似p->m_events_waits_history_long_sizing pfs_minimal_setting(param); 如果设置了performance_schema_minimal 为true, 则很多设置全部关掉 init_timers(); //初始化timer 一些偏硬件/操作系统底层函数, 方便获取一些时间 init_event_name_sizing(param); //设置 mutex_class_start/rwlock_class_start, //在register psi(register_mutex_class)时, 得到PSI_mutex_info->m_event_name_index=mutex_class_start + index register_global_classes(); // 注冊global 的class in pre_initialize_performance_schema minimal_global_classes(param); // 当注册performance_schema_minimal 为true, 修改global class的一些enable和m_timed // init_sync_class // 以mutex 为例: 申请param->m_mutex_class_sizing 个PFS_mutex_class, //存储到mutex_class_array 后, 后面register 会进行设置, 在init 会查找, 申请的内存变化会更新到builtin_memory_mutex_class init_thread_class init_table_share // 初始化global_table_share_container, 但没有真正申请内存 //typedef PFS_buffer_scalable_container<PFS_table_share, 4 * 1024, 4 * 1024> PFS_table_share_container; init_table_share_lock_stat // 很多数据结构使用无锁hash 来存储 // 设置一大堆consumer的flag flag_events_stages_current = param->m_consumer_events_stages_current_enabled; init_pfs_plugin_table // PFS_dynamic_table_shares::init_mutex // }
设置PFS service 设置各种service, 类似这样
1 2 3 4 5 6 7 8 。。。 if (psi_memory_hook != NULL) { service = psi_memory_hook->get_interface(PSI_CURRENT_MEMORY_VERSION); if (service != NULL) { set_psi_memory_service(service); } } 。。。
这个地方就是把这个地方设置一下 typedef struct PSI_mutex_service_v1 PSI_mutex_service_t; PSI_mutex_service_t psi_mutex_service = pfs_mutex_service_v1 / psi_mutex_noop psi_mutex_service 定义在psi_noop.cc 文件中, pfs_mutex_service_v1 定义在storage/perfschema/pfs.cc中
如果是pfs 插件编译, 就会使用这个, 但目前是直接编译进内核, 因此不需要插件化加载 mysql_service_psi_mutex_v1_t mysql_service_psi_mutex_v1 = imp_performance_schema_psi_mutex_v1 mysql_service_psi_mutex_v1_t imp_performance_schema_psi_mutex_v1 定义在storage/perfschema/pfs.cc中
初始化所有的key 初始化所有的key, 提前把一部分 register mutext/memory psi 等结构 注册进去
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 /* Now that we have parsed the command line arguments, and have initialized the performance schema itself, the next step is to register all the server instruments. */ static void init_server_psi_keys(void) {. ... count = static_cast<int>(array_elements(all_server_mutexes)); mysql_mutex_register(category, all_server_mutexes, count); count = static_cast<int>(array_elements(all_server_rwlocks)); mysql_rwlock_register(category, all_server_rwlocks, count); ... }
基本结构 公共数据结构, 后续会使用到, 先列在这里https://dev.mysql.com/doc/dev/mysql-server/8.0.20/structPFS__instr.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 struct PFS_instr { /** Internal lock. */ pfs_lock m_lock; /** Enabled flag. */ bool m_enabled; /** Timed flag. */ bool m_timed; /** Container page. */ PFS_opaque_container_page *m_page; // 参考PFS_partitioned_buffer_scalable_container }; //存儲在結構pfs_instr_config_array 中 struct PFS_instr_config { /* Instrument name. */ char *m_name; /* Name length. */ uint m_name_length; /** Enabled flag. */ bool m_enabled; /** Timed flag. */ bool m_timed; };
有這麼多種類
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 enum PFS_class_type { PFS_CLASS_NONE = 0, PFS_CLASS_MUTEX = 1, PFS_CLASS_RWLOCK = 2, PFS_CLASS_COND = 3, PFS_CLASS_FILE = 4, PFS_CLASS_TABLE = 5, PFS_CLASS_STAGE = 6, PFS_CLASS_STATEMENT = 7, PFS_CLASS_TRANSACTION = 8, PFS_CLASS_SOCKET = 9, PFS_CLASS_TABLE_IO = 10, PFS_CLASS_TABLE_LOCK = 11, PFS_CLASS_IDLE = 12, PFS_CLASS_MEMORY = 13, PFS_CLASS_METADATA = 14, PFS_CLASS_ERROR = 15, PFS_CLASS_THREAD = 16, /* Reserve 17-29 for official mysql */ PFS_CLASS_PARALLEL_QUERY = 30, PFS_CLASS_LAST = PFS_CLASS_PARALLEL_QUERY, PFS_CLASS_MAX = PFS_CLASS_LAST + 1 }; // 做一些状态统计的 struct PFS_single_stat { /** Count of values. */ ulonglong m_count; /** Sum of values. */ ulonglong m_sum; /** Minimum value. */ ulonglong m_min; /** Maximum value. */ ulonglong m_max; } /** Instrumentation metadata for a mutex. */ struct PFS_ALIGNED PFS_mutex_class : public PFS_instr_class { /** Mutex usage statistics. */ PFS_mutex_stat m_mutex_stat; /** Singleton instance. */ PFS_mutex *m_singleton; } /** Information for all instrumentation. */ struct PFS_instr_class { /** Class type */ PFS_class_type m_type; /** True if this instrument is enabled. */ bool m_enabled; /** True if this instrument is timed. */ bool m_timed; /** Instrument flags. */ uint m_flags; /** Volatility index. */ int m_volatility; /** Instrument name index. Self index in: - EVENTS_WAITS_SUMMARY_*_BY_EVENT_NAME for waits - EVENTS_STAGES_SUMMARY_*_BY_EVENT_NAME for stages - EVENTS_STATEMENTS_SUMMARY_*_BY_EVENT_NAME for statements - EVENTS_TRANSACTIONS_SUMMARY_*_BY_EVENT_NAME for transactions */ uint m_event_name_index; /** Instrument name. */ char m_name[PFS_MAX_INFO_NAME_LENGTH]; /** Length in bytes of @c m_name. */ uint m_name_length; /** Documentation. */ char *m_documentation; };
使用 以mutex 为例, 在配置文件中,打开performance_schema, 对部分的配置进行单独设置
对于performance schema 关闭的情况下, psi_mutex_service 对应的就是psi_mutex_noop 对于打开performance schema情况下, 对应的是 pfs_mutex_service_v1 每个配置项是 performance_schema_instrument = ‘ wait/synch/mutex = ON ‘ 是一行, 可以多项,表示enable 多个pfs 监控项, 如果不是精确匹配的话, 就建议增加正则匹配% 来代表所有
m_consumer_global_instrumentation_enabled 可以控制如锁之类(mutex/lock/rwlock/cond), 文件(file), table 之类, 控制范围比较广, 默认为true performance_schema_consumer_thread_instrumentation, thread 相关的都由他控制, 默认为true,
1 2 3 4 5 6 performance_schema = ON m_consumer_global_instrumentation_enabled = 1 // 对于一些配置,可以单独进行设置 performance_schema_max_mutex_classes=1024 performance_schema_instrument = ' wait/synch/mutex/% = ON ' performance_schema_instrument = ' wait/io/file/% = ON '
使用接口 对mutex 的使用完全和使用pthread mutex 行为基本一致, 可以参考components/pfs_example/pfs_example.cc
1 2 3 4 5 6 7 mysql_mutex_register --> psi_mutex_service->register_mutex --> pfs_register_mutex_v1 mysql_mutex_init --> psi_mutex_service->init_mutex /my_mutex_init mysql_mutex_destroy mysql_mutex_lock mysql_mutex_trylock mysql_mutex_unlock
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 static PSI_mutex_key key_mutex_x = 0; static PSI_mutex_key key_mutex_y = 0; static PSI_mutex_info all_example_mutex[] = { {&key_mutex_x, "X", PSI_FLAG_SINGLETON, PSI_VOLATILITY_PERMANENT, "Example doc, permanent mutex, singleton."}, {&key_mutex_y, "Y", 0, PSI_VOLATILITY_QUERY, "Example doc, very volatile mutexes."}}; static mysql_mutex_t my_mutex_x; static mysql_mutex_t my_mutex_y; mysql_mutex_register("pfs_example", all_example_mutex, 2); mysql_mutex_init(key_mutex_x, &my_mutex_x, NULL); mysql_mutex_init(key_mutex_y, &my_mutex_y, NULL); mysql_mutex_lock(&my_mutex_x); mysql_mutex_trylock(&my_mutex_y); mysql_mutex_unlock(&my_mutex_y); mysql_mutex_unlock(&my_mutex_x); mysql_mutex_destroy(&my_mutex_x); mysql_mutex_destroy(&my_mutex_y);
lock 的时候, 就是mysql_mutex_lock(&m_thd->LOCK_thd_data);
关键的数据结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 struct mysql_mutex_t { /** The real mutex. */ my_mutex_t m_mutex; /** The instrumentation hook. Note that this hook is not conditionally defined, for binary compatibility of the @c mysql_mutex_t interface. */ struct PSI_mutex *m_psi{nullptr}; }; struct my_mutex_t { union u { native_mutex_t m_native; ////////pthread_mutex_t safe_mutex_t *m_safe_ptr; } m_u; }; /** Instrumented mutex implementation. @see PSI_mutex. */ struct PFS_ALIGNED PFS_mutex : public PFS_instr { /** Mutex identity, typically a @c pthread_mutex_t. */ const void *m_identity; /** Mutex class. */ PFS_mutex_class *m_class; /** Instrument statistics. */ PFS_mutex_stat m_mutex_stat; /** Current owner. */ PFS_thread *m_owner; /** Time stamp of the last lock. This statistic is not exposed in user visible tables yet. */ ulonglong m_last_locked; }; struct PSI_mutex_info_v1 { /** Pointer to the key assigned to the registered mutex. */ PSI_mutex_key *m_key; /** The name of the mutex to register. */ const char *m_name; /** The flags of the mutex to register. @sa PSI_FLAG_SINGLETON */ unsigned int m_flags; /** Volatility index. */ int m_volatility; /** Documentation. */ const char *m_documentation; }; struct PFS_ALIGNED PFS_mutex_class : public PFS_instr_class { /** Mutex usage statistics. */ PFS_mutex_stat m_mutex_stat; /** Singleton instance. */ PFS_mutex *m_singleton; }; struct PFS_mutex_stat { /** Wait statistics. */ PFS_single_stat m_wait_stat; }
使用流程
注册 使用上,需要先register psi, 类似这样
先创建PSI_mutex_key/PSI_mutex_info, 然后进行注册
参考之前的使用方式
注册函数解析
1 2 3 4 5 6 7 8 9 10 pfs_register_mutex_v1 { // 生成formatted_name <-- mutex_instrument_prefix.str + / + category + / + PSI_mutex_info_v1.m_name key = register_mutex_class(formatted_name, (uint)full_length, info); *(PSI_mutex_info_v1->m_key) = key //register_mutex_class 功能 // 从mutex_class_array 中找到一个空的 PFS_mutex_class, 这个index 后面存储到*(PSI_mutex_info_v1->m_key) // init_instr_class, 初始化这个PFS_mutex_class, // configure_instr_class , 从pfs_instr_config_array 中找有没有和PFS_mutex_class->m_name 正则匹配的, 则设置PFS_mutex_class }
init 操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 static inline int inline_mysql_mutex_init( PSI_mutex_key key MY_ATTRIBUTE((unused)), mysql_mutex_t *that, const native_mutexattr_t *attr, const char *src_file MY_ATTRIBUTE((unused)), uint src_line MY_ATTRIBUTE((unused))) { that->m_psi = PSI_MUTEX_CALL(init_mutex)(key, &that->m_mutex); // 这个地方调用 psi_mutex_service->init_mutex(key, &that->m_mutex); --> pfs_init_mutex_v1 return my_mutex_init(&that->m_mutex, attr); // 这个就是原始pthread 的init } //從mutex_class_array 拿到對應key的PFS_mutex_class // pfs = create_mutex(klass, identity); 从global_mutex_container 中拿到PFS_mutex, 然后初始化PFS_mutex PSI_mutex *pfs_init_mutex_v1(PSI_mutex_key key, const void *identity) { PFS_mutex_class *klass; PFS_mutex *pfs; klass = find_mutex_class(key); if (unlikely(klass == NULL)) { return NULL; } pfs = create_mutex(klass, identity); return reinterpret_cast<PSI_mutex *>(pfs); }
lock/unlock 的过程 当开始锁的时候, 如果enable 了time 跟踪, 会记录下申请锁 到获得锁 的时间戳, 在获得锁的时候, 会把等待时间累加进去, 并记录获得锁的时间, 如果enable 了thread, 会把时间累加到event_name_array[index]上,如果enable FLAG_EVENT, 会有一个PFS_events_waits event , 然后插入 insert_events_waits_history. unlock 的时候,就直接把指针给指向空, 这个地方没有跟踪 lock的时间。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 static inline int inline_mysql_mutex_lock( mysql_mutex_t *that, const char *src_file MY_ATTRIBUTE((unused)), uint src_line MY_ATTRIBUTE((unused))) { int result; if (that->m_psi != NULL) { /* Instrumentation start */ PSI_mutex_locker *locker; PSI_mutex_locker_state state; locker = PSI_MUTEX_CALL(start_mutex_wait)( &state, that->m_psi, PSI_MUTEX_LOCK, src_file, src_line); --> pfs_start_mutex_wait_v1 /* Instrumented code */ result = my_mutex_lock(&that->m_mutex); /* Instrumentation end */ if (locker != NULL) { PSI_MUTEX_CALL(end_mutex_wait)(locker, result); } return result; } /* Non instrumented code */ result = my_mutex_lock(&that->m_mutex); return result; } PSI_mutex_locker *pfs_start_mutex_wait_v1(PSI_mutex_locker_state *state, PSI_mutex *mutex, PSI_mutex_operation op, const char *src_file, uint src_line) { PFS_mutex *pfs_mutex = reinterpret_cast<PFS_mutex *>(mutex); DBUG_ASSERT((int)op >= 0); DBUG_ASSERT((uint)op < array_elements(mutex_operation_map)); DBUG_ASSERT(state != NULL); DBUG_ASSERT(pfs_mutex != NULL); DBUG_ASSERT(pfs_mutex->m_class != NULL); if (!pfs_mutex->m_enabled) { return NULL; } uint flags; ulonglong timer_start = 0; if (flag_thread_instrumentation) { PFS_thread *pfs_thread = my_thread_get_THR_PFS(); if (unlikely(pfs_thread == NULL)) { return NULL; } if (!pfs_thread->m_enabled) { return NULL; } state->m_thread = reinterpret_cast<PSI_thread *>(pfs_thread); flags = STATE_FLAG_THREAD; if (pfs_mutex->m_timed) { timer_start = get_wait_timer(); state->m_timer_start = timer_start; flags |= STATE_FLAG_TIMED; } if (flag_events_waits_current) { if (unlikely(pfs_thread->m_events_waits_current >= &pfs_thread->m_events_waits_stack[WAIT_STACK_SIZE])) { locker_lost++; return NULL; } PFS_events_waits *wait = pfs_thread->m_events_waits_current; state->m_wait = wait; flags |= STATE_FLAG_EVENT; PFS_events_waits *parent_event = wait - 1; wait->m_event_type = EVENT_TYPE_WAIT; wait->m_nesting_event_id = parent_event->m_event_id; wait->m_nesting_event_type = parent_event->m_event_type; wait->m_thread_internal_id = pfs_thread->m_thread_internal_id; wait->m_class = pfs_mutex->m_class; wait->m_timer_start = timer_start; wait->m_timer_end = 0; wait->m_object_instance_addr = pfs_mutex->m_identity; wait->m_event_id = pfs_thread->m_event_id++; wait->m_end_event_id = 0; wait->m_operation = mutex_operation_map[(int)op]; wait->m_source_file = src_file; wait->m_source_line = src_line; wait->m_wait_class = WAIT_CLASS_MUTEX; pfs_thread->m_events_waits_current++; } } else { if (pfs_mutex->m_timed) { timer_start = get_wait_timer(); state->m_timer_start = timer_start; flags = STATE_FLAG_TIMED; state->m_thread = NULL; } else { /* Complete shortcut. */ /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (counted) */ pfs_mutex->m_mutex_stat.m_wait_stat.aggregate_counted(); return NULL; } } state->m_flags = flags; state->m_mutex = mutex; return reinterpret_cast<PSI_mutex_locker *>(state); } void pfs_end_mutex_wait_v1(PSI_mutex_locker *locker, int rc) { PSI_mutex_locker_state *state = reinterpret_cast<PSI_mutex_locker_state *>(locker); DBUG_ASSERT(state != NULL); ulonglong timer_end = 0; ulonglong wait_time = 0; PFS_mutex *mutex = reinterpret_cast<PFS_mutex *>(state->m_mutex); DBUG_ASSERT(mutex != NULL); PFS_thread *thread = reinterpret_cast<PFS_thread *>(state->m_thread); uint flags = state->m_flags; if (flags & STATE_FLAG_TIMED) { timer_end = get_wait_timer(); wait_time = timer_end - state->m_timer_start; /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (timed) */ mutex->m_mutex_stat.m_wait_stat.aggregate_value(wait_time); } else { /* Aggregate to EVENTS_WAITS_SUMMARY_BY_INSTANCE (counted) */ mutex->m_mutex_stat.m_wait_stat.aggregate_counted(); } if (likely(rc == 0)) { mutex->m_owner = thread; mutex->m_last_locked = timer_end; } if (flags & STATE_FLAG_THREAD) { PFS_single_stat *event_name_array; event_name_array = thread->write_instr_class_waits_stats(); uint index = mutex->m_class->m_event_name_index; DBUG_ASSERT(index <= wait_class_max); DBUG_ASSERT(sanitize_thread(thread) != NULL); if (flags & STATE_FLAG_TIMED) { /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (timed) */ event_name_array[index].aggregate_value(wait_time); } else { /* Aggregate to EVENTS_WAITS_SUMMARY_BY_THREAD_BY_EVENT_NAME (counted) */ event_name_array[index].aggregate_counted(); } if (flags & STATE_FLAG_EVENT) { PFS_events_waits *wait = reinterpret_cast<PFS_events_waits *>(state->m_wait); DBUG_ASSERT(wait != NULL); wait->m_timer_end = timer_end; wait->m_end_event_id = thread->m_event_id; if (thread->m_flag_events_waits_history) { insert_events_waits_history(thread, wait); } if (thread->m_flag_events_waits_history_long) { insert_events_waits_history_long(wait); } thread->m_events_waits_current--; DBUG_ASSERT(wait == thread->m_events_waits_current); } } } // unlock 逻辑比较简单, 把对于的psi 对象指针指向空 void pfs_unlock_mutex_v1(PSI_mutex *mutex) { PFS_mutex *pfs_mutex = reinterpret_cast<PFS_mutex *>(mutex); DBUG_ASSERT(pfs_mutex != NULL); /* Note that this code is still protected by the instrumented mutex, and therefore is thread safe. See inline_mysql_mutex_unlock(). */ /* Always update the instrumented state */ pfs_mutex->m_owner = NULL; pfs_mutex->m_last_locked = 0; }
> show tables from performance_schema;
> show tables like 'events_statement%';
> show tables like 'events_wait%';
> select * from events_statements_history;