慢查询日志攒了783MB,而真正超过1秒的只有32条

慢查询日志攒了783MB,而真正超过1秒的只有32条
张文保 28 分钟阅读 2,450 阅读
本文目录
  1. 慢查询日志开着,为什么还是没发现问题?
  2. 先看看这份日志长什么样
  3. 把这266万条按耗时分个档
  4. 所以它不是没发现问题,是发现了太多不是问题的事
  5. 那98.7% 的噪声,是哪两个开关放进来的?
  6. 第一个开关:记录没走索引的查询
  7. 第二个开关:检查行数的下限
  8. 两个开关叠在一起,就成了全表扫描日志
  9. 把阈值调小就够了吗?
  10. 默认值是10秒,这个数你多半用不上
  11. 网页场景该定多少
  12. 有一类慢查询,调阈值也抓不到
  13. EXPLAIN该先看哪一列?
  14. type:一眼定生死的那一列
  15. rows:是估算,不是实数
  16. Extra:两个词要警惕
  17. key:实际用了哪条索引
  18. 索引建了却没走,多半是这几种写法
  19. 第一种:条件列压根没索引
  20. 第二种:条件不在索引的最左边
  21. 第三种:排序列没索引,于是filesort
  22. 第四种:条件走了索引,排序却没走
  23. 三条查询的实际耗时
  24. 为什么小站的慢查询不表现为慢?
  25. 耗时是滞后指标,形态是领先指标
  26. 缓冲池大会把问题藏得更深
  27. 那个比例才是真正该看的数
  28. Googlebot感受到的是哪一档速度?
  29. 官方原话把TTFB写进了抓取容量
  30. 可是爬虫抓的那批页,缓存最不容易命中
  31. 实测两档差多少
  32. 这件事为什么在Core Web Vitals报表里看不见?
  33. 第一层:TTFB本来就不是Core Web Vitals
  34. 第二层:报表只收真实用户,收不到爬虫
  35. 第三层:长尾页根本不够格进数据集
  36. 抓取预算的账要另算
  37. 我把自己这个站量了一遍,结论是还没到
  38. 数字不支持那个结论
  39. 但形态上的问题是真的
  40. 这个诚实结论本身就是个方法
  41. 一套能长期跑下去的检查顺序
  42. 第一步:先把日志调到能用
  43. 第二步:按比例挑出嫌疑犯
  44. 第三步:逐条EXPLAIN,只看四列
  45. 第四步:建索引之前先想清楚顺序
  46. 第五步:改完用冷启动验证
  47. 常见问题解答
  48. 我开了慢查询日志,里面全是几毫秒的查询,正常吗?
  49. long_query_time该设多少?
  50. 为什么有的卡顿在慢查询日志里查不到?
  51. EXPLAIN输出这么多列,先看哪个?
  52. 索引明明建了,为什么EXPLAIN说没用上?
  53. 我的站数据量不大,需要管这些吗?
  54. 数据库慢真的会影响SEO吗?
  55. 为什么性能报表里看不出长尾页慢?
  56. 权威参考资料

摘要:我把自己这台服务器的慢查询日志翻出来数了一遍。文件783MB,266万条记录,时间跨度37天。其中真正执行超过1秒的——32条。每8万多条记录里才藏着1条值得看的。

剩下那266万条不是白记的,是两个开关把它们放进来的。而更麻烦的地方在于:等你能从耗时上看出数据库慢,通常说明数据量已经涨到位了。真正该盯的信号在别处。

先交代背景,免得后面的数字看着莫名其妙。这台机器跑的是一个内容站,两千来篇文章,MySQL 8.0.45,InnoDB缓冲池给到32GB,最大连接数800。按任何标准这都算配置宽裕的小站——正因为宽裕,它才适合用来说明这件事。

做内容和SEO的人通常不碰数据库。服务器慢了找运维,运维看一眼负载不高就说没问题,事情就这么过去了。但页面响应速度这件事,已经明确写进了Google关于抓取预算的官方文档里,而数据库正是响应时间里最容易被忽略的那一段。

