목차
为什么Mysql explain extended中的filtered列值总是100%
1. 问题
2.原因
3. 引申
4. 总结
백엔드 개발 PHP 튜토리얼 为什么Mysql explain extended中的filtered列值总是100%_PHP教程

为什么Mysql explain extended中的filtered列值总是100%_PHP教程

Jul 12, 2016 am 09:03 AM
android

为什么Mysql explain extended中的filtered列值总是100%

1. 问题

执行Mysql的explain extended的输出会比单纯的explain多一列filtered(MySQL5.7缺省就会输出filtered),它指返回结果的行占需要读到的行(rows列的值)的百分比。按说filtered是个非常有用的值,因为对于join操作,前一个表的结果集大小直接影响了循环的次数。但是我的环境下测试的结果却是,filtered的值一直是100%,也就是说失去了意义。

参考下面mysql 5.6的代码,filtered值只对index和all的扫描有效(这可以理解,其它场合,通常rows值就等于估算的结果集大小。)。
sql/opt_explain.cc
  1. bool Explain_join::explain_rows_and_filtered()
  2. {
  3. if (table->pos_in_table_list->schema_table)
  4. return false;

  5. double examined_rows;
  6. if (select && select->quick)
  7. examined_rows= rows2double(select->quick->records);
  8. else if (tab->type == JT_INDEX_SCAN || tab->type == JT_ALL)
  9. {
  10. if (tab->limit)
  11. examined_rows= rows2double(tab->limit);
  12. else
  13. {
  14. table->pos_in_table_list->fetch_number_of_rows();
  15. examined_rows= rows2double(table->file->stats.records);
  16. }
  17. }
  18. else
  19. examined_rows= tab->position->records_read;

  20. fmt->entry()->col_rows.set(static_cast(examined_rows));

  21. /* Add "filtered" field */
  22. if (describe(DESCRIBE_EXTENDED))
  23. {
  24. float f= 0.0;
  25. if (examined_rows)
  26. f= 100.0 * tab->position->records_read / examined_rows;
  27. fmt->entry()->col_filtered.set(f);
  28. }
  29. return false;
  30. }

但是,我构造了一个全表扫描后,filtered的结果却不对,仍然是100%,而我期待的是0.1%。
  1. mysql> desc tb2;
    +-------+--------------+------+-----+---------+-------+
    | Field | Type | Null | Key | Default | Extra |
    +-------+--------------+------+-----+---------+-------+
    | id | int(11) | NO | PRI | 0 | |
    | c1 | int(11) | YES | | NULL | |
    | c2 | varchar(100) | YES | | NULL | |
    +-------+--------------+------+-----+---------+-------+
    3 rows in set (0.00 sec)

    mysql> explain extended select * from tb2 where c1+----+-------------+-------+------+---------------+------+---------+------+--------+----------+-------------+
    | id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
    +----+-------------+-------+------+---------------+------+---------+------+--------+----------+-------------+
    | 1 | SIMPLE | tb2 | ALL | NULL | NULL | NULL | NULL | 996355 | 100.00 | Using where |
    +----+-------------+-------+------+---------------+------+---------+------+--------+----------+-------------+
    1 row in set, 1 warning (10 min 29.96 sec)

    mysql> select count(*) from tb2 where c1+----------+
    | count(*) |
    +----------+
    | 1001 |
    +----------+
    1 row in set (1.99 sec)

通过gdb跟踪,发现代码走的分支是对的,但下面的值有问题。
  1. (gdb) p table->file->stats.records
  2. $18 = 996355
  3. (gdb) p tab->position->records_read
  4. $19 = 996355
上面的tab->position->records_read应该是估算出的返回行数,正确的值应该是1001左右,而不是全表大小996355。

2.原因

为什么会出现上面的情况呢?后来我查看了下MySQL收集的统计信息就明白了。
MySQL和其它主流数据库一样会自动需要收集统计信息以便生成更好的执行计划,也可以用analyze table手动收集,收集的统计信息存储在mysql.innodb_table_stats和mysql.innodb_index_stats里。
参考:http://dev.mysql.com/doc/refman/5.6/en/innodb-persistent-stats.html#innodb-persistent-stats-tables

