制限付きMySQLはパフォーマンスに影響を与えるだろうか?

I.はじめに 

 まず、MySQLのバージョンを説明します。

MySQLの>  を選択したバージョン();
+ - --------- + 
| バージョン()| 
+ - --------- + 
|  5.717     | 
+ - --------- + 
1 セット0.00秒)

表の構造:

mysqlの>  DESC テスト。
+ - ------ + --------------------- + ------ + ----- + ----- ---- + ---------------- + 
| フィールド   | タイプ                 |  ヌル |  キー |  デフォルト | エクストラ           | 
+ - ------ + --------------------- + ------ + ----- + ----- ---- + ---------------- + 
| ID      |  BIGINT20)符号なし| NO    | PRI |  NULL     | AUTO_INCREMENT | 
| ヴァル     | INT10)符号なし     | NO    | MUL |  0        |                 | 
| ソース|  INT10)符号なし     | NO    |      |  0        |                 | 
+ - ------ + --------------------- + ------ + ----- + ----- ---- + ---------------- + 
3 セット0.00 秒)
 

主キーの増加へのid、valの非ユニークなインデックス。

 

データの注ぎ大量の500万ドルの合計:

MySQLの>  を選択し 、カウント*からテスト。

+ - -------- + 
|  カウント*| 
+ - -------- + 
|   5242882  | 
+ - -------- + 
1 セット4.25秒)

私たちは、オフセット限界が大の行をオフセットする場合、効率があることを知っています:

MySQLの>  を選択 *  からテストここヴァル= 4リミット3000005 

+ - ------- + ----- + -------- + 
| ID       | ヴァル| ソース| 
+ - ------- + ----- + -------- + 
|  3327622  |    4  |       4  | 
|  3327632  |    4  |       4  | 
|  3327642  |    4  |       4  | 
|  3327652  |    4  |       4  | 
|  3327662  |    4  |       4  | 
+ - ------- + ----- + -------- + 
5 セット15.98秒)

 同じ目的を達成するために、我々は一般的に次の文を書き換えます。

MySQLの>  を選択 *  からテスト内側に 参加選択 IDをからテストここヴァル= 4リミット3000005 B) a.id = b.id。

 

+ - ------- + ----- + -------- + --------- + 
| ID       | ヴァル| ソース| ID       | 
+ - ------- + ----- + -------- + --------- + 
|  3327622  |    4  |       4  |  3327622  | 
|  3327632  |    4  |       4  |  3327632  | 
|  3327642  |    4  |       4  |  3327642  | 
|  3327652  |    4  |       4  |  3327652  |
|  3327662  |    4  |       4  |  3327662  | 
+ - ------- + ----- + -------- + --------- + 
5 セット0.38秒)

時間差は明らかです。

 

なぜ、上記の結果はありますか?私たちは、テストどこのval = 4 SELECT * FROM見リミット300000,5、 クエリプロセス:

 索引リーフ・ノード・データへのクエリ。

インデックスフィールド値のすべてのクエリは、リーフノードクラスタへのマスターキーに応じて必要。


下の絵に似て

 

最後の5つを取り外し、前に必要、iノード300005回、300005回のクエリデータクラスタ化インデックス、上記のように、最終的な結果は300,000濾過しました。クエリに300000ランダムI / Oデータがセットの結果には表示されませんしながら、MySQLは、データクエリクラスタ化インデックスにランダムI / Oの多くの費用。

推奨:いくつかの広範な原則MySQLのインデックスのB +ツリーの原理と同様に、インデックスを構築します

 

きっと誰かが尋ねてきます:インデックスの使用が始まって以来、なぜそのノードに索引リーフ・ノードに沿っていない最初のチェック最後の5つのニーズは、実際のデータを照会するためにクラスタ化インデックスを行きます。唯一の5ランダムI / Oように、プロセスは以下の画像のようになります。

 

実際に、私はこの質問をお願いしたいと思います。

確認

ここでは、上記の推論を確認するために、実際の動作を見てみましょう。