这篇讲三件事:慢查询日志为什么开了也白开,该看的信号到底是哪几个,以及这一切和搜索引擎的关系在哪。全文的数字都来自我自己这台机器,包括那个让我自己也没想到的结论。

慢查询日志开着,为什么还是没发现问题?

先说一个反直觉的事实:慢查询日志最常见的失败方式不是没开,是开了之后没人能从里面读出东西。

先看看这份日志长什么样

我这台机器上的日志在/www/server/data/mysql-slow.log,最早一条时间戳是2026年6月20日,最新一条是7月27日,中间37天。文件783MB,含Query_time的记录一共2667930条。

平均下来每天7.2万条。一个日均访问量不大的内容站,一天往磁盘里写7万多条查询记录——这个量级本身就说明记录条件不对劲。

把这266万条按耗时分个档

我按Query_time的数值把全部记录分了三档,结果是这样:

耗时区间条数占比
1秒以上320.001%
100毫秒以上56390.21%
10毫秒以内263255398.7%

98.7%的记录是10毫秒以内跑完的查询。它们被写进一个名字叫“慢查询”的文件里,而它们一点也不慢。

要找那32条真正超过1秒的,得在266万条里翻。信噪比是1比83373。这个比例大概相当于在一整座图书馆里找那半页被折过角的纸。

所以它不是没发现问题,是发现了太多不是问题的事

这就是慢查询日志最典型的失效形态。它没坏,它工作得非常尽责,只是尽责的方向不对。你打开文件看两眼,全是零点零零几秒的查询,看不出所以然,于是关掉窗口,从此再也不看。

日志本身还在长。783MB这个数字放在磁盘监控里也不显眼,直到某天磁盘告警才想起来它。关于服务器日志怎么管才不爆盘,logrotate与journald那篇里讲过一套完整做法,慢查询日志也该纳进同一套轮转策略。

那98.7% 的噪声,是哪两个开关放进来的?

我查了这台机器上的实际配置,问题出在两个变量上。

第一个开关:记录没走索引的查询

本机的log_queries_not_using_indexesON。MySQL官方手册对这个变量的说明是:启用它,就把“没有使用索引做行查找”的语句也写进慢查询日志。

注意这句话里没有任何关于快慢的条件。只要没走索引,多快都记。一张只有几十行的小表,全表扫描零点几毫秒就完事,照记不误。

内容管理系统的后台和前台每分钟要跑几十上百条这样的查询——查配置项、查选项表、查计数——它们全都没走索引,因为表太小根本不需要索引。于是日志就被这些查询淹没了。

第二个开关:检查行数的下限

另一个变量min_examined_row_limit在本机是0。手册里的判定逻辑写得很清楚:一条查询要被记录,必须至少检查过这个数量的行。

设成0,等于这道闸门完全打开。一条只扫了19行的查询也够格进日志。我从日志里随手翻到的一条真实记录就是这样:

字段实测值
Query_time0.000802秒
Rows_sent6
Rows_examined19

零点八毫秒,检查19行,返回6行。这条记录进慢查询日志唯一的原因,是它没走索引。

两个开关叠在一起,就成了全表扫描日志

把这两条合起来看:不管快慢、不管扫了几行,只要没走索引就记。这个文件的真实身份已经不是慢查询日志了,是一份全表扫描流水账。

它有它的价值——想知道哪些语句没吃到索引,这份流水账确实全。但把它当成“找出拖慢站点的查询”的工具,方向就错了。这两件事需要两套不同的记录条件,而多数面板把它们混在一个文件里。

把阈值调小就够了吗?

看到这里很容易得出一个结论:那就把阈值调好呗。这个方向对,但没那么简单。

默认值是10秒,这个数你多半用不上

MySQL手册的原话是:long_query_time的最小值和默认值分别是0和10。也就是说,如果你没改过配置,只有跑满10秒的查询才会被记下来。

一个网页请求要是真跑到10秒,用户早走光了,这条记录留下来更像是给考古队看的。所以很多人第一次打开慢查询日志发现是空的,不是数据库没问题,是阈值根本没到。

本机的值是1秒——面板装机时改过。1秒对网页场景仍然偏松:一个页面里跑十几条查询,每条都在900毫秒,日志一条不记,而页面已经慢得没法看了。