但这不是重点,重点是,查看这两个表就会发现MySQL收集的统计信息非常少。
  1. mysql> select * from mysql.innodb_table_stats where table_name='tb2';
    +---------------+------------+---------------------+--------+----------------------+--------------------------+
    | database_name | table_name | last_update | n_rows | clustered_index_size | sum_of_other_index_sizes |
    +---------------+------------+---------------------+--------+----------------------+--------------------------+
    | test | tb2 | 2015-12-02 06:26:54 | 996355 | 3877 | 0 |
    +---------------+------------+---------------------+--------+----------------------+--------------------------+
    1 row in set (0.00 sec)

    mysql> select * from mysql.innodb_index_stats where table_name='tb2';
    +---------------+------------+------------+---------------------+--------------+------------+-------------+-----------------------------------+
    | database_name | table_name | index_name | last_update | stat_name | stat_value | sample_size | stat_description |
    +---------------+------------+------------+---------------------+--------------+------------+-------------+-----------------------------------+
    | test | tb2 | PRIMARY | 2015-12-02 06:26:54 | n_diff_pfx01 | 996355 | 20 | id |
    | test | tb2 | PRIMARY | 2015-12-02 06:26:54 | n_leaf_pages | 3841 | NULL | Number of leaf pages in the index |
    | test | tb2 | PRIMARY | 2015-12-02 06:26:54 | size | 3877 | NULL | Number of pages in the index |
    +---------------+------------+------------+---------------------+--------------+------------+-------------+-----------------------------------+
    3 rows in set (0.00 sec)
重要的信息也就2个,一是表的总记录数(n_rows),二是索引中的列的唯一值数(n_diff_pfx01)。也就是说MySQL不会统计非索引列的值分布信息,在前面的查询的例子中,由于c1没有被索引,所以MySQL无法估算出"c1

3. 引申

后面我联系到MySQL匮乏的统计信息会带来什么后果?
不难想象,如果缺少索引,MySQL很可能会生成性能糟糕的执行计划,比如搞错大表和小表的join顺序,就像下面这样。
  1. mysql> explain extended select count(*) from tb1,tb2 where tb1.c1=tb2.c1 and tb2.c2='xx';
    +----+-------------+-------+------+---------------+------+---------+------+--------+----------+----------------------------------------------------+
    | id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
    +----+-------------+-------+------+---------------+------+---------+------+--------+----------+----------------------------------------------------+
    | 1 | SIMPLE | tb1 | ALL | NULL | NULL | NULL | NULL | 1000 | 100.00 | NULL |
    | 1 | SIMPLE | tb2 | ALL | NULL | NULL | NULL | NULL | 996355 | 100.00 | Using where; Using join buffer (Block Nested Loop) |
    +----+-------------+-------+------+---------------+------+---------+------+--------+----------+----------------------------------------------------+
    2 rows in set, 1 warning (0.00 sec)
虽然t1表时小表,tb2表是大表,但是tb2上加上tb2.c2='xx'的条件限制后结果集就变成0了,因此先扫描tb2表才是性能更好的选择。
相同的查询,PostgreSQL给出的执行计划是更好的,先扫描t2表再循环扫描t1表。

  1. postgres=# explain select count(*) from tb1,tb2 where tb1.c1=tb2.c1 and tb2.c2='xx';
  2. QUERY PLAN
  3. -------------------------------------------------------------------
  4. Aggregate (cost=20865.50..20865.51 rows=1 width=0)
  5. -> Nested Loop (cost=0.00..20865.50 rows=1 width=0)
  6. Join Filter: (tb1.c1 = tb2.c1)
  7. -> Seq Scan on tb2 (cost=0.00..20834.00 rows=1 width=4)
  8. Filter: ((c2)::text = 'xx'::text)
  9. -> Seq Scan on tb1 (cost=0.00..19.00 rows=1000 width=4)
  10. (6 rows)
下面实际对比一下执行时间看看。

MySQL花了0.34s

  1. mysql> select count(*) from tb1,tb2 where tb1.c1=tb2.c1 and tb2.c2='xx';
  2. +----------+
  3. | count(*) |
  4. +----------+
  5. | 0 |
  6. +----------+
  7. 1 row in set (0.34 sec)

PostgreSQL花了0.139s
  1. postgres=# select count(*) from tb1,tb2 where tb1.c1=tb2.c1 and tb2.c2='xx';
  2. count
  3. -------
  4. 0
  5. (1 row)

  6. Time: 139.600 ms

