Rumah pangkalan data tutorial mysql MySQL一个错误查询有关问题追查

MySQL一个错误查询有关问题追查

Jun 07, 2016 pm 04:25 PM
mysql satu Pertanyaan kesilapan

MySQL一个异常查询问题追查 问题 线上碰到的问题:相同的语句,只是最后的limit行数不同。奇怪的是,limit 10 的性能比limit 100的语句还慢约10倍。隐藏用户表信息,语句及结果如下SELECT f1 , SUM(`f2`) `CNT` FROM T WHERE f1 IS NOT NULL AND f3 = '2014-0

MySQL一个异常查询问题追查

问题

<code>线上碰到的问题:相同的语句,只是最后的limit行数不同。奇怪的是,limit 10 的性能比limit 100的语句还慢约10倍。

隐藏用户表信息,语句及结果如下
SELECT  f1 , SUM(`f2`) `CNT` FROM T  WHERE f1 IS NOT NULL AND f3 = '2014-05-12'     GROUP BY f1 ORDER BY `CNT` DESC LIMIT 10;
执行时间3 min 3.65 sec

SELECT  f1 , SUM(`f2`) `CNT` FROM T  WHERE f1 IS NOT NULL AND f3 = '2014-05-12'     GROUP BY f1 ORDER BY `CNT` DESC LIMIT 100;
执行时间1.24Sec.

性能差距非常大!
</code>
Salin selepas log masuk

分析

<code>MySQL Tips:<span>追查语句执行时最常用的方法,是通过explain来看语句的执行计划。 </span></code><span>?</span>
Salin selepas log masuk
<code>更有冲击性的效果是通过缩小范围后,在这个数据下,limit 67和limit 68的执行计划相差很大。

两个执行计划:
LIMIT 67     

id: 1
select_type: SIMPLE
table: a
type: range
possible_keys: A,B,C
<span>key: B</span>
key_len: 387
ref: NULL
<span>rows: 2555192</span>
Extra: Using where; Using temporary; Using filesort
1 row in set (0.00 sec) 

LIMIT 68
id: 1
select_type: SIMPLE
table: a
type: ref
possible_keys: A,B,C
<span>key: A</span>
key_len: 3
ref: const
<span>rows: 67586</span>
Extra: Using where; Using temporary; Using filesort
1 row in set (0.00 sec)

可以看到,两个语句的执行计划不同:使用的索引不同。
</code>
Salin selepas log masuk
<code>
MySQL Tips:<span>explain的结果中,key表示最终使用的索引,rows表示使用这个索引需要扫描的行数,这是个估计值。</span>

表中 索引A定义为 (f3, f4, f1, f2, f5); 索引B定义为(f1, f2, f3);
</code>
Salin selepas log masuk

一个确认

<code>虽然rows是估计值,但是指导索引使用的依据。既然limit 68能达到rows 67586,         说明在第一个语句优化器可选结果中,也应该有此值,为什么不会选择索引A?
先确认一下我们上面的这个结论。</code>
Salin selepas log masuk
<code>MySQL Tips:<span>MySQL语法中能够用force index 来强行要求优化器使用某一个索引。</span>
</code>
Salin selepas log masuk

Explain SELECT f1 , SUM(f2)?CNT?FROM t force index(A) WHERE f1 IS NOT NULL AND f3 = ‘2014-05-12’ GROUP BY P ORDER BY?CNT?DESC LIMIT 67\G

id: 1?
select_type: SIMPLE?
table: a?
type: ref?
possible_keys:A?
key: A?
key_len: 3?
ref: const?
rows: 67586?
Extra: Using where; Using temporary; Using filesort?
1 row in set (0.00 sec)

<code>顺便说明,由于我们指定了force index,因此优化器不会考虑其他索引,possible_keys里只会显示A。
我们关注的是rows:67586。这说明在limit 67语句里,使用索引A也能够减少行扫描。</code>
Salin selepas log masuk
<code>MySQL Tips:<span>MySQL优化器会对possiable_key中的每个可能索引都计算查询代价,选择最小代价的查询计划。</span></code>
Salin selepas log masuk
<code><span>?</span>至此我们大概可以猜测,这个应该是MySQL实现上的bug:没有选择合适的索引,导致使用了明显错误的执行计划。</code>
Salin selepas log masuk
<code>MySQL Tips:<span>MySQL的优化器执行期间需要依赖于表的统计信息,而统计信息是估算值,因此有可能导致得到的执行计划非最优。</span></code>
Salin selepas log masuk
<code>但要说明的是,上述Tip是客观情况造成(可接受),但本例却是例外,因此优化器实际上可以拿到能够作出选择正确结果的数据(rows值),但是最终选择错误。
</code>
Salin selepas log masuk

