事件是通过添加到服务器源代码中的仪器来收集的。仪器计时事件,这是性能模式提供事件持续时间的方式。也可以配置仪器不收集计时信息。本节讨论了可用的计时器及其特征,以及事件中的计时值表示。
性能模式计时器在精度和开销方面有所不同。要查看可用的计时器及其特征,请检查 performance_timers
表:
mysql> SELECT * FROM performance_schema.performance_timers;
+-------------+-----------------+------------------+----------------+
| TIMER_NAME | TIMER_FREQUENCY | TIMER_RESOLUTION | TIMER_OVERHEAD |
+-------------+-----------------+------------------+----------------+
| CYCLE | 2389029850 | 1 | 72 |
| NANOSECOND | 1000000000 | 1 | 112 |
| MICROSECOND | 1000000 | 1 | 136 |
| MILLISECOND | 1036 | 1 | 168 |
| THREAD_CPU | 339101694 | 1 | 798 |
+-------------+-----------------+------------------+----------------+
如果给定计时器名称关联的值为 NULL
,则该计时器在您的平台上不受支持。
列的含义如下:
-
TIMER_NAME
列显示可用的计时器名称。CYCLE
引用基于 CPU(处理器)周期计数器的计时器。 -
TIMER_FREQUENCY
表示每秒的计时器单位数。对于周期计时器,频率通常与 CPU 速度相关。显示的值是在 2.4GHz 处理器系统上获得的。其他计时器基于固定秒分数。 -
TIMER_RESOLUTION
表示计时器值增加的单位数。如果计时器的分辨率为 10,其值每次增加 10。 -
TIMER_OVERHEAD
是获取一个计时值的最小周期开销。每个事件的开销是显示值的两倍,因为计时器在事件开始和结束时被调用。
性能模式按照以下方式分配计时器:
-
等待计时器使用
CYCLE
。 -
空闲、阶段、语句和事务计时器使用
NANOSECOND
,在NANOSECOND
计时器可用的平台上,否则使用MICROSECOND
。
在服务器启动时,性能模式验证了在构建时关于计时器分配的假设,并在计时器不可用时显示警告。
要计时等待事件,最重要的标准是减少开销,以可能牺牲计时器精度为代价,因此使用 CYCLE
计时器是最好的。
语句(或阶段)执行所需的时间通常比等待事件执行所需的时间长几个数量级。要计时语句,最重要的标准是获得准确的度量,这不受处理器频率变化的影响,因此使用不基于周期的计时器是最好的。语句的默认计时器是 NANOSECOND
。与 CYCLE
计时器相比,额外的“开销”不重要,因为调用计时器两次(一次在语句开始,一次在语句结束)的开销与执行语句本身所需的 CPU 时间相比是微不足道的。使用 CYCLE
计时器这里没有好处,只有缺点。
周期计数器的精度取决于处理器速度。如果处理器以 1 GHz(每秒一亿周期)或更高速度运行,周期计数器提供亚纳秒精度。使用周期计数器比获取实际时间要便宜得多。例如,标准的 gettimeofday()
函数可能需要数百个周期,这对于每秒钟可能发生数千次或数百万次的数据收集来说是不可接受的开销。
周期计数器也存在一些缺点:
-
最终用户期望看到以秒为单位的计时结果。从周期到秒的转换可能很昂贵。因此,转换是一个快速的近似乘法操作。
-
处理器的周期速率可能会改变,例如笔记本电脑进入省电模式或 CPU 降频以减少热生成。如果处理器的周期速率波动,周期到实际时间单位的转换将存在错误。
-
周期计数器可能不可靠或不可用,取决于处理器或操作系统。例如,在 Pentium 处理器上,指令是
RDTSC
(汇编语言指令,而不是 C 语言指令),理论上操作系统可以阻止用户模式程序使用它。 -
一些处理器细节,例如乱序执行或多处理器同步,可能使计数器看起来快或慢 1000 个周期。
MySQL 在 x386(Windows、macOS、Linux、Solaris 和其他 Unix 口味)、PowerPC 和 IA-64 处理器上使用周期计数器。
性能模式表中的当前事件和历史事件行都有三个列来表示计时信息:TIMER_START
和 TIMER_END
指示事件的开始和结束时间,而 TIMER_WAIT
指示事件的持续时间。
setup_instruments
表有一个 ENABLED
列来指示要收集事件的仪器。该表还具有一个 TIMED
列来指示哪些仪器是计时的。如果一个仪器未启用,它将不产生事件。如果一个启用的仪器未计时,则该仪器产生的事件将具有 NULL
的 TIMER_START
、TIMER_END
和 TIMER_WAIT
计时值。这将导致这些值在计算汇总表中的聚合时间值时被忽略(sum、minimum、maximum 和 average)。
在内部,事件中的时间以当前计时器的单位存储。当从性能模式表中检索事件时,时间以皮秒(秒的万亿分之一)显示,以标准化到一个标准单位,不管选择哪个计时器。
计时器基准(“时间零点”)发生在性能模式初始化期间的服务器启动时。TIMER_START
和 TIMER_END
值在事件中表示自基准以来的皮秒。TIMER_WAIT
值是皮秒的持续时间。
事件中的皮秒值是近似的。它们的准确性受到通常与单位转换相关的错误形式的影响。如果使用 CYCLE
计时器并且处理器速率变化,可能会出现漂移。因此,不太合理地将 TIMER_START
值视为自服务器启动以来的精确时间测量。另一方面,可以使用 TIMER_START
或 TIMER_WAIT
值在 ORDER BY
子句中对事件按开始时间或持续时间排序。
选择皮秒作为事件中的时间单位,而不是微秒或其他值,是基于性能的考虑。一个理想的实现目标是以统一的时间单位显示结果,不管选择哪个计时器。在理想世界中,这个时间单位应该类似于墙上时钟单位,并且具有合理的精度;换言之,微秒。但是,要将周期或纳秒转换为微秒,需要对每个仪器进行除法运算。除法在许多平台上都是昂贵的。相反,乘法不是昂贵的,因此使用乘法。因此,时间单位是最高可能的 TIMER_FREQUENCY
值的整数倍,使用足够大的乘数以确保不出现主要的精度损失。结果是时间单位是“皮秒”。这种精度是虚拟的,但这使得开销最小化。
当等待、阶段、语句或事务事件执行时,相应的当前事件表将显示当前事件的计时信息:
events_waits_current
events_stages_current
events_statements_current
events_transactions_current
为了能够确定尚未完成的事件已经运行了多长时间,计时器列被设置如下:
-
TIMER_START
被填充。 -
TIMER_END
被填充为当前计时器值。 -
TIMER_WAIT
被填充为到目前为止的时间消耗 (TIMER_END
−TIMER_START
)。
尚未完成的事件具有 NULL
的 END_EVENT_ID
值。要评估尚未完成的事件的时间消耗,可以使用 TIMER_WAIT
列。因此,要确定尚未完成的事件已经运行了 N
皮秒,可以在查询中使用以下表达式:
WHERE END_EVENT_ID IS NULL AND TIMER_WAIT > N
事件识别假设相应的仪器具有 ENABLED
和 TIMED
设置为 YES
,并且相关的消费者已启用。