上面这个例子的性能差别其实不是很大,如果去掉tb2.c2='xx'的条件,差别就非常大了。
Mysql花了1分08秒
  1. mysql> explain select count(*) from tb1,tb2 where tb1.c1=tb2.c1;
    +----+-------------+-------+------+---------------+------+---------+------+--------+----------------------------------------------------+
    | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
    +----+-------------+-------+------+---------------+------+---------+------+--------+----------------------------------------------------+
    | 1 | SIMPLE | tb1 | ALL | NULL | NULL | NULL | NULL | 1000 | NULL |
    | 1 | SIMPLE | tb2 | ALL | NULL | NULL | NULL | NULL | 996355 | Using where; Using join buffer (Block Nested Loop) |
    +----+-------------+-------+------+---------------+------+---------+------+--------+----------------------------------------------------+
    2 rows in set (0.00 sec)

    mysql> select count(*) from tb1,tb2 where tb1.c1=tb2.c1;
    +----------+
    | count(*) |
    +----------+
    | 9949 |
    +----------+
    1 row in set (1 min 8.26 sec)

PostgreSQL只用了0.163秒
  1. postgres=# explain select count(*) from tb1,tb2 where tb1.c1=tb2.c1;
  2. QUERY PLAN
  3. -------------------------------------------------------------------------
  4. Aggregate (cost=23502.34..23502.35 rows=1 width=0)
  5. -> Hash Join (cost=31.50..23474.97 rows=10947 width=0)
  6. Hash Cond: (tb2.c1 = tb1.c1)
  7. -> Seq Scan on tb2 (cost=0.00..18334.00 rows=1000000 width=4)
  8. -> Hash (cost=19.00..19.00 rows=1000 width=4)
  9. -> Seq Scan on tb1 (cost=0.00..19.00 rows=1000 width=4)
  10. (6 rows)

  11. Time: 0.690 ms
  12. postgres=# select count(*) from tb1,tb2 where tb1.c1=tb2.c1;
  13. count
  14. -------
  15. 10068
  16. (1 row)

  17. Time: 163.868 ms

不过这个性能差别和统计信息无关,原因在于PG支持Nest Loop Join,Merge Join和Hash Join,而MySQL只支持Nest Loop Join,缺了索引Nest Loop Join会慢得跟龟似的。

4. 总结

1. MySQL的统计信息非常少,只有表行数和索引列的唯一值数目,这使得MySQL的优化器经常不能对数据规模有一个正确的认识而给出性能不佳的执行计划。
2.MySQL的join操作的效率非常依赖于索引(我之前两次帮人调优MySQL的SQL语句都是在加索引)。并不是说PG的join不需要索引,只是不像MySQL缺了索引的反应那么大。上面那个MySQL执行了1分多钟的例子,加上索引后,不管是MySQL还是PG的执行时间都立刻降到10毫秒以内。所以,开发人员在设计表的时候应该对可能的查询方式做个评估,把该建的索引都建上(不能少建也不宜多建)。
3.相比之下,PG不仅统计所有列的值分布,而且除了唯一值还有直方图,频繁值等等信息,支撑了PG的优化器做出正确的决策。猜测也是由于这个原因,PG社区认为PG的优化器已经足够智能,不需要把和Oracle类似的hint功能加到PG的内核里(因为hint可能会被人滥用,导致系统很难维护;不过,实在想用的话可以自己装pg_hint_plan插件)。

www.bkjia.comtruehttp://www.bkjia.com/PHPjc/1080260.htmlTechArticle为什么Mysql explain extended中的filtered列值总是100% 1. 问题 执行Mysql的explain extended的输出会比单纯的explain多一列filtered(MySQL5.7缺省就会输出fil...
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

새로운 보고서는 소문난 삼성 갤럭시 S25, 갤럭시 S25 플러스, 갤럭시 S25 울트라 카메라 업그레이드에 대한 비판적인 평가를 제공합니다. 새로운 보고서는 소문난 삼성 갤럭시 S25, 갤럭시 S25 플러스, 갤럭시 S25 울트라 카메라 업그레이드에 대한 비판적인 평가를 제공합니다. Sep 12, 2024 pm 12:23 PM

최근 아이스 유니버스는 삼성의 차기 플래그십 스마트폰으로 널리 알려진 갤럭시 S25 울트라에 대한 세부 정보를 꾸준히 공개해 왔습니다. 무엇보다도 유출자는 삼성이 카메라 업그레이드를 하나만 가져올 계획이라고 주장했습니다.

삼성 갤럭시 S25 울트라, 디자인 변경 루머가 공개된 첫 번째 렌더링 이미지 유출 삼성 갤럭시 S25 울트라, 디자인 변경 루머가 공개된 첫 번째 렌더링 이미지 유출 Sep 11, 2024 am 06:37 AM

