慢查询日志攒了783MB,而真正超过1秒的只有32条
本文目录
- 慢查询日志开着,为什么还是没发现问题?
- 先看看这份日志长什么样
- 把这266万条按耗时分个档
- 所以它不是没发现问题,是发现了太多不是问题的事
- 那98.7% 的噪声,是哪两个开关放进来的?
- 第一个开关:记录没走索引的查询
- 第二个开关:检查行数的下限
- 两个开关叠在一起,就成了全表扫描日志
- 把阈值调小就够了吗?
- 默认值是10秒,这个数你多半用不上
- 网页场景该定多少
- 有一类慢查询,调阈值也抓不到
- EXPLAIN该先看哪一列?
- type:一眼定生死的那一列
- rows:是估算,不是实数
- Extra:两个词要警惕
- key:实际用了哪条索引
- 索引建了却没走,多半是这几种写法
- 第一种:条件列压根没索引
- 第二种:条件不在索引的最左边
- 第三种:排序列没索引,于是filesort
- 第四种:条件走了索引,排序却没走
- 三条查询的实际耗时
- 为什么小站的慢查询不表现为慢?
- 耗时是滞后指标,形态是领先指标
- 缓冲池大会把问题藏得更深
- 那个比例才是真正该看的数
- Googlebot感受到的是哪一档速度?
- 官方原话把TTFB写进了抓取容量
- 可是爬虫抓的那批页,缓存最不容易命中
- 实测两档差多少
- 这件事为什么在Core Web Vitals报表里看不见?
- 第一层:TTFB本来就不是Core Web Vitals
- 第二层:报表只收真实用户,收不到爬虫
- 第三层:长尾页根本不够格进数据集
- 抓取预算的账要另算
- 我把自己这个站量了一遍,结论是还没到
- 数字不支持那个结论
- 但形态上的问题是真的
- 这个诚实结论本身就是个方法
- 一套能长期跑下去的检查顺序
- 第一步:先把日志调到能用
- 第二步:按比例挑出嫌疑犯
- 第三步:逐条EXPLAIN,只看四列
- 第四步:建索引之前先想清楚顺序
- 第五步:改完用冷启动验证
- 常见问题解答
- 我开了慢查询日志,里面全是几毫秒的查询,正常吗?
- long_query_time该设多少?
- 为什么有的卡顿在慢查询日志里查不到?
- EXPLAIN输出这么多列,先看哪个?
- 索引明明建了,为什么EXPLAIN说没用上?
- 我的站数据量不大,需要管这些吗?
- 数据库慢真的会影响SEO吗?
- 为什么性能报表里看不出长尾页慢?
- 权威参考资料
摘要:我把自己这台服务器的慢查询日志翻出来数了一遍。文件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秒以上 | 32 | 0.001% |
| 100毫秒以上 | 5639 | 0.21% |
| 10毫秒以内 | 2632553 | 98.7% |
98.7%的记录是10毫秒以内跑完的查询。它们被写进一个名字叫“慢查询”的文件里,而它们一点也不慢。
要找那32条真正超过1秒的,得在266万条里翻。信噪比是1比83373。这个比例大概相当于在一整座图书馆里找那半页被折过角的纸。
所以它不是没发现问题,是发现了太多不是问题的事
这就是慢查询日志最典型的失效形态。它没坏,它工作得非常尽责,只是尽责的方向不对。你打开文件看两眼,全是零点零零几秒的查询,看不出所以然,于是关掉窗口,从此再也不看。
日志本身还在长。783MB这个数字放在磁盘监控里也不显眼,直到某天磁盘告警才想起来它。关于服务器日志怎么管才不爆盘,logrotate与journald那篇里讲过一套完整做法,慢查询日志也该纳进同一套轮转策略。
那98.7% 的噪声,是哪两个开关放进来的?
我查了这台机器上的实际配置,问题出在两个变量上。
第一个开关:记录没走索引的查询
本机的log_queries_not_using_indexes是ON。MySQL官方手册对这个变量的说明是:启用它,就把“没有使用索引做行查找”的语句也写进慢查询日志。
注意这句话里没有任何关于快慢的条件。只要没走索引,多快都记。一张只有几十行的小表,全表扫描零点几毫秒就完事,照记不误。
内容管理系统的后台和前台每分钟要跑几十上百条这样的查询——查配置项、查选项表、查计数——它们全都没走索引,因为表太小根本不需要索引。于是日志就被这些查询淹没了。
第二个开关:检查行数的下限
另一个变量min_examined_row_limit在本机是0。手册里的判定逻辑写得很清楚:一条查询要被记录,必须至少检查过这个数量的行。
设成0,等于这道闸门完全打开。一条只扫了19行的查询也够格进日志。我从日志里随手翻到的一条真实记录就是这样:
| 字段 | 实测值 |
|---|---|
| Query_time | 0.000802秒 |
| Rows_sent | 6 |
| Rows_examined | 19 |
零点八毫秒,检查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 filesort和Using temporary。
前者意味着MySQL得额外走一趟来排序,后者意味着它需要建临时表来存中间结果。两个词单独出现就够麻烦,一起出现说明这条查询的排序和分组都没有索引可以借力。
key:实际用了哪条索引
这一列是NULL,说明优化器一条索引都没选上。有时候索引明明建了它就是不用,下一节专门讲这种情况。
索引建了却没走,多半是这几种写法
这一节全部用本机的真实表来讲。先把这个站的索引现状摊开:
| 表 | 行数(约) | 索引 |
|---|---|---|
| typecho_contents | 2022 | PRIMARY(cid)、slug、created |
| typecho_relationships | 9581 | PRIMARY(cid, mid)、idx_mid(mid) |
| typecho_fields | 9541 | PRIMARY(cid, name)、int_value、float_value |
| typecho_metas | 3544 | PRIMARY(mid)、slug、idx_type(type) |
看出问题了吗——文章表上没有任何一条索引覆盖type和status这两个几乎每条查询都会带的条件。
第一种:条件列压根没索引
我从慢查询日志里挑了一条真实记录,它统计已发布文章的总数:
| 指标 | 值 |
|---|---|
| 语句 | 按type与status统计contents表行数 |
| Query_time | 0.002786秒 |
| Rows_sent | 1 |
| Rows_examined | 2072 |
返回1个数字,检查了2072行——正好是整张表。因为没有(type, status)这样的组合索引,它只能一行行数过去。
2.8毫秒,快得根本没人会注意。但这个比例是2072比1,而且它随文章数线性增长。文章涨到两万篇,同一条语句就要扫两万行。
第二种:条件不在索引的最左边
这是最容易踩的一种,手册的表述很直接:如果这些列没有构成索引的最左前缀,MySQL就无法用这条索引来做查找。
手册举的例子是(last_name, first_name)这条两列索引。只按last_name查能用上,同时按两列查也能用上;而只按first_name查,用不上。
放到本机的typecho_fields表上就很直观:主键是(cid, name)。按cid查、或者按cid加name查,都走得漂亮;只按name查,这条主键索引就用不上了——而“找出所有设置了某个字段的文章”恰恰是这种写法。
我实测了一条这样的查询:按name过滤再对内容做模糊匹配。EXPLAIN的结果是type=ALL、key=NULL、rows=9541——整张表9541行,一行不落全扫。
第三种:排序列没索引,于是filesort
再看一条:按标题倒序取前10篇。EXPLAIN给的是type=ALL、rows=2022、Extra=Using where; Using filesort。
标题列上没有索引,所以MySQL得把2022行全读出来、排完序、再扔掉2012行只留10行。只要10行,代价是全表加一次排序。
顺带一条来自日志的真实记录,是取标签云用的:按类型过滤标签、按计数倒序取40条,Rows_sent是40,Rows_examined是4250。idx_type帮它过滤掉了一部分,但count列没有索引,排序还是得自己来。
第四种:条件走了索引,排序却没走
这一种最隐蔽,因为EXPLAIN看上去是好的。本机分类页那条查询就是活标本——关联文章表和关系表,按分类取最新12篇:
| 表 | type | key | rows | Extra |
|---|---|---|---|---|
| relationships | ref | idx_mid | 97 | Using index; Using temporary; Using filesort |
| contents | eq_ref | PRIMARY | 1 | Using where |
两张表都走了索引,type是ref和eq_ref,看着很健康。但Extra里Using temporary和Using filesort一起出现了——因为驱动表是关系表,而排序字段在文章表上,优化器只能先把97条捞齐,建临时表,再排序。
这就是前面那句手册建议的用处:不看Extra只看type,会漏掉这一整类问题。
三条查询的实际耗时
把上面几条各跑三次取中位数,同一台机器同一时刻:
| 查询 | EXPLAIN形态 | 中位耗时 |
|---|---|---|
| 分类页取最新12篇 | ref + 临时表 + filesort | 1.52 ms |
| 按标题排序取10篇 | ALL + filesort | 5.76 ms |
| 按字段模糊匹配 | ALL,扫9541行 | 23.94 ms |
走索引的和全表扫描的差了15.7倍。但请注意最右边那一列的绝对值——最慢的那条也才24毫秒。这就引出了下一节。
为什么小站的慢查询不表现为慢?
如果你照着上面的方法查完自己的站,很可能得到和我一样的结果:形态很难看,耗时很好看。这不矛盾,这是数据量还没到。
耗时是滞后指标,形态是领先指标
全表扫描的代价和表的行数成正比。我这张文章表2022行,扫一遍24毫秒;涨到2万行,同一条语句的EXPLAIN输出一个字都不会变,耗时却要奔着240毫秒去。
SQL的形态今天就已经定了,代价要等数据量涨上来才结算。等你从耗时上看出问题,通常意味着这半年数据涨了一个量级,而那条查询从第一天起就是这么写的。
所以该盯的不是“有没有超过某个毫秒数”,是三个和数据量无关的信号:type是不是ALL、Extra里有没有那两个词、Rows_examined和Rows_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 ms | 234 / 234 ms |
| 一个分类页 | 445 ms | 234 / 484 ms |
| 首页 | 432 ms | 231 / 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_examined和Rows_sent的比值算出来排序。100比1以上进候选,1000比1以上优先处理。这个口径比按耗时排序稳,因为它不受当天负载和缓存状态影响。
第三步:逐条EXPLAIN,只看四列
type是不是ALL或index、key是不是NULL、rows的量级、Extra里有没有那两个词。四列看完基本能定位到是缺索引、最左前缀不对,还是排序没能借上索引。
第四步:建索引之前先想清楚顺序
组合索引的列序决定了它能覆盖哪些查询。按最左前缀规则,把区分度高、且几乎每条查询都会带的列放前面。同时记住索引不是免费的——写入要维护它,本机文章表上再加索引,每次发文章和改字段都要多付一点代价。
第五步:改完用冷启动验证
别用热页面验证,缓存会把改动的效果全盖住。正确做法是清掉整页缓存,挑一个平时没人访问的长尾URL,直接量首字节时间,前后各测几次取中位。
如果这个数没动,说明瓶颈本来就不在数据库——就像我这个站一样。那就把力气收回去,投到真正占大头的那一层。想批量改数据的话,SQL语句生成器那篇里的写法可以直接抄,别手搓UPDATE。
⚡ 顺手一起用:SQL语句生成器
按表名、条件和字段拼出可执行的SELECT、UPDATE与DELETE,带条件预览和转义处理,改一批文章的SEO字段时不用自己手写还担心少个WHERE。
保哥自研免费在线工具,浏览器打开就能用。
常见问题解答
我开了慢查询日志,里面全是几毫秒的查询,正常吗?
不正常,但很常见,多半是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 filesort和Using temporary。手册专门建议留意后面这两个词。要当心type=index,它看着像走了索引,实际手册说它和全表扫描一样,只是扫的是索引树。
索引明明建了,为什么EXPLAIN说没用上?
最常见的原因是查询条件没构成索引的最左前缀。手册的原话是,如果这些列没有构成索引的最左前缀,MySQL就无法用这条索引做查找。比如一条(cid, name)的组合索引,按cid查、或按cid加name查都能用上,单独按name查就用不上了。我实测本机一条这样的查询,EXPLAIN给的是type=ALL、key=NULL、扫满9541行。
我的站数据量不大,需要管这些吗?
要管的是形态,不是耗时。全表扫描的代价和行数成正比,SQL怎么写今天就定了,代价要等数据量涨上来才结算。我这张文章表2022行时全表扫描24毫秒,涨到2万行同一条语句的EXPLAIN一个字都不会变,耗时却奔着240毫秒去。所以该盯三个和数据量无关的信号:type是不是ALL、Extra里有没有那两个词、检查行数与返回行数的比例。这三个在小站上就能看出来。
数据库慢真的会影响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