Jadual Kandungan
为什么Mysql explain extended中的filtered列值总是100%
1. 问题
2.原因
3. 引申
4. 总结
Rumah pembangunan bahagian belakang tutorial 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...
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

AI Hentai Generator

AI Hentai Generator

Menjana ai hentai secara percuma.

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Laporan baharu memberikan penilaian yang memberatkan terhadap khabar angin tentang peningkatan kamera Samsung Galaxy S25, Galaxy S25 Plus dan Galaxy S25 Ultra Laporan baharu memberikan penilaian yang memberatkan terhadap khabar angin tentang peningkatan kamera Samsung Galaxy S25, Galaxy S25 Plus dan Galaxy S25 Ultra Sep 12, 2024 pm 12:23 PM

Dalam beberapa hari kebelakangan ini, Ice Universe terus mendedahkan butiran mengenai Galaxy S25 Ultra, yang secara meluas dipercayai sebagai telefon pintar utama Samsung yang seterusnya. Antara lain, pembocor itu mendakwa bahawa Samsung hanya merancang untuk membawa satu peningkatan kamera

Samsung Galaxy S25 Ultra bocor dalam imej paparan pertama dengan perubahan reka bentuk khabar angin didedahkan Samsung Galaxy S25 Ultra bocor dalam imej paparan pertama dengan perubahan reka bentuk khabar angin didedahkan Sep 11, 2024 am 06:37 AM

OnLeaks kini telah bekerjasama dengan Android Headlines untuk memberikan pandangan pertama pada Galaxy S25 Ultra, beberapa hari selepas percubaan gagal untuk menjana lebih daripada $4,000 daripada pengikut X (dahulunya Twitter). Untuk konteks, imej pemaparan yang dibenamkan di bawah h

IFA 2024 | NXTPAPER 14 TCL tidak akan sepadan dengan prestasi Galaxy Tab S10 Ultra, tetapi ia hampir sepadan dengan saiznya IFA 2024 | NXTPAPER 14 TCL tidak akan sepadan dengan prestasi Galaxy Tab S10 Ultra, tetapi ia hampir sepadan dengan saiznya Sep 07, 2024 am 06:35 AM

Di samping mengumumkan dua telefon pintar baharu, TCL juga telah mengumumkan tablet Android baharu yang dipanggil NXTPAPER 14, dan saiz skrinnya yang besar adalah salah satu nilai jualannya. NXTPAPER 14 menampilkan versi 3.0 jenama tandatangan TCL panel LCD matte

Vivo Y300 Pro mempunyai bateri 6,500 mAh dalam badan 7.69 mm yang tipis Vivo Y300 Pro mempunyai bateri 6,500 mAh dalam badan 7.69 mm yang tipis Sep 07, 2024 am 06:39 AM

Vivo Y300 Pro baru sahaja didedahkan sepenuhnya, dan ia merupakan salah satu telefon Android jarak pertengahan paling tipis dengan bateri yang besar. Tepatnya, telefon pintar ini hanya tebal 7.69 mm tetapi mempunyai bateri 6,500 mAh. Ini adalah kapasiti yang sama seperti yang dilancarkan baru-baru ini

Samsung Galaxy S24 FE dibilkan untuk melancarkan kurang daripada yang dijangkakan dalam empat warna dan dua pilihan memori Samsung Galaxy S24 FE dibilkan untuk melancarkan kurang daripada yang dijangkakan dalam empat warna dan dua pilihan memori Sep 12, 2024 pm 09:21 PM

Samsung belum menawarkan sebarang petunjuk lagi tentang bila ia akan mengemas kini siri telefon pintar Edisi Peminat (FE). Seperti sedia ada, Galaxy S23 FE kekal sebagai edisi terbaharu syarikat, telah dibentangkan pada awal Oktober 2023. Walau bagaimanapun, banyak

Laporan baharu memberikan penilaian yang memberatkan terhadap khabar angin tentang peningkatan kamera Samsung Galaxy S25, Galaxy S25 Plus dan Galaxy S25 Ultra Laporan baharu memberikan penilaian yang memberatkan terhadap khabar angin tentang peningkatan kamera Samsung Galaxy S25, Galaxy S25 Plus dan Galaxy S25 Ultra Sep 12, 2024 pm 12:22 PM

Dalam beberapa hari kebelakangan ini, Ice Universe terus mendedahkan butiran mengenai Galaxy S25 Ultra, yang secara meluas dipercayai sebagai telefon pintar utama Samsung yang seterusnya. Antara lain, pembocor itu mendakwa bahawa Samsung hanya merancang untuk membawa satu peningkatan kamera

Xiaomi Redmi Note 14 Pro Plus tiba sebagai telefon pintar Qualcomm Snapdragon 7s Gen 3 pertama dengan kamera Light Hunter 800 Xiaomi Redmi Note 14 Pro Plus tiba sebagai telefon pintar Qualcomm Snapdragon 7s Gen 3 pertama dengan kamera Light Hunter 800 Sep 27, 2024 am 06:23 AM

Redmi Note 14 Pro Plus kini rasmi sebagai pengganti langsung kepada Redmi Note 13 Pro Plus tahun lepas (sekira $375 di Amazon). Seperti yang dijangkakan, Redmi Note 14 Pro Plus mengetuai siri Redmi Note 14 bersama Redmi Note 14 dan Redmi Note 14 Pro. Li

iQOO Z9 Turbo Plus: Tempahan bermula untuk siri perdana yang berpotensi dipertingkatkan iQOO Z9 Turbo Plus: Tempahan bermula untuk siri perdana yang berpotensi dipertingkatkan Sep 10, 2024 am 06:45 AM

Jenama OnePlus iQOO mempunyai kitaran produk 2023-4 yang mungkin hampir tamat; Namun begitu, jenama itu telah mengisytiharkan bahawa ia belum selesai dengan siri Z9nya. Varian Turbo+yang terakhir, dan mungkin paling akhir, baru sahaja diumumkan seperti yang diramalkan. T

See all articles