- GreatSQL 社区原创内容未经受权不得随便应用,转载请分割小编并注明起源。
- GreatSQL 是 MySQL 的国产分支版本,应用上与 MySQL 统一。
- 作者:bruce
- 文章起源:GreatSQL 社区原创
什么是 events_statements_current
表
在 MySQL 中,PFS 下有一张内存表: events_statements_current
,看到这个名称 ”xxx_current”,小白如我可能会认为这张表中的数据就是以后零碎的沉闷 (active) 语句。该表的形容如下(有局部省略):
mysql> desc events_statements_current;
+-------------------------+------------------------------------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------------------+------------------------------------------------+------+-----+---------+-------+
| THREAD_ID | bigint unsigned | NO | PRI | NULL | |
| EVENT_ID | bigint unsigned | NO | PRI | NULL | |
| END_EVENT_ID | bigint unsigned | YES | | NULL | |
| EVENT_NAME | varchar(128) | NO | | NULL | |
| SOURCE | varchar(64) | YES | | NULL | |
| TIMER_START | bigint unsigned | YES | | NULL | |
| TIMER_END | bigint unsigned | YES | | NULL | |
| TIMER_WAIT | bigint unsigned | YES | | NULL | |
| LOCK_TIME | bigint unsigned | NO | | NULL | |
| SQL_TEXT | longtext | YES | | NULL | |
| DIGEST | varchar(64) | YES | | NULL | |
| DIGEST_TEXT | longtext | YES | | NULL | |
| CURRENT_SCHEMA | varchar(64) | YES | | NULL | |
...
| MYSQL_ERRNO | int | YES | | NULL | |
| RETURNED_SQLSTATE | varchar(5) | YES | | NULL | |
| MESSAGE_TEXT | varchar(128) | YES | | NULL | |
...
+-------------------------+------------------------------------------------+------+-----+---------+-------+
44 rows in set (0.01 sec)
间接应用以后语句表的问题
间接做个小试验,终端 1:
SELECT sleep(1);
终端 2:
USE performance_schema;
-- 查看最近用户语句
SELECT s.thread_id, s.sql_text FROM events_statements_current s, threads t
WHERE s.thread_id = t.thread_id AND t.type = 'FOREGROUND';
输入:
mysql> SELECT s.thread_id, s.sql_text FROM events_statements_current s, threads t WHERE s.thread_id = t.thread_id AND t.type = 'FOREGROUND';
+-----------+--------------------------------------------------------------------------------------------------------------------------------------+
| thread_id | sql_text |
+-----------+--------------------------------------------------------------------------------------------------------------------------------------+
| 58 | select sleep(1) |
| 1849 | SELECT s.thread_id, s.sql_text FROM events_statements_current s, threads t WHERE s.thread_id = t.thread_id AND t.type = 'FOREGROUND' |
+-----------+--------------------------------------------------------------------------------------------------------------------------------------+
2 rows in set (0.00 sec)
重复执行上述语句,发现终端 1 以后执行的语句都是:SELECT sleep(1)
,看起来有点像 BUG 呀。
真的是这样吗? 来细看一下文档的定义:
The events_statements_current table contains current statement events. The table stores one row per thread showing the current status of the thread’s most recent monitored statement event, so there is no system variable for configuring the table size.
发现了一些要害信息: “showing the current status of the thread’s most recent monitored statement event”,也就是说:保留的是线程最近被检测到的语句事件,并非以后正在处于执行状态的语句
获取以后沉闷的语句办法
为了理解这个问题的解决方案,来看看具体实现的代码:
void pfs_end_statement_vc(PSI_statement_locker *locker, void *stmt_da) {
PSI_statement_locker_state *state =
reinterpret_cast<PSI_statement_locker_state *>(locker);
...
if (state->m_discarded) {return;}
...
// storage/perfschema/pfs.cc:6462
if (flags & STATE_FLAG_TIMED) {timer_end = get_statement_timer();
wait_time = timer_end - state->m_timer_start;
}
...
if (flags & STATE_FLAG_THREAD) {
...
if(flags & STATE_FLAG_EVENT) {
...
// storage/perfschema/pfs.cc:6528
pfs->m_timer_end = timer_end;
pfs->m_cpu_time = cpu_time;
pfs->m_end_event_id = thread->m_event_id;
...
}
}
阐明,在语句运行完结的时候,PFS 会更新记录:
- m_timer_end 运行完结工夫
- m_end_event_id 语句完结事件 ID
持续浏览代码,发现除了在:pfs_start_statement_vc
中对该值进行初始化外,批改该值的代码仅有这一处,根本能够得出结论:m_timer_end > 0
或 m_end_event_id >0
代表语句运行曾经完结。
整个繁难工具测一下:
. ./setenv
FOR i IN $(seq 1 1000000)
DO
mysql -h127.0.0.1 -P3306 -uroot <<EOF
USE performance_schema;
-- 打印所有的进行中用户 SQL(除了本身)
SELECT s.* FROM events_statements_current s, threads t WHERE s.thread_id = t.thread_id AND t.type = 'FOREGROUND' AND sql_text like '%FOREGROUND%' = FALSE AND s.end_event_id IS NULL \G
EOF
sleep 0.1 # 每 0.1 秒跑一下
DONE
运行后果:
*************************** 1. row ***************************
THREAD_ID: 15082
EVENT_ID: 127
END_EVENT_ID: NULL
EVENT_NAME: statement/sql/select
SOURCE: init_net_server_extension.cc:95
TIMER_START: 19879433851621000
TIMER_END: 19880288626029000
TIMER_WAIT: 854774408000
LOCK_TIME: 0
SQL_TEXT: select sleep(1), 3
...
将工具 SQL 替换成
-- 移除原来的 s.end_event_id is NULL
SELECT s.* FROM events_statements_current s, threads t
WHERE s.thread_id = t.thread_id AND t.type = 'FOREGROUND' AND sql_text like '%FOREGROUND%' = FALSE AND s.timer_end = 0 \G
再次运行:
## 真的是啥也没有啊,真的是啥也没有啊,
再认真看看文档 (https://dev.mysql.com/doc/ref…):
TIMER_START, TIMER_END, TIMER_WAIT
Timing information for the event. The unit for these values is picoseconds (trillionths of a second).
The TIMER_START and TIMER_END values indicate when event timing started and ended. TIMER_WAIT is the event elapsed time (duration).
If an event has not finished, TIMER_END is the current timer value and TIMER_WAIT is the time elapsed so far (TIMER_END − TIMER_START).
重要信息: 当事件没有实现,TIMER_END 会继续参考以后工夫更新
与设想的很不一样,无奈关上源码,看看有什么线索
//storage/perfschema/table_events_statements.cc:315(table_events_statements_common)
if (m_row.m_end_event_id == 0) {timer_end = get_statement_timer();
} else {timer_end = statement->m_timer_end;}
换句话说:运行未完结时,statement->m_timer_end
的值的确是 0,然而 timer_end 赋值用的是语句计时器的以后工夫。
测试论断:events_statements_current
能够用于形容以后沉闷语句,沉闷这个条件能够应用:END_EVENT_ID IS NULL
来过滤。
Enjoy GreatSQL :)
## 对于 GreatSQL
GreatSQL 是由万里数据库保护的 MySQL 分支,专一于晋升 MGR 可靠性及性能,反对 InnoDB 并行查问个性,是实用于金融级利用的 MySQL 分支版本。
相干链接:GreatSQL 社区 Gitee GitHub Bilibili
GreatSQL 社区:
社区博客有奖征稿详情:https://greatsql.cn/thread-10…
技术交换群:
微信:扫码增加
GreatSQL 社区助手
微信好友,发送验证信息加群
。