ホームページ  >  記事  >  データベース  >  PHP 動的 Web サイト MySQL インデックス分析と最適化を高速化します。

PHP 動的 Web サイト MySQL インデックス分析と最適化を高速化します。

黄舟
黄舟オリジナル
2016-12-13 17:08:041183ブラウズ

この記事では主に、MySQL インデックス分析と動的 Web サイトの最適化を高速化する方法について説明します。

1. インデックスとは何ですか?

インデックスは、特定の値を持つレコードをすばやく検索するために使用されます。すべての MySQL インデックスは B ツリーの形式で保存されます。インデックスがない場合、クエリを実行するときに、MySQL は要件を満たすレコードが見つかるまで、テーブル全体のすべてのレコードを最初のレコードからスキャンする必要があります。テーブル内のレコードの数が増えるほど、この操作のコストも高くなります。検索条件として使用されるカラムにインデックスが作成されている場合、MySQL はレコードをスキャンすることなく、目的のレコードの位置を迅速に取得できます。テーブルに 1000 レコードがある場合、インデックスを使用してレコードを検索すると、レコードを順次スキャンするよりも少なくとも 100 倍高速になります。

people という名前のテーブルを作成するとします。

CREATE TABLE people ( peopleid SMALLINT NOT NULL, name CHAR(50) NOT NULL );

次に、1000 個の異なる名前の値を people テーブルに完全にランダムに挿入します。データ ファイル内では、名前列には明示的な順序がありません。 name カラムにインデックスを作成すると、MySQL はインデックス内の name カラムをソートし、インデックス内の各項目に対して、データ ファイル内の実際のレコードの場所への「ポインタ」を内部的に保存します。したがって、名前が「Mike」に等しいレコードの peopleid を見つけたい場合 (SQL コマンドは「SELECT peopleid FROM people WHERE name='Mike';」)、MySQL は「Mike」を検索できます。 name のインデックスの値を入力し、直接データ ファイルの対応する行に移動すると、その行の peopleid (999) が正確に返されます。このプロセス中、MySQL は 1 行を処理するだけで結果を返します。 「name」列にインデックスがない場合、MySQL はデータ ファイル内のすべてのレコード、つまり 1,000 レコードをスキャンします。明らかに、MySQL が処理する必要があるレコードの数が少ないほど、タスクをより速く完了できます。

2. インデックスの種類

MySQL は、選択できるさまざまなインデックス タイプを提供します:

通常のインデックス:

これは最も基本的なインデックス タイプであり、一意性などの制限はありません。通常のインデックスは次の方法で作成できます:

CREATE INDEX ON tablename (列のリスト) などのインデックスを作成します。

ALTER TABLE tablename ADD INDEX [name of ] のようにテーブルを変更します。インデックス] (列のリスト);

テーブル作成時にインデックスを指定します。例: CREATE TABLE tablename ([...], INDEX [インデックス名] (列のリスト));

一意のインデックス:

この種のインデックスと前述の「通常のインデックス」は基本的に同じですが、1 つの違いがあります。インデックス列のすべての値は 1 回だけ出現でき、一意である必要があります。一意のインデックスは次の方法で作成できます:

CREATE UNIQUE INDEX ON tablename (列のリスト) などのインデックスを作成します。

ALTER TABLE tablename ADD UNIQUE [name] のようにテーブルを変更します。 of Index] ] (列のリスト);

CREATE TABLE tablename ([...], UNIQUE [インデックス名] (列のリスト));

主キー: など、テーブルを作成するときにインデックスを指定します。

主キーはUniqueインデックスですが、「PRIMARY KEY」として指定する必要があります。 AUTO_INCREMENT 型の列を扱ったことがある場合は、主キーなどの概念にすでに精通しているかもしれません。主キーは通常、テーブルの作成時に「CREATE TABLE テーブル名 ([...], PRIMARY KEY (列のリスト));」のように指定します。ただし、「ALTER TABLE tablename ADD PRIMARY KEY (list of columns);」のようにテーブルを変更して主キーを追加することもできます。各テーブルは主キーを 1 つだけ持つことができます。

全文インデックス:

MySQL は、バージョン 3.23.23 以降、全文インデックス作成と全文検索をサポートします。 MySQL では、フルテキスト インデックスのインデックス タイプは FULLTEXT です。フルテキスト インデックスは、VARCHAR 型または TEXT 型の列に作成できます。これは、CREATE TABLE コマンド、または ALTER TABLE または CREATE INDEX コマンドによって作成できます。大規模なデータ セットの場合、ALTER TABLE (または CREATE INDEX) コマンドを使用してフルテキスト インデックスを作成する方が、フルテキスト インデックスを使用して空のテーブルにレコードを挿入するよりも高速です。この記事の以下の説明では、フルテキスト インデックスについては説明しません。詳細については、MySQL のドキュメントを参照してください。

3. 単一列インデックスと複数列インデックス

インデックスは単一列インデックスまたは複数列インデックスにすることができます。以下では、具体的な例を使用して、これら 2 つのインデックスの違いを説明します。このような people テーブルがあるとします。

CREATE TABLE people ( peopleid 
SMALLINT NOT NULL AUTO_INCREMENT,
firstname CHAR(50) NOT NULL, lastname CHAR(50) NOT NULL, 
age SMALLINT NOT NULL,
townid SMALLINT NOT NULL, PRIMARY KEY (peopleid) );

以下は、この people テーブルに挿入するデータです:

このデータ フラグメントには、「Mikes」という名前の 4 人の人物がいます (そのうちの 2 人はサリバンズ、2 人はマコネルズです)。 2人は17歳で、もう1人はジョー・スミスという珍しい名前だった。