网页场景该定多少

我的建议是先按0.2秒起步。理由不复杂:一个动态页面通常要跑十几到几十条查询,如果单条能到200毫秒,几条叠起来就把整个后端时间吃光了。

配合把log_queries_not_using_indexes关掉,或者退一步——保持开启但把min_examined_row_limit抬到1000。后一种做法能留住“扫了很多行还没走索引”的查询,同时把那些小表全扫的噪声挡在外面。这是一个折中,但比0要好用得多。

有一类慢查询,调阈值也抓不到

这条坑值得单独讲。手册里有一句容易被略过的话:获取初始锁的时间不计入执行时间。

意思是,一条查询如果被别的事务锁住等了3秒,然后自己跑了20毫秒,那么记录里的Query_time是20毫秒,不是3秒多。锁等待造成的卡顿,在慢查询日志里是隐身的

用户那边体感是页面卡了三秒,你翻日志一条异常都没有。碰到这种对不上的情况,别再盯着慢查询日志了,该去看锁和事务那一层。

EXPLAIN该先看哪一列?

日志告诉你哪条查询有问题,EXPLAIN告诉你问题出在哪。这个命令输出十几列,实际要盯的是四列。

type:一眼定生死的那一列

手册对type=ALL的评语相当不客气——全表扫描,如果不是第一张非常量表,“通常非常糟糕”。这是原文的措辞,不是我加的形容词。

常见取值从好到坏大致是:const最多命中一行、ref走索引取匹配行、range按范围取、index扫整棵索引树、ALL扫整张表。

要留神index这一档,它看着像走了索引,手册的说明却是“和ALL一样,只是扫的是索引树”。除非它同时是覆盖索引,否则并不比全表扫描好多少。

rows:是估算,不是实数

手册明确写着,这一列是MySQL认为需要检查的行数,对InnoDB表来说“是一个估算值,未必总是精确”。

所以别拿它当准数用,拿它当量级用。返回10行却要检查两万行,这个比例才是信号。真实数据到慢查询日志里的Rows_examined去看,那个是实测值。

Extra:两个词要警惕

手册给了句很直接的建议:想让查询尽可能快,就留意Extra列里的Using filesortUsing temporary

前者意味着MySQL得额外走一趟来排序,后者意味着它需要建临时表来存中间结果。两个词单独出现就够麻烦,一起出现说明这条查询的排序和分组都没有索引可以借力。

key:实际用了哪条索引

这一列是NULL,说明优化器一条索引都没选上。有时候索引明明建了它就是不用,下一节专门讲这种情况。

索引建了却没走,多半是这几种写法

这一节全部用本机的真实表来讲。先把这个站的索引现状摊开:

行数(约)索引
typecho_contents2022PRIMARY(cid)、slug、created
typecho_relationships9581PRIMARY(cid, mid)、idx_mid(mid)
typecho_fields9541PRIMARY(cid, name)、int_value、float_value
typecho_metas3544PRIMARY(mid)、slug、idx_type(type)

看出问题了吗——文章表上没有任何一条索引覆盖typestatus这两个几乎每条查询都会带的条件。

第一种:条件列压根没索引

我从慢查询日志里挑了一条真实记录,它统计已发布文章的总数:

指标
语句按type与status统计contents表行数
Query_time0.002786秒
Rows_sent1
Rows_examined2072

返回1个数字,检查了2072行——正好是整张表。因为没有(type, status)这样的组合索引,它只能一行行数过去。

2.8毫秒,快得根本没人会注意。但这个比例是2072比1,而且它随文章数线性增长。文章涨到两万篇,同一条语句就要扫两万行。

第二种:条件不在索引的最左边

这是最容易踩的一种,手册的表述很直接:如果这些列没有构成索引的最左前缀,MySQL就无法用这条索引来做查找。

手册举的例子是(last_name, first_name)这条两列索引。只按last_name查能用上,同时按两列查也能用上;而只按first_name查,用不上。