原因分析

<code>MySQL优化器是按照查询代价的估算值,来确定要使用的索引。计算这个估算值的过程,基本是按照“估计需要扫描的行数”来确定的。
</code>
Salin selepas log masuk
<code>MySQL Tips:<span>MySQL在目前集团主流使用的5.1和5.5版本中只能使用前缀索引。</span>
</code>
Salin selepas log masuk

因此,使用索引A只能用上字段f3,使用索引B只能用上字段f1。Rows即为使用了索引查到上下界,之后需要扫描的数据行数(估算值)。

<code>上述的语句需要用到group和order by,因此执行计划中都有Using temporary; Using filesort。
流程上按顺序先计算使用索引A的查询代价。

之后依次计算其他possitabe_key的查询代价。由于过程中需要排序,在得到一个暂定结果后,需要判断是否有代价更低的排序方式(test_if_cheaper_ordering)。
与之前的大同小异,也是依靠估计扫描行数来计算代价。

在这个逻辑的实现过程中,存在一个bug:在估计当前索引的区分度的时候,没有考虑到前缀索引。

即:假设表中有50w行数据,索引B(f1,f2,f3),则计算索引区分度时,需要根据能够用上的前缀部分来确定。比如f1有1000个不同的值,则平均每个key值上的记录数为500.如(f1,f2)有10000个同的值,则平均每个组合key上的记录数为50,若(f1,f2,f3)有50w个不同的值,则平均每个组合key上的记录数为1。</code>
Salin selepas log masuk
<code>MySQL Tips:<span>每个key上的记录数越少,说明使用该索引查询时效率最高。对应于show index from tbl 输出结果中的Cardinality值越大。</span></code>
Salin selepas log masuk

在这个case下,索引A只能使用f1做前缀索引,但是在计算单key上的行平均值时用的是(f1,f2,f3),这就导致估算用索引B估算的时候,得到的代价偏小。导致误选。

回到问题本身

<code>1、  为什么limit值大的时候反而选对了呢?
</code><span>这是因为在计算B的查询代价时,查询需要返回的行数limit_rows也参与乘积,若limit值较大,则计算出来的B的代价就会更大,反而会由于代价。值超过A,而导致优化器最终选择A。</span>
Salin selepas log masuk
<code>2、  这个表有50w行数就,为什么limit相差1为就差别这么大?
这与语句本身有关。这个语句中有group by,这就意味着每多limit一个值,实际上需要扫描更多的行N。 这里N为“表的总行数”/“表中不同的f2值”。
也就是说这个语句使得这个bug有放大作用。
</code>
Salin selepas log masuk

解决方法

<code>分析清楚后解决方法就比较简单了,修改代码逻辑,在执行test_if_cheaper_ordering过程中,改用字段f1的区分度来计算即可。</code>
Salin selepas log masuk
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.

Artikel Panas

R.E.P.O. Kristal tenaga dijelaskan dan apa yang mereka lakukan (kristal kuning)
2 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
Repo: Cara menghidupkan semula rakan sepasukan
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island Adventure: Cara mendapatkan biji gergasi
3 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌

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)

Penyelesaian kepada ralat segera Windows Update Ralat 0x8024401c Penyelesaian kepada ralat segera Windows Update Ralat 0x8024401c Jun 08, 2024 pm 12:18 PM

Jadual Kandungan Penyelesaian 1 Penyelesaian 21. Padamkan fail sementara Windows Update 2. Membaiki fail sistem yang rosak 3. Lihat dan ubah suai entri pendaftaran 4. Matikan kad rangkaian IPv6 5. Jalankan alat WindowsUpdateTroubleshooter untuk membaiki 6. Matikan firewall dan perisian anti-virus lain yang berkaitan. 7. Tutup perkhidmatan WidowsUpdate. Penyelesaian 3 Penyelesaian 4 Ralat "0x8024401c" berlaku semasa kemas kini Windows pada komputer Huawei Gejala Masalah Punca Penyelesaian Masih tidak diselesaikan? Baru-baru ini, pelayan web perlu dikemas kini kerana kelemahan sistem Selepas log masuk ke pelayan, kemas kini menggesa kod ralat 0x8024401c

Kemahiran pemprosesan struktur data besar PHP Kemahiran pemprosesan struktur data besar PHP May 08, 2024 am 10:24 AM