確認のため

select * from test where val=4 limit 300000,5

是扫描300005个索引节点和300005个聚簇索引上的数据节点,我们需要知道MySQL有没有办法统计在一个sql中通过索引节点查询数据节点的次数。我先试了Handler_read_*系列,很遗憾没有一个变量能满足条件。

 

我只能通过间接的方式来证实:

InnoDB中有buffer pool。里面存有最近访问过的数据页,包括数据页和索引页。所以我们需要运行两个sql,来比较buffer pool中的数据页的数量。预测结果是运行select * from test a inner join (select id from test where val=4 limit 300000,5) b>之后,buffer pool中的数据页的数量远远少于select * from test where val=4 limit 300000,5;对应的数量,因为前一个sql只访问5次数据页,而后一个sql访问300005次数据页。

select * from test where val=4 limit 300000,5
mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('val','primary') and TABLE_NAME like '%test%' group by index_name;
Empty set (0.04 sec)

可以看出,目前buffer pool中没有关于test表的数据页。

mysql> select * from test where val=4 limit 300000,5;
+---------+-----+--------+
| id      | val | source |
+---------+-----+--------+
| 3327622 |   4 |      4 |
| 3327632 |   4 |      4 |
| 3327642 |   4 |      4 |
| 3327652 |   4 |      4 |
| 3327662 |   4 |      4 |
+---------+-----+--------+
5 rows in set (26.19 sec)
 
mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('val','primary') and TABLE_NAME like '%test%' group by index_name;
+------------+----------+
| index_name | count(*) |
+------------+----------+
| PRIMARY    |     4098 |
| val        |      208 |
+------------+----------+
2 rows in set (0.04 sec)

可以看出,此时buffer pool中关于test表有4098个数据页,208个索引页。

select * from test a inner join (select id from test where val=4 limit 300000,5) b>为了防止上次试验的影响,我们需要清空buffer pool,重启mysql。

mysqladmin shutdown
/usr/local/bin/mysqld_safe &
mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('val','primary') and TABLE_NAME like '%test%' group by index_name;
Empty set (0.03 sec)

运行sql:

mysql> select * from test a inner join (select id from test where val=4 limit 300000,5) b on a.id=b.id;
+---------+-----+--------+---------+
| id      | val | source | id      |
+---------+-----+--------+---------+
| 3327622 |   4 |      4 | 3327622 |
| 3327632 |   4 |      4 | 3327632 |
| 3327642 |   4 |      4 | 3327642 |
| 3327652 |   4 |      4 | 3327652 |
| 3327662 |   4 |      4 | 3327662 |
+---------+-----+--------+---------+
5 rows in set (0.09 sec)
mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('val','primary') and TABLE_NAME like '%test%' group by index_name;
+------------+----------+
| index_name | count(*) |
+------------+----------+
| PRIMARY    |        5 |
| val        |      390 |
+------------+----------+
2 rows in set (0.03 sec)

我们可以看明显的看出两者的差别:第一个sql加载了4098个数据页到buffer pool,而第二个sql只加载了5个数据页到buffer pool。符合我们的预测。也证实了为什么第一个sql会慢:读取大量的无用数据行(300000),最后却抛弃掉。

而且这会造成一个问题:加载了很多热点不是很高的数据页到buffer pool,会造成buffer pool的污染,占用buffer pool的空间。

遇到的问题

 

为了在每次重启时确保清空buffer pool,我们需要关闭innodb_buffer_pool_dump_at_shutdown和innodb_buffer_pool_load_at_startup,这两个选项能够控制数据库关闭时dump出buffer pool中的数据和在数据库开启时载入在磁盘上备份buffer pool的数据。

 

参考资料:

1.https://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/

2.https://dev.mysql.com/doc/refman/5.7/en/innodb-information-schema-buffer-pool-tables.html

 

作者:zhangyachen
来源:https://dwz.cn/K1Q1cePW

おすすめ

転載: www.cnblogs.com/FYZHANG/p/zhangyachen.html