放到本机的typecho_fields表上就很直观:主键是(cid, name)。按cid查、或者按cidname查,都走得漂亮;只按name查,这条主键索引就用不上了——而“找出所有设置了某个字段的文章”恰恰是这种写法。

我实测了一条这样的查询:按name过滤再对内容做模糊匹配。EXPLAIN的结果是type=ALLkey=NULLrows=9541——整张表9541行,一行不落全扫。

第三种:排序列没索引,于是filesort

再看一条:按标题倒序取前10篇。EXPLAIN给的是type=ALLrows=2022Extra=Using where; Using filesort

标题列上没有索引,所以MySQL得把2022行全读出来、排完序、再扔掉2012行只留10行。只要10行,代价是全表加一次排序。

顺带一条来自日志的真实记录,是取标签云用的:按类型过滤标签、按计数倒序取40条,Rows_sent是40,Rows_examined是4250。idx_type帮它过滤掉了一部分,但count列没有索引,排序还是得自己来。

第四种:条件走了索引,排序却没走

这一种最隐蔽,因为EXPLAIN看上去是好的。本机分类页那条查询就是活标本——关联文章表和关系表,按分类取最新12篇:

typekeyrowsExtra
relationshipsrefidx_mid97Using index; Using temporary; Using filesort
contentseq_refPRIMARY1Using where

两张表都走了索引,typerefeq_ref,看着很健康。但ExtraUsing temporaryUsing filesort一起出现了——因为驱动表是关系表,而排序字段在文章表上,优化器只能先把97条捞齐,建临时表,再排序。

这就是前面那句手册建议的用处:不看Extra只看type,会漏掉这一整类问题。

三条查询的实际耗时

把上面几条各跑三次取中位数,同一台机器同一时刻:

查询EXPLAIN形态中位耗时
分类页取最新12篇ref + 临时表 + filesort1.52 ms
按标题排序取10篇ALL + filesort5.76 ms
按字段模糊匹配ALL,扫9541行23.94 ms

走索引的和全表扫描的差了15.7倍。但请注意最右边那一列的绝对值——最慢的那条也才24毫秒。这就引出了下一节。

为什么小站的慢查询不表现为慢?

如果你照着上面的方法查完自己的站,很可能得到和我一样的结果:形态很难看,耗时很好看。这不矛盾,这是数据量还没到。

耗时是滞后指标,形态是领先指标

全表扫描的代价和表的行数成正比。我这张文章表2022行,扫一遍24毫秒;涨到2万行,同一条语句的EXPLAIN输出一个字都不会变,耗时却要奔着240毫秒去。

SQL的形态今天就已经定了,代价要等数据量涨上来才结算。等你从耗时上看出问题,通常意味着这半年数据涨了一个量级,而那条查询从第一天起就是这么写的。

所以该盯的不是“有没有超过某个毫秒数”,是三个和数据量无关的信号:type是不是ALLExtra里有没有那两个词、Rows_examinedRows_sent的比例有多离谱。这三个在小站上就能看出来,不用等它慢。

缓冲池大会把问题藏得更深

我这台机器的InnoDB缓冲池是32GB,而整个库连备份表算上也远没到这个数。换句话说,全部数据都在内存里,全表扫描扫的是内存不是磁盘。

这解释了为什么24毫秒还能算“快”。一旦数据量超过缓冲池,同样的全表扫描要开始读磁盘,那时候的差距就不是15倍了。配置宽裕不是免死金牌,它只是把账单推迟了。

那个比例才是真正该看的数

日志里那条统计语句,Rows_examined 2072、Rows_sent 1,比例是2072比1。取标签云那条是4250比40,约106比1。而我从日志里捞出的Rows_examined最大值是20750。

这个比值有个好处:它和机器配置无关,和缓冲池大小无关,甚至和当前数据量都关系不大——它反映的是查询本身的效率。100比1以上就该看一眼,1000比1以上基本可以确定缺索引。

Googlebot感受到的是哪一档速度?

前面全在讲数据库,这一节把它接到搜索上。这个连接点在Google的官方文档里,写得比多数人以为的明确。

官方原话把TTFB写进了抓取容量