Kemahiran pemprosesan struktur data besar: Pecahan: Pecahkan set data dan proseskannya dalam bahagian untuk mengurangkan penggunaan memori. Penjana: Hasilkan item data satu demi satu tanpa memuatkan keseluruhan set data, sesuai untuk set data tanpa had. Penstriman: Baca fail atau hasil pertanyaan baris demi baris, sesuai untuk fail besar atau data jauh. Storan luaran: Untuk set data yang sangat besar, simpan data dalam pangkalan data atau NoSQL.

Bagaimana untuk mengoptimumkan prestasi pertanyaan MySQL dalam PHP? Bagaimana untuk mengoptimumkan prestasi pertanyaan MySQL dalam PHP? Jun 03, 2024 pm 08:11 PM

Prestasi pertanyaan MySQL boleh dioptimumkan dengan membina indeks yang mengurangkan masa carian daripada kerumitan linear kepada kerumitan logaritma. Gunakan PreparedStatements untuk menghalang suntikan SQL dan meningkatkan prestasi pertanyaan. Hadkan hasil pertanyaan dan kurangkan jumlah data yang diproses oleh pelayan. Optimumkan pertanyaan penyertaan, termasuk menggunakan jenis gabungan yang sesuai, membuat indeks dan mempertimbangkan untuk menggunakan subkueri. Menganalisis pertanyaan untuk mengenal pasti kesesakan; gunakan caching untuk mengurangkan beban pangkalan data;

Bagaimana untuk menggunakan sandaran dan pemulihan MySQL dalam PHP? Bagaimana untuk menggunakan sandaran dan pemulihan MySQL dalam PHP? Jun 03, 2024 pm 12:19 PM

Membuat sandaran dan memulihkan pangkalan data MySQL dalam PHP boleh dicapai dengan mengikuti langkah berikut: Sandarkan pangkalan data: Gunakan arahan mysqldump untuk membuang pangkalan data ke dalam fail SQL. Pulihkan pangkalan data: Gunakan arahan mysql untuk memulihkan pangkalan data daripada fail SQL.

Bagaimana untuk memasukkan data ke dalam jadual MySQL menggunakan PHP? Bagaimana untuk memasukkan data ke dalam jadual MySQL menggunakan PHP? Jun 02, 2024 pm 02:26 PM

Bagaimana untuk memasukkan data ke dalam jadual MySQL? Sambung ke pangkalan data: Gunakan mysqli untuk mewujudkan sambungan ke pangkalan data. Sediakan pertanyaan SQL: Tulis pernyataan INSERT untuk menentukan lajur dan nilai yang akan dimasukkan. Laksanakan pertanyaan: Gunakan kaedah query() untuk melaksanakan pertanyaan sisipan Jika berjaya, mesej pengesahan akan dikeluarkan.

Bagaimana untuk membetulkan ralat mysql_native_password tidak dimuatkan pada MySQL 8.4 Bagaimana untuk membetulkan ralat mysql_native_password tidak dimuatkan pada MySQL 8.4 Dec 09, 2024 am 11:42 AM

Salah satu perubahan utama yang diperkenalkan dalam MySQL 8.4 (keluaran LTS terkini pada 2024) ialah pemalam &quot;Kata Laluan Asli MySQL&quot; tidak lagi didayakan secara lalai. Selanjutnya, MySQL 9.0 mengalih keluar pemalam ini sepenuhnya. Perubahan ini mempengaruhi PHP dan apl lain

Bagaimana untuk menggunakan prosedur tersimpan MySQL dalam PHP? Bagaimana untuk menggunakan prosedur tersimpan MySQL dalam PHP? Jun 02, 2024 pm 02:13 PM

Untuk menggunakan prosedur tersimpan MySQL dalam PHP: Gunakan PDO atau sambungan MySQLi untuk menyambung ke pangkalan data MySQL. Sediakan penyata untuk memanggil prosedur tersimpan. Laksanakan prosedur tersimpan. Proses set keputusan (jika prosedur tersimpan mengembalikan hasil). Tutup sambungan pangkalan data.

Bagaimana untuk membuat jadual MySQL menggunakan PHP? Bagaimana untuk membuat jadual MySQL menggunakan PHP? Jun 04, 2024 pm 01:57 PM

Mencipta jadual MySQL menggunakan PHP memerlukan langkah berikut: Sambung ke pangkalan data. Buat pangkalan data jika ia tidak wujud. Pilih pangkalan data. Buat jadual. Laksanakan pertanyaan. Tutup sambungan.

See all articles