このテーブルの主な目的は、指定されたユーザーの姓、名、年齢に基づいて、対応する peopleid を返すことです。たとえば、名前が Mike Sullivan で年齢が 17 歳のユーザーの peopleid を検索する必要がある場合があります (SQL コマンドは SELECT peopleid FROM people WHERE firstname='Mike' AND lastname='Sullivan' AND age=17 です) ;)。クエリが実行されるたびに MySQL がテーブル全体をスキャンすることを望まないため、ここではインデックスを考慮する必要があります。

首先,我们可以考虑在单个列上创建索引,比如firstname、lastname或者age列。如果我们创建firstname列的索引(ALTER TABLE people ADD INDEX firstname (firstname);),MySQL将通过这个索引迅速把搜索范围限制到那些firstname='Mike'的记录,然后再在这个“中间结果集”上进行其他条件的搜索:它首先排除那些lastname不等于“Sullivan”的记录,然后排除那些age不等于17的记录。当记录满足所有搜索条件之后,MySQL就返回最终的搜索结果。

由于建立了firstname列的索引,与执行表的完全扫描相比,MySQL的效率提高了很多,但我们要求MySQL扫描的记录数量仍旧远远超过了实际所需要的。虽然我们可以删除firstname列上的索引,再创建lastname或者age列的索引,但总地看来,不论在哪个列上创建索引搜索效率仍旧相似。

为了提高搜索效率,我们需要考虑运用多列索引。如果为firstname、lastname和age这三个列创建一个多列索引,MySQL只需一次检索就能够找出正确的结果!下面是创建这个多列索引的SQL命令:

ALTER TABLE people ADD INDEX fname_lname_age (firstname,lastname,age);

由于索引文件以B-树格式保存,MySQL能够立即转到合适的firstname,然后再转到合适的lastname,最后转到合适的age。在没有扫描数据文件任何一个记录的情况下,MySQL就正确地找出了搜索的目标记录!

那么,如果在firstname、lastname、age这三个列上分别创建单列索引,效果是否和创建一个firstname、lastname、age的多列索引一样呢?答案是否定的,两者完全不同。当我们执行查询的时候,MySQL只能使用一个索引。如果你有三个单列的索引,MySQL会试图选择一个限制最严格的索引。但是,即使是限制最严格的单列索引,它的限制能力也肯定远远低于firstname、lastname、age这三个列上的多列索引。

四、最左前缀

多列索引还有另外一个优点,它通过称为最左前缀(Leftmost Prefixing)的概念体现出来。继续考虑前面的例子,现在我们有一个firstname、lastname、age列上的多列索引,我们称这个索引为fname_lname_age。当搜索条件是以下各种列的组合时,MySQL将使用fname_lname_age索引:

firstname,lastname,age
firstname,lastname
firstname

从另一方面理解,它相当于我们创建了(firstname,lastname,age)、(firstname,lastname)以及(firstname)这些列组合上的索引。下面这些查询都能够使用这个fname_lname_age索引:

SELECT peopleid FROM people 
WHERE firstname='Mike' AND lastname='Sullivan' AND age='17'; 
SELECT peopleid FROM people WHERE firstname='Mike' AND lastname='Sullivan'; 
SELECT peopleid FROM people WHERE firstname='Mike'; 
The following queries cannot use the index at all: 
SELECT peopleid FROM people WHERE lastname='Sullivan'; 
SELECT peopleid FROM people WHERE age='17'; 
SELECT peopleid FROM people WHERE lastname='Sullivan' AND age='17';

五、选择索引列

在性能优化过程中,选择在哪些列上创建索引是最重要的步骤之一。可以考虑使用索引的主要有两种类型的列:在WHERE子句中出现的列,在join子句中出现的列。请看下面这个查询:

SELECT age ## 不使用索引
FROM people WHERE firstname='Mike' ## 考虑使用索引
AND lastname='Sullivan' ## 考虑使用索引

这个查询与前面的查询略有不同,但仍属于简单查询。由于age是在SELECT部分被引用,MySQL不会用它来限制列选择操作。因此,对于这个查询来说,创建age列的索引没有什么必要。下面是一个更复杂的例子:

SELECT people.age, ##不使用索引
town.name ##不使用索引
FROM people LEFT JOIN town ON
people.townid=town.townid ##考虑使用索引
WHERE firstname='Mike' ##考虑使用索引
AND lastname='Sullivan' ##考虑使用索引

与前面的例子一样,由于firstname和lastname出现在WHERE子句中,因此这两个列仍旧有创建索引的必要。除此之外,由于town表的townid列出现在join子句中,因此我们需要考虑创建该列的索引。那么,我们是否可以简单地认为应该索引WHERE子句和join子句中出现的每一个列呢?差不多如此,但并不完全。我们还必须考虑到对列进行比较的操作符类型。MySQL只有对以下操作符才使用索引:da9a35d818b73c4dd4603fcd175d71ed,>=,BETWEEN,IN,以及某些时候的LIKE。可以在LIKE操作中使用索引的情形是指另一个操作数不是以通配符(%或者_)开头的情形。例如,“SELECT peopleid FROM people WHERE firstname LIKE 'Mich%';”这个查询将使用索引,但“SELECT peopleid FROM people WHERE firstname LIKE '%ike';”这个查询不会使用索引。

以上内容就是加速PHP动态网站 MySQL索引分析和优化,希望可以帮助到大家,想要更多相关内容请关注PHP中文网(www.php.cn)!

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。