Google关于大型站点抓取预算管理的文档里有这么两句。第一句:如果站点响应稳定,而且响应时间(包括延迟和首字节时间)保持稳定或有所改善,上限就会提高,意味着可以用更多连接来抓取。

第二句是反过来的:如果站点变慢(延迟增加或响应时间变长),或者返回服务器错误、返回限流信号,这个上限就会下调,Google会少抓

注意它点名了首字节时间。这不是社区推测,是官方文档里把TTFB和抓取容量直接绑在一起的表述。而TTFB里包含后端生成页面的时间,数据库就在这一段里。

可是爬虫抓的那批页,缓存最不容易命中

这里有个结构性的错位。整页缓存对首页和热门文章几乎百分百命中,因为访问密集,缓存还没过期就又被访问了。fastcgi_cache那套配置解决的正是这一档。

但爬虫的抓取分布和用户的访问分布不是一回事。它会去抓那些几周没人点过的老文章、翻到很深的分页、以及各种归档页——这些URL访问稀疏,缓存基本上每次都是过期状态,于是每次抓取都真的落到PHP和数据库上。

换句话说,你精心调优的缓存命中率保护的是用户,而爬虫大概率吃的是没有缓存的那一档。想知道爬虫具体在抓哪些URL,得去翻访问日志,日志分析那篇里有完整的挖法。

实测两档差多少

我在本机上做了个直接的对照:清空整页缓存后请求一次,紧接着再请求两次(这时应该命中缓存),记录首字节时间。

URL清缓存后首次随后两次
一篇老文章640 ms234 / 234 ms
一个分类页445 ms234 / 484 ms
首页432 ms231 / 215 ms

命中缓存稳定在230毫秒上下,没命中要432到640毫秒。差距在1.9到2.7倍之间。分类页那组第二次跑出484毫秒,明显是测量噪声——公网单次采样本来就抖,这一格我不拿它当结论。

按web.dev给的口径,首字节时间0.8秒以内算好、超过1.8秒算差。640毫秒仍然在“好”的区间里。所以就本站而言,诚实的结论是:这个差距真实存在,但还没到会拖累抓取的程度。

这件事为什么在Core Web Vitals报表里看不见?

上一节说差距真实存在。那为什么盯着性能报表的人基本上不会注意到它?因为报表在设计上就收不到这部分数据,有三层原因。

第一层:TTFB本来就不是Core Web Vitals

web.dev讲得很干脆:因为TTFB不是Core Web Vitals指标,所以只要它不妨碍那几个真正算分的指标,站点并非必须达到“好”的门槛。

这句话的言外之意是,你的三项核心指标可以全绿,而首字节时间是另一回事。它更像是那几项的上游——TTFB差会把后面所有指标一起推后,但它自己不在计分板上。

第二层:报表只收真实用户,收不到爬虫

Core Web Vitals的字段数据来自Chrome用户体验报告。它的方法论文档写明了纳入条件:用户要开启用量统计上报、同步浏览器历史、没有设置同步密码,还得用受支持的平台。

Googlebot显然一个条件都不满足。爬虫抓一次你的老文章等了多久,这个数字永远不会出现在任何一份性能报表里。你和搜索引擎对同一个站的速度感知,用的是两套完全不相交的采样。

第三层:长尾页根本不够格进数据集

这一层是我核对文档时才注意到的,也是最关键的一层。同一份方法论文档里还有一条页面纳入条件:页面必须足够热门,而判定标准是访问人数达到某个最低门槛(具体数值没有公开)。

把三层叠起来看:访问最稀疏、缓存最不容易命中、爬虫又抓得最勤的那批长尾页,恰恰因为访问人数不够而进不了数据集。它们慢或不慢,在字段数据里是空白。

这不是报表做得不好,这是采样口径决定的结构性盲区。要看这批页面的速度,只能自己主动去测——比如照上一节那样,清掉缓存直接请求。

抓取预算的账要另算

顺带说清一个容易混的地方:响应变慢影响的是抓取容量上限,不是排名信号。它的伤害路径是“抓得少了,新内容和改动进索引更慢”,不是“因为慢所以降权”。

