Der Zweck des Index besteht darin, die Abfrageeffizienz zu verbessern, was mit einem Wörterbuch verglichen werden kann. Wenn wir das Wort „MySQL“ nachschlagen möchten, müssen wir unbedingt den Buchstaben „m“ finden und dann den Buchstaben „y“ von unten finden nach unten, und finden Sie dann die verbleibende SQL. Ohne einen Index müssen Sie möglicherweise alle Wörter durchsehen, um das Gesuchte zu finden.
1. Vorteile des Index
Angenommen, Sie haben drei nicht indizierte Tabellen t1, t2 und t3. Jede Tabelle enthält die Datenspalten i1, i2 und i3, und jede Tabelle enthält 1000 Datenzeilen mit Seriennummern von 1 bis 1000. . Eine Abfrage zum Finden von Kombinationen von Datenzeilen, die mit bestimmten Werten übereinstimmen, könnte wie folgt aussehen:
SELECT t1.i1, t2.i2, t3.i3 FROM t1, t2, t3 WHERE t1.i1 = t2.i2 AND t2.i1 = t3.i3;
Das Ergebnis dieser Abfrage sollte 1000 Zeilen sein, wobei jede Datenzeile drei gleiche Werte enthält. Wenn wir diese Abfrage ohne Indizes verarbeiten würden, hätten wir keine Möglichkeit zu wissen, welche Zeilen welche Werte enthielten, ohne alle Tabellen zu scannen. Daher müssen Sie alle Kombinationen ausprobieren, um Datensätze zu finden, die der WHERE-Bedingung entsprechen. Die Anzahl möglicher Kombinationen beträgt 1000 x 1000 x 1000 (eine Milliarde!), was einer Million Mal der Anzahl übereinstimmender Datensätze entspricht. Dadurch wird viel Arbeit verschwendet. Dieses Beispiel zeigt, dass ohne die Verwendung von Indizes die Zeit, die für die Verarbeitung von Verknüpfungen zwischen diesen Tabellen aufgewendet wird, schneller zunimmt, wenn die Datensätze in den Tabellen wachsen, was zu einer schlechten Leistung führt. Durch die Indizierung dieser Datentabellen können wir erhebliche Geschwindigkeitsverbesserungen erzielen, da Indizes die Verarbeitung von Abfragen wie folgt ermöglichen:
1. Wählen Sie die erste Zeile in Tabelle t1 aus und sehen Sie sich den Wert dieser Datenzeile an.
2. Verwenden Sie den Index für Tabelle t2, um direkt die Datenzeile zu finden, die dem Wert von t1 entspricht. Verwenden Sie in ähnlicher Weise den Index für Tabelle t3, um direkt die Datenzeile zu finden, die dem Wert in Tabelle t2 entspricht.
3. Verarbeiten Sie die nächste Zeile der Tabelle t1 und wiederholen Sie den vorherigen Vorgang. Dieser Vorgang wird ausgeführt, bis alle Datenzeilen in t1 überprüft wurden.
In diesem Fall führen wir immer noch einen vollständigen Scan für Tabelle t1 durch, können aber Indexsuchen für t2 und t3 durchführen, um Zeilen direkt aus diesen Tabellen abzurufen. Das Ausführen der obigen Abfrage auf diese Weise wäre theoretisch eine Million Mal schneller. Natürlich ist dieses Beispiel künstlich etabliert, um Schlussfolgerungen zu ziehen. Das dadurch gelöste Problem ist jedoch real und das Hinzufügen von Indizes zu Tabellen, die keine Indizes haben, führt oft zu erstaunlichen Leistungsverbesserungen.
-
2. Die Kosten der Indexierung
Erstens beschleunigen Indizes das Abrufen, verlangsamen jedoch das Einfügen und Löschen sowie das Aktualisieren von Werten in indizierten Datenspalten. Das heißt, Indizes verlangsamen die meisten Schreibvorgänge. Der Grund für dieses Phänomen liegt darin, dass beim Schreiben eines Datensatzes nicht nur die Datenzeilen geschrieben werden müssen, sondern auch alle Indizes geändert werden müssen. Je mehr Indizes eine Datentabelle hat, desto mehr Änderungen müssen vorgenommen werden und desto größer ist die Verringerung der durchschnittlichen Leistung. Im Abschnitt „Daten effizient laden“ dieses Artikels gehen wir näher auf diese Phänomene ein und erfahren, wie wir damit umgehen können.
Zweitens verbrauchen Indizes Speicherplatz und mehrere Indizes verbrauchen entsprechend mehr Speicherplatz. Dies kann dazu führen, dass die Größenbeschränkung der Datentabelle schneller erreicht wird:
· Bei MyISAM-Tabellen kann eine häufige Indizierung dazu führen, dass die Indexdatei schneller die maximale Grenze erreicht als die Datendatei.
· Bei BDB-Tabellen, die Daten und Indexwerte zusammen in derselben Datei speichern, führt das Hinzufügen eines Index dazu, dass solche Tabellen schneller das maximale Dateilimit erreichen.
Alle im gemeinsam genutzten Tablespace von InnoDB zugewiesenen Tabellen konkurrieren um die Nutzung desselben gemeinsamen Space-Pools, sodass durch das Hinzufügen von Indizes der Speicherplatz im Tablespace schneller erschöpft wird. Im Gegensatz zu den von MyISAM- und BDB-Tabellen verwendeten Dateien ist der gemeinsam genutzte InnoDB-Tablespace jedoch nicht durch die Dateigröße des Betriebssystems begrenzt, da wir ihn für die Verwendung mehrerer Dateien konfigurieren können. Solange zusätzlicher Speicherplatz verfügbar ist, können Sie den Tabellenbereich durch Hinzufügen neuer Komponenten erweitern.
Eine InnoDB-Tabelle, die einen separaten Tabellenbereich verwendet, unterliegt denselben Einschränkungen wie eine BDB-Tabelle, da ihre Daten und Indexwerte in einer einzigen Datei gespeichert werden.
Was diese Elemente tatsächlich bedeuten, ist: Wenn Sie keinen speziellen Index benötigen, um die Ausführung von Abfragen zu beschleunigen, erstellen Sie keinen Index.
3. Index auswählen
Es wird davon ausgegangen, dass Sie die Syntax für die Indizierung bereits kennen, die Syntax sagt Ihnen jedoch nicht, wie die Datentabelle indiziert werden soll. Dies erfordert, dass wir darüber nachdenken, wie Datentabellen verwendet werden. In diesem Abschnitt erfahren Sie, wie Sie mögliche Datenspalten für die Indizierung identifizieren und wie Sie am besten Indizes erstellen:
Indexdatenspalten, die zum Suchen, Sortieren und Gruppieren verwendet werden, dienen nicht nur der Ausgabeanzeige. Mit anderen Worten: Die besten Kandidaten für die Datenspalten für die Indizierung sind diejenigen, die in der WHERE-Klausel, der Join-Klausel, der ORDER BY- oder der GROUP BY-Klausel vorkommen. Datenspalten, die nur in der Ausgabedatenspaltenliste nach dem Schlüsselwort SELECT erscheinen, sind keine geeigneten Kandidatenspalten:
SELECT col_a <- 不是备选列 FROM tbl1 LEFT JOIN tbl2 ON tbl1.col_b = tbl2.col_c <- 备选列 WHERE col_d = expr; <- 备选列
Natürlich können die angezeigte Datenspalte und die in der WHERE-Klausel verwendete Datenspalte auch identisch sein. Unser Punkt ist, dass die Datenspalten in der Ausgabeliste grundsätzlich keine guten Kandidaten für die Indizierung sind.
Join子句或WHERE子句中类似col1 = col2形式的表达式中的数据列都是特别好的索引备选列。前面显示的查询中的col_b和col_c就是这样的例子。如果MySQL能够利用联结列来优化查询,它一定会通过减少整表扫描来大幅度减少潜在的表-行组合。
考虑数据列的基数(cardinality)。基数是数据列所包含的不同值的数量。例如,某个数据列包含值1、3、7、4、7、3,那么它的基数就是4。索引的基数相对于数据表行数较高(也就是说,列中包含很多不同的值,重复的值很少)的时候,它的工作效果最好。如果某数据列含有很多不同的年龄,索引会很快地分辨数据行。如果某个数据列用于记录性别(只有”M”和”F”两种值),那么索引的用处就不大。如果值出现的几率几乎相等,那么无论搜索哪个值都可能得到一半的数据行。在这些情况下,最好根本不要使用索引,因为查询优化器发现某个值出现在表的数据行中的百分比很高的时候,它一般会忽略索引,进行全表扫描。惯用的百分比界线是”30%”。现在查询优化器更加复杂,把其它一些因素也考虑进去了,因此这个百分比并不是MySQL决定选择使用扫描还是索引的唯一因素。
索引较短的值。尽可能地使用较小的数据类型。例如,如果MEDIUMINT足够保存你需要存储的值,就不要使用BIGINT数据列。如果你的值不会长于25个字符,就不要使用CHAR(100)。较小的值通过几个方面改善了索引的处理速度:
· 较短的值可以更快地进行比较,因此索引的查找速度更快了。
· 较小的值导致较小的索引,需要更少的磁盘I/O。
· 使用较短的键值的时候,键缓存中的索引块(block)可以保存更多的键值。MySQL可以在内存中一次保持更多的键,在不需要从磁盘读取额外的索引块的情况下,提高键值定位的可能性。
对于InnoDB和BDB等使用聚簇索引(clustered index)的存储引擎来说,保持主键(primary key)短小的优势更突出。聚簇索引中数据行和主键值存储在一起(聚簇在一起)。其它的索引都是次级索引;它们存储主键值和次级索引值。次级索引屈从主键值,它们被用于定位数据行。这暗示主键值都被复制到每个次级索引中,因此如果主键值很长,每个次级索引就需要更多的额外空间。
索引字符串值的前缀(prefixe)。如果你需要索引一个字符串数据列,那么最好在任何适当的情况下都应该指定前缀长度。例如,如果有CHAR(200)数据列,如果前面10个或20个字符都不同,就不要索引整个数据列。索引前面10个或20个字符会节省大量的空间,并且可能使你的查询速度更快。通过索引较短的值,你可以获得那些与比较速度和磁盘I/O节省相关的好处。当然你也需要利用常识。仅仅索引某个数据列的第一个字符串可能用处不大,因为如果这样操作,那么在索引中不会有太多的唯一值。
你可以索引CHAR、VARCHAR、BINARY、VARBINARY、BLOB和TEXT数据列的前缀。
使用最左(leftmost)前缀。建立多列复合索引的时候,你实际上建立了MySQL可以使用的多个索引。复合索引可以作为多个索引使用,因为索引中最左边的列集合都可以用于匹配数据行。这种列集合被称为”最左前缀”(它与索引某个列的前缀不同,那种索引把某个列的前面几个字符作为索引值)。
假设你在表的state、city和zip数据列上建立了复合索引。索引中的数据行按照state/city/zip次序排列,因此它们也会自动地按照state/city和state次序排列。这意味着,即使你在查询中只指定了state值,或者指定state和city值,MySQL也可以使用这个索引。因此,这个索引可以被用于搜索如下所示的数据列组合:
state, city, zip state, city state
MySQL不能利用这个索引来搜索没有包含在最左前缀的内容。例如,如果你按照city或zip来搜索,就不会使用到这个索引。如果你搜索给定的state和具体的ZIP代码(索引的1和3列),该索引也是不能用于这种组合值的,尽管MySQL可以利用索引来查找匹配的state从而缩小搜索的范围。
不要过多地索引。不要认为”索引越多,性能越高”,不要对每个数据列都进行索引。我们在前面提到过,每个额外的索引都会花费更多的磁盘空间,并降低写操作的性能。当你修改表的内容的时候,索引就必须被更新,甚至可能重新整理。如果你的索引很少使用或永不使用,你就没有必要减小表的修改操作的速度。此外,为检索操作生成执行计划的时候,MySQL会考虑索引。建立额外的索引会给查询优化器增加更多的工作量。如果索引太多,有可能(未必)出现MySQL选择最优索引失败的情况。维护自己必须的索引可以帮助查询优化器来避免这类错误。
如果你考虑给已经索引过的表添加索引,那么就要考虑你将增加的索引是否是已有的多列索引的最左前缀。如果是这样的,不用增加索引,因为已经有了(例如,如果你在state、city和zip上建立了索引,那么没有必要再增加state的索引)。
让索引类型与你所执行的比较的类型相匹配。在你建立索引的时候,大多数存储引擎会选择它们将使用的索引实现。例如,InnoDB通常使用B树索引。MySQL也使用B树索引,它只在三维数据类型上使用R树索引。但是,MEMORY存储引擎支持散列索引和B树索引,并允许你选择使用哪种索引。为了选择索引类型,需要考虑在索引数据列上将执行的比较操作类型:
· 对于散列(hash)索引,会在每个数据列值上应用散列函数。生成的结果散列值存储在索引中,并用于执行查询。散列函数实现的算法类似于为不同的输入值生成不同的散列值。使用散列值的好处是散列值比原始值的比较效率更高。散列索引用于执行=或<=>操作等精确匹配的时候速度非常快。但是对于查询一个值的范围效果就非常差了:
id < 30 weight BETWEEN 100 AND 150
· B树索引可以用于高效率地执行精确的或者基于范围(使用操作<、<=、=、>=、>、<>、!=和BETWEEN)的比较。B树索引也可以用于LIKE模式匹配,前提是该模式以文字串而不是通配符开头。
如果你使用的MEMORY数据表只进行精确值查询,散列索引是很好的选择。这是MEMORY表使用的默认的索引类型,因此你不需要特意指定。如果你希望在MEMORY表上执行基于范围的比较,应该使用B树索引。为了指定这种索引类型,需要给索引定义添加USING BTREE。例如:
CREATE TABLE lookup ( id INT NOT NULL, name CHAR(20), PRIMARY KEY USING BTREE (id) ) ENGINE = MEMORY;
如果你希望执行的语句的类型允许,单个MEMORY表可以同时拥有散列索引和B树索引,即使在同一个数据列上。
有些类型的比较不能使用索引。如果你只是通过把值传递到函数(例如STRCMP())中来执行比较操作,那么对它进行索引就没有价值。服务器必须计算出每个数据行的函数值,它会排除数据列上索引的使用。
使用慢查询(slow-query)日志来识别执行情况较差的查询。这个日志可以帮助你找出从索引中受益的查询。你可以直接查看日志(它是文本文件),或者使用mysqldumpslow工具来统计它的内容。如果某个给定的查询多次出现在”慢查询”日志中,这就是一个线索,某个查询可能没有优化编写。你可以重新编写它,使它运行得更快。你要记住,在评估”慢查询”日志的时候,”慢”是根据实际时间测定的,在负载较大的服务器上”慢查询”日志中出现的查询会多一些。
*4.建索引的几大原则*
4.1.最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
4.2.=和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式
4.3.尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录
4.4. Indexspalten können nicht an Berechnungen teilnehmen. Wenn beispielsweise from_unixtime(create_time) = '2014-05-29' ist, kann der Index nicht verwendet werden Im B-Baum befinden sich die Feldwerte in der Datentabelle. Beim Abrufen müssen jedoch Funktionen auf alle zu vergleichenden Elemente angewendet werden, was offensichtlich zu kostspielig ist. Daher sollte die Anweisung als create_time = unix_timestamp('2014-05-29');
geschrieben werden4.5. Erweitern Sie den Index so weit wie möglich und erstellen Sie keinen neuen Index. Wenn beispielsweise bereits ein Index von a in der Tabelle vorhanden ist und Sie nun einen Index von (a, b) hinzufügen möchten, müssen Sie nur den ursprünglichen Index ändern.
Das Obige ist die Einführung des Herausgebers in die Verwendung von Indizes durch MySQL, um eine Abfrageoptimierung zu erreichen. Wenn Sie Fragen haben, hinterlassen Sie mir bitte eine Nachricht und der Herausgeber wird Ihnen rechtzeitig antworten. Vielen Dank für Ihre Unterstützung der Website!