OnLeaks는 이제 Android Headlines와 제휴하여 X(이전 Twitter) 팔로어로부터 4,000달러 이상의 수익을 창출하려는 시도가 실패한 지 며칠 후 Galaxy S25 Ultra에 대한 첫 번째 모습을 제공합니다. 맥락에 따라 h 아래에 포함된 렌더링 이미지

IFA 2024 | TCL의 NXTPAPER 14는 성능 면에서는 Galaxy Tab S10 Ultra와 일치하지 않지만 크기에서는 거의 일치합니다. IFA 2024 | TCL의 NXTPAPER 14는 성능 면에서는 Galaxy Tab S10 Ultra와 일치하지 않지만 크기에서는 거의 일치합니다. Sep 07, 2024 am 06:35 AM

TCL은 두 가지 새로운 스마트폰을 발표하는 것과 함께 NXTPAPER 14라는 새로운 Android 태블릿도 발표했는데, TCL의 거대한 화면 크기는 판매 포인트 중 하나입니다. NXTPAPER 14는 TCL의 시그니처 브랜드인 무광택 LCD 패널 버전 3.0을 갖추고 있습니다.

Vivo Y300 Pro는 7.69mm의 슬림한 본체에 6,500mAh 배터리를 탑재했습니다. Vivo Y300 Pro는 7.69mm의 슬림한 본체에 6,500mAh 배터리를 탑재했습니다. Sep 07, 2024 am 06:39 AM

Vivo Y300 Pro는 방금 완전히 공개되었으며 대용량 배터리를 갖춘 가장 얇은 중급 Android 휴대폰 중 하나입니다. 정확히 말하면 스마트폰의 두께는 7.69mm에 불과하지만 배터리 용량은 6,500mAh입니다. 최근 출시된 것과 동일한 용량이다.

Samsung Galaxy S24 FE는 4가지 색상과 2가지 메모리 옵션으로 예상보다 낮은 가격으로 출시될 예정 Samsung Galaxy S24 FE는 4가지 색상과 2가지 메모리 옵션으로 예상보다 낮은 가격으로 출시될 예정 Sep 12, 2024 pm 09:21 PM

삼성전자는 팬에디션(FE) 스마트폰 시리즈를 언제 업데이트할지 아직 힌트를 주지 않았다. 현재 상태로 Galaxy S23 FE는 2023년 10월 초에 출시된 회사의 최신 버전으로 남아 있습니다.

새로운 보고서는 소문난 삼성 갤럭시 S25, 갤럭시 S25 플러스, 갤럭시 S25 울트라 카메라 업그레이드에 대한 비판적인 평가를 제공합니다. 새로운 보고서는 소문난 삼성 갤럭시 S25, 갤럭시 S25 플러스, 갤럭시 S25 울트라 카메라 업그레이드에 대한 비판적인 평가를 제공합니다. Sep 12, 2024 pm 12:22 PM

최근 아이스 유니버스는 삼성의 차기 플래그십 스마트폰으로 널리 알려진 갤럭시 S25 울트라에 대한 세부 정보를 꾸준히 공개해 왔습니다. 무엇보다도 유출자는 삼성이 카메라 업그레이드를 하나만 가져올 계획이라고 주장했습니다.

Xiaomi Redmi Note 14 Pro Plus는 Light Hunter 800 카메라를 탑재한 최초의 Qualcomm Snapdragon 7s Gen 3 스마트폰으로 출시됩니다. Xiaomi Redmi Note 14 Pro Plus는 Light Hunter 800 카메라를 탑재한 최초의 Qualcomm Snapdragon 7s Gen 3 스마트폰으로 출시됩니다. Sep 27, 2024 am 06:23 AM

Redmi Note 14 Pro Plus는 이제 작년 Redmi Note 13 Pro Plus(Amazon에서 현재 $375)의 직접적인 후속 제품으로 공식화되었습니다. 예상대로 Redmi Note 14 Pro Plus는 Redmi Note 14 및 Redmi Note 14 Pro와 함께 Redmi Note 14 시리즈를 주도합니다. 리

iQOO Z9 Turbo Plus: 잠재적으로 강화된 시리즈 플래그십에 대한 예약 시작 iQOO Z9 Turbo Plus: 잠재적으로 강화된 시리즈 플래그십에 대한 예약 시작 Sep 10, 2024 am 06:45 AM

OnePlus의 자매 브랜드 iQOO는 2023-4년 제품 주기가 거의 끝날 수 있습니다. 그럼에도 불구하고 브랜드는 Z9 시리즈가 아직 끝나지 않았다고 선언했습니다. 최종이자 아마도 최고급인 Turbo+ 변형이 예상대로 발표되었습니다. 티

See all articles