对页面数量少的站,这层影响微乎其微,Google文档自己也说抓取预算主要是大站要关心的事。真会疼的是URL数量大的电商站——尤其是筛选参数生成海量URL的那种,分面导航那篇把这个坑讲得更细。

我把自己这个站量了一遍,结论是还没到

写到这儿该交个底。我原本准备的论点比现在这篇要激进——大意是“数据库慢查询正在悄悄吃掉你的抓取预算”。实测之后这条论点站不住,我把它删了。

数字不支持那个结论

理由很简单。冷启动首字节640毫秒,按web.dev的口径还在“好”的区间;而这640毫秒里,数据库占的份额小得可怜——上一节那几条查询加起来也就几十毫秒,剩下的是PHP渲染、模板、TLS握手和公网往返。

把数据库优化到零,这个站的TTFB也降不到400毫秒以下。真正的大头在别处。要按贡献排序,OPcache那一层和整页缓存的收益都比调索引大得多,多层缓存怎么组合影响TTFB那篇讲的才是这个站眼下的主要矛盾。

但形态上的问题是真的

结论不是“数据库没事”。同一次排查里,这几件是坐实的:文章表缺(type, status)组合索引,导致一条统计语句要扫全表;分类页那条查询同时踩了临时表和文件排序;有查询单次检查过20750行;慢查询日志98.7%是噪声,783MB还在长。

这些今天不疼,是因为表只有两千行、缓冲池装得下全部数据。它们是那种会随数据量一起长大的问题,现在改成本最低。

这个诚实结论本身就是个方法

我把这段留在文章里,是因为它比一个漂亮的结论有用:先量再下判断,量出来不支持就把论点改掉。性能这个领域最容易出的错,就是拿着一套通用道理去套自己的站,然后把力气花在贡献最小的那一层上。

顺序应该反过来:先测出各层各占多少,再决定动哪一层。至于什么时候数据库会变成主要矛盾——数据量涨一个量级、或者上了商品筛选这类查询形态复杂的功能,那时候再看这篇里的方法就正好。

一套能长期跑下去的检查顺序

最后把前面拆开的东西收成一份可以照着做的清单。整套跑一遍不到半小时,季度做一次就够。

第一步:先把日志调到能用

确认三个变量的现值,然后按网页场景改:阈值先定0.2秒;把记录未走索引查询这个开关关掉,或者保留但把检查行数下限抬到1000;给日志文件加上轮转,别让它长成几百MB。

改完等一两天再看。这时候日志里剩下的条目才是真正值得逐条读的,数量应该从每天几万降到几十条以内。

第二步:按比例挑出嫌疑犯

从日志里把Rows_examinedRows_sent的比值算出来排序。100比1以上进候选,1000比1以上优先处理。这个口径比按耗时排序稳,因为它不受当天负载和缓存状态影响。

第三步:逐条EXPLAIN,只看四列

type是不是ALLindexkey是不是NULLrows的量级、Extra里有没有那两个词。四列看完基本能定位到是缺索引、最左前缀不对,还是排序没能借上索引。

第四步:建索引之前先想清楚顺序

组合索引的列序决定了它能覆盖哪些查询。按最左前缀规则,把区分度高、且几乎每条查询都会带的列放前面。同时记住索引不是免费的——写入要维护它,本机文章表上再加索引,每次发文章和改字段都要多付一点代价。

第五步:改完用冷启动验证

别用热页面验证,缓存会把改动的效果全盖住。正确做法是清掉整页缓存,挑一个平时没人访问的长尾URL,直接量首字节时间,前后各测几次取中位。

如果这个数没动,说明瓶颈本来就不在数据库——就像我这个站一样。那就把力气收回去,投到真正占大头的那一层。想批量改数据的话,SQL语句生成器那篇里的写法可以直接抄,别手搓UPDATE。

顺手一起用:SQL语句生成器

按表名、条件和字段拼出可执行的SELECT、UPDATE与DELETE,带条件预览和转义处理,改一批文章的SEO字段时不用自己手写还担心少个WHERE。

保哥自研免费在线工具,浏览器打开就能用。

→ 打开SQL语句生成器

常见问题解答

