MySQL显然在很多方面都做对了,否则,它将不是世界上最受欢迎的开源数据库(根据DB-Engines)。但是,有时我会遇到一些决策或行为,这些决策或行为简直就是糟糕的设计。许多这样的设计背后都有很多历史原因,也许它们仍然存在,因为没有足够的资源来清理技术债务。
我对可观察性充满热情,尤其是在了解系统性能方面。理解MySQL性能的最重要的数据之一就是了解其闩锁争用(互斥体,rwlock等)。
了解MySQL中闩锁的“最佳”方法是性能模式。不幸的是,默认情况下,在性能模式中禁用了闩锁性能分析,因为这会导致相当大的开销;足够重要,您可能不会一直在生产中使用此仪器。
如果您希望从MySQL获取一些始终可用的互斥锁信息,则可以从InnoDB存储引擎中获取它们(通常足够好,因为这是发生大多数争用的地方)。
一种选择是查看SHOW ENGINE INNODB STATUS输出-特别是在SEMAPHORES部分:
---------- SEMAPHORES ---------- OS WAIT ARRAY INFO: reservation count 490020789 --Thread 140407582807808 has waited at row0ins.cc line 2412 for 0 seconds the semaphore: S-lock on RW-latch at 0x7fa0159bd6f0 created in file buf0buf.cc line 785 a writer (thread id 140407910762240) has reserved it in mode exclusive number of readers 0, waiters flag 1, lock_word: 0 Last time read locked in file row0ins.cc line 2412 Last time write locked in file /mnt/workspace/percona-server-8.0-debian-binary/label_exp/min-bionic-x64/test/percona-server-8.0.18-9/storage/innobase/include/mtr0mtr.ic line 142 --Thread 140386577712896 has waited at row0ins.cc line 2412 for 0 seconds the semaphore: S-lock on RW-latch at 0x7fa0159bd6f0 created in file buf0buf.cc line 785 a writer (thread id 140407910762240) has reserved it in mode exclusive number of readers 0, waiters flag 1, lock_word: 0 Last time read locked in file row0ins.cc line 2412 Last time write locked in file /mnt/workspace/percona-server-8.0-debian-binary/label_exp/min-bionic-x64/test/percona-server-8.0.18-9/storage/innobase/include/mtr0mtr.ic line 142
复制
本文将提供有关正在等待的互斥锁的信息以及很有用的等待时间信息。不幸的是,该信息以不易解析的形式提供,只能通过整个SHOW ENGINE INNODB STATUS输出来检索,这会造成额外的负担,使其不适用于高频采样。
为什么从未使用某些INFORMATION_SCHEMA表访问此信息是一个很大的难题。是否是因为想法是PERFORMANCE_SCHEMA应该是唯一的可观察性工具,即使MySQL工程团队无法以可接受的开销执行它?
但是,等等,您可以使用SHOW ENGINE INNODB MUTEX来获取统计信息的摘要:
mysql> SHOW ENGINE INNODB MUTEX; +--------+----------------------------+-----------------+ | Type | Name | Status | +--------+----------------------------+-----------------+ | InnoDB | rwlock: dict0dict.cc:2454 | waits=138 | | InnoDB | rwlock: dict0dict.cc:2454 | waits=545 | | InnoDB | rwlock: dict0dict.cc:2454 | waits=124 | | InnoDB | rwlock: dict0dict.cc:2454 | waits=110 | | InnoDB | rwlock: dict0dict.cc:2454 | waits=134 | | InnoDB | rwlock: dict0dict.cc:2454 | waits=132 | | InnoDB | rwlock: dict0dict.cc:2454 | waits=5317 | | InnoDB | rwlock: dict0dict.cc:2454 | waits=538 | … | InnoDB | rwlock: hash0hash.cc:171 | waits=219 | | InnoDB | rwlock: hash0hash.cc:171 | waits=291 | | InnoDB | rwlock: hash0hash.cc:171 | waits=290 | | InnoDB | rwlock: hash0hash.cc:171 | waits=312 | | InnoDB | rwlock: hash0hash.cc:171 | waits=281 | | InnoDB | rwlock: hash0hash.cc:171 | waits=226 | | InnoDB | rwlock: hash0hash.cc:171 | waits=327 | | InnoDB | sum rwlock: buf0buf.cc:785 | waits=138699546 | +--------+----------------------------+-----------------+ 332 rows in set (0.59 sec)
复制
该命令不提供相同的信息(它显示等待的次数,而不是当前的等待次数),但是很有用。该命令的问题在于,它看起来似乎是经过特别设计的,以使其尽可能地少用。看到“名称”上有很多重复项。这是因为存在相同类型互斥的多个实例。在许多情况下,当您想了解要处理的争用时,您希望求和“等待次数”(按Name分组),不幸的是,您不能使用SHOW命令来做到这一点。
更奇怪的是,选择了waits = N语法并命名为“状态”列,其中关系数据库设计建议使用“等待”作为列名。
我也希望在这里看到同步对象名称,而不是源代码行,因为它通常更具描述性。顺便说一句,MariaDB做到了,而且还使其可作为innodb_mutex信息架构表使用。
最后,请注意此命令有多慢:在80GB缓冲池上为0.6秒。原因是它捕获了缓冲池页面上的互斥锁争用,这对于识别特定于页面的争用非常有帮助,而且还需要汇总可能耗时的数百万个对象的信息。
我认为INFORMATION_SCHEMA表也是显示此信息的更好选择。
好的,因此我们不能在INFORMATION_SCHEMA表上运行简单明了的SELECT来以易于消化的形式获取数据,但是也许我们应该编写一个存储过程呢?
这给我们带来了另一个设计问题。虽然您可以轻松地在存储过程中迭代SELECT输出,但不适用于SHOW命令。对此限制可能有一个很好的实际原因,但这一点对用户来说也不太友好。
如果没有其他帮助,总会有Shell脚本,我们也可以使用它来解决此问题:
root@rocky:~# mysql -BNe "SHOW ENGINE INNODB MUTEX" | awk -F'\t' '{split($3, waits, "="); out[$2]+=waits[2];} END { for(el in out) printf "%s\t%d\n", el, out[el] } ' sum rwlock: buf0buf.cc:785 138984320 rwlock: btr0sea.cc:202 226767645 rwlock: trx0purge.cc:222 13 rwlock: ibuf0ibuf.cc:543 345822 rwlock: dict0dict.cc:2454 1958468 rwlock: dict0dict.cc:1042 66610 rwlock: fil0fil.cc:3150 1064444 rwlock: hash0hash.cc:171 37536 rwlock: dict0dict.cc:330 131
复制
但是,如果您的数据库要求您对数据库提供的信息进行基本分组,那么这里有问题!
我想看什么? 我认为应检查所有SHOW语句,如果不打算弃用它们,则应从表或视图中获取与其提供的信息类似的信息。事实上,这项工作已经完成了大多数常见命令,但看起来从未完成。