我开了慢查询日志,里面全是几毫秒的查询,正常吗?

不正常,但很常见,多半是log_queries_not_using_indexes开着导致的。这个开关只看有没有走索引,不看快慢,于是小表全扫这类零点几毫秒的查询也照记。我这台机器上37天攒了2667930条,其中98.7%在10毫秒以内跑完,真正超过1秒的只有32条。解法是把它关掉,或者保留但把min_examined_row_limit从0抬到1000,把小表全扫的噪声挡在外面。

long_query_time该设多少?

MySQL的默认值是10秒,网页场景下基本等于不记录——一个请求真跑到10秒用户早走了。建议从0.2秒起步:一个动态页面通常要跑十几到几十条查询,单条到200毫秒时几条叠起来就把后端时间吃光了。设好之后观察一两天,如果条目还是多得读不完,说明确实有一批查询要处理,而不是把阈值再往上调。

为什么有的卡顿在慢查询日志里查不到?

因为锁等待不计入执行时间,这一点MySQL手册里写得很明确。一条查询被别的事务锁住等了3秒、自己只跑了20毫秒,日志里记的就是20毫秒。用户体感页面卡了三秒,你翻日志一条异常都没有。遇到这种对不上的情况别再盯慢查询日志,该去看锁和事务那一层。

EXPLAIN输出这么多列,先看哪个?

看四列。type是不是ALL(手册对它的评语是通常非常糟糕);key是不是NULL(说明一条索引都没选上);rows的量级(注意这是估算值不是实数);Extra里有没有Using filesortUsing temporary。手册专门建议留意后面这两个词。要当心type=index,它看着像走了索引,实际手册说它和全表扫描一样,只是扫的是索引树。

索引明明建了,为什么EXPLAIN说没用上?

最常见的原因是查询条件没构成索引的最左前缀。手册的原话是,如果这些列没有构成索引的最左前缀,MySQL就无法用这条索引做查找。比如一条(cid, name)的组合索引,按cid查、或按cidname查都能用上,单独按name查就用不上了。我实测本机一条这样的查询,EXPLAIN给的是type=ALLkey=NULL、扫满9541行。

我的站数据量不大,需要管这些吗?

要管的是形态,不是耗时。全表扫描的代价和行数成正比,SQL怎么写今天就定了,代价要等数据量涨上来才结算。我这张文章表2022行时全表扫描24毫秒,涨到2万行同一条语句的EXPLAIN一个字都不会变,耗时却奔着240毫秒去。所以该盯三个和数据量无关的信号:type是不是ALLExtra里有没有那两个词、检查行数与返回行数的比例。这三个在小站上就能看出来。

数据库慢真的会影响SEO吗?

会影响抓取,不是直接影响排名。Google关于抓取预算的官方文档写得很直接:如果响应时间(包括延迟和首字节时间)保持稳定或改善,抓取容量上限就提高;如果站点变慢或返回服务器错误、限流信号,上限就下调,Google会少抓。伤害路径是新内容和改动进索引更慢,而不是降权。页面数量少的站影响微乎其微,URL数量大的电商站才真会疼。

为什么性能报表里看不出长尾页慢?

三层原因叠在一起。首先TTFB本身不是Core Web Vitals指标,三项核心指标可以全绿而它另算。其次字段数据来自Chrome用户体验报告,只收开启了用量统计并同步历史的真实Chrome用户,爬虫一个条件都不满足。最后也是最关键的,那份方法论文档写明页面必须足够热门、访问人数达到最低门槛才会被纳入——而访问最稀疏、缓存最不容易命中、爬虫抓得最勤的长尾页,恰恰因此进不了数据集。要看这批页面只能自己清缓存去测。

权威参考资料

分享到
标签
版权声明

本文标题:《慢查询日志攒了783MB,而真正超过1秒的只有32条》

本文链接:https://zhangwenbao.com/mysql-slow-query-log-signal-noise-ttfb-crawl-budget.html

版权声明:本文原创,转载与引用请注明作者与原文链接。许可协议: CC BY 4.0

继续阅读
发表评论
分享到微信 或在下方手动填写
支持 Ctrl + Enter 提交