数据库index

Indexes(索引):html

    索引可使mysql快速的找到和检索一张包含百万甚至亿万数据的表中的一组纪录。若是你曾使用过mysql,无论是多久,你可能为了更轻快的获得查询的结果而使用过索引。你也可能发现mysql的索引有时候并不想你想像的那样工做。mysql

    对不少使用者来讲,索引就像黑色的艺术。有时候奇迹快速地工做,有时候却像是缓慢或者阻碍数据插入。程序员

    在本章中,咱们将介绍一些索引的概念和mysql提供的各类不一样索引。sql

Indexing Basics(索引基础):数据库

    理解mysql如何使用索引,最好首先理解索引的基础工做和特征。一旦你对索引的特征有基本的理解,你就可以更加合理的正确使用它们了。缓存

Index Concepts(索引的概念):安全

    要理解索引让mysql作什么,思考mysql是如何工做才获得查询的结果。想象电话本是一个含有California州大约35000000条电话的电话本集合。无序地记录在大脑里,考虑这样查询:服务器

select * from phone_book where last_name = 'Zawodny';ide

    没有任何类型的索引来咨询,数据库必须读在phone_book这张表里的全部记录,而后比较last_name这个字段是否与字符串"Zawodny"匹配上。固然了,这种方法是低效的。一旦电话的记录增长了,就会须要去找对应给予的记录。在计算机科学中,咱们称之为O(n)时间复杂度问题。性能

    然而给定的真实电话本,咱们都知道如何快速定位到名字是Zawodny的地方:翻到书的后面,以Z开头的地方。由于第二个单词是a,咱们知道全部匹配的会在名单列表以Z开头的未知附近。这个方法是基于数据排序的知识的。

    这是做弊,不是吗?并非。你能够快速找到Zawodnys的缘由是它们的姓被按字母排序了。固然了,如此简单的找到Zawodny是由于你知道ABC字母。。。

    多数教科书(好比这本)都会在数的背面提供索引。由于这些索引按顺序排列在相应的页码,使你常常快速的找到书中的术语和概念。想要知道数据库热拷贝在哪里有讨论吗?查看索引页久知道了。

    数据库索引也是相似的。书的做者或出版者将会在书中选择重要的概念和术语做为索引。你能够在数据库的表中选择特定的字段建立索引。用前面的例子,你会建立姓为索引来快速查找电话号码:

ALTER TABLE phone_book ADD INDEX (last_name)

    在这样作的过程当中,你会在phone_book表中向数据库要一个按姓排列的顺序列表。每一个名字都有本身匹配记录的位置--就像这本书后面索引中列的各个条目的页码。

    从数据库服务器的角度来看,当在执行一个查询时索引的存在能够从固定的结果中快速的删除可能的行。在没有任何索引时,MYSQL(好比一个数据库服务器)会检查表中的每一行数据。这不只仅是时间上的浪费,也会占用大量磁盘输入输出从而严重弄脏磁盘缓存。

    在真实世界中,不多能找到刚刚被排序和已经排序的动态的数据。书是一种特例,它们趋向于保持静止。

    由于数据库须要为索引值维护一个单独的列表,并在数据更新同时使它们保持更新。你千万不能但愿一个表中的全部字段都做为索引。索引是一种空间和时间的折中。在作每一个insert,update,delete查询时使你的大部分(不是所有)查询更快时,你将在磁盘空间和CPU上牺牲一些额外的空间。

    大部分数据库的说明书使用了术语索引和键可互换。说last_name是phone_book表中的一个键等同于last_name字段是phone_book表的索引。

Partial indexes(部分索引):

    索引是空间和性能的交换。可是有时候你并不但愿用空间来交换性能。幸好,MYSQL为你提供了不少关于经过索引控制空间的方法。当你有一张phone_book表,里面有二十亿数据。在last_name上增长一个索引将须要不少空间,若是每一个last_name占8位,你会发现这个数据的索引大约须要16G的空间。不管你要作什么,行指针都要对每条记录增长额外的4-8位。

    你能够只将前4位做为索引,而不是将整个last name做为索引。

ALERT TABLE phone_book ADD INDEX (last_name(4))

    当你这样作的时候,已经减小了索引所须要部分数据约一半的空间。这个折中的是MYSQL不能消除太多行使用这个索引。好比像下面这样的查询:

SELECT * FROM phone_book WHERE last_name = 'Smith';

    取出全部以Smit开头的字段,包括全部名字是Smith,Smitty等人。这个查询会在以后丢弃Smitty等不想关的行。

Multicolumn indexes(多列):

    像不少关系数据库引擎同样,MySQL容许你建立的索引由多列组成。

ALERT TABLE phone_book ADD INDEX (last_name, first_name)

    若是你常常在where条件中用简短或单一没有足够的种类的列但查询结果是全部列,这种索引能够提升查询速度。固然你也可使用部分索引减小占用空间:

ALERT TABLE phone_table ADD INDEX (last_name(4), first_name(4))

    在任一状况下,查找Josh Woodward的快速执行:

select * from phone_book where last_name = 'Woodward' and first_name = 'Josh'

    拥有last_name和first_name两个索引意味着MYSQL能够基于两个字段消除行。因而更大程度上减小须要考虑的行。毕竟,在电话本上姓氏以Wood开头的人远远多于姓氏以Wood开头而且名以Josh开头的人。

    在讨论多列索引时,你可能会看到单独的索引列被称为键部分或“部分键”。多列索引也被称为综合索引或混合索引。

    那么为何不直接建立两个索引,一个建在last_name上,另外一个建在first_name上?你也能够那么作,可是MYSQL不会同时使用它们。事实上,MYSQL在每次查询的时候只会在每张表中使用一个索引--除了UNIONs以外[3].这个事实足以说明MySQL对于每张表的每次查询将永远只使用一个索引。

    对于拆分索引上的first_name和last_name,MySQL会选择其中一个。这个选择是经过一个颇有根据的猜测获得结果的,即判断哪一个索引匹配到的行比较少。咱们之因此说这是一个颇有根据的猜测,是由于MySQL会对这个索引的踪影进行统计来支撑他以为哪一个索引更好的推断。固然,这个统计是归纳性的。虽然他们常常让MySQL作出明智的决定,可是若是你有很是团结的数据,MySQL可能会作出关于索引使用的次优选择。在某些地方若是索引的键是稀疏的(好比以X开头的名字)且其余地方是高度集中的(好比名字是Smith的以英语为母语的国家),咱们把这种数据称为次优数据。这是一个重要的点,咱们在书的稍后内容中将会提到。

Index order(索引的顺序):

    MySQL是如何在索引中对值排序的?若是你使用过其余RDBMS,你可能但愿MySQL对索引有按升序、降序或其余顺序排序的指定句法。MySQL提供了一个内部没有控制排序方法的索引值。这是有缘由的,在4.0版本中,这个特性很好的优化了致使其余数据库性能下降的问题。

    例如,有些数据库可能会执行这个快速查询:

SELECT * FROM phone_book WHERE last_name = 'Zawodny' ORDER BY first_name DESC

    而后这个查询变慢:

SELECT * FROM phone_book WHERE last_name = 'Zawodny' ORDER BY first_name ASC

    为什呢?由于有些数据库按降序排序存储索引,并对按这个顺序读取索引进行了优化。在第一个例子中,数据库使用了多列索引定位到全部匹配的记录,由于这些记录已经按降序被存储了,已经没有必要再对他们进行排序了。可是在第二个例子中,服务器找到全部匹配的记录,而后对这些行执行第二遍,以对它们进行排序。

    在必要时,MySQL可以向后遍历索引。这也能使查询变得很是快,他能实如今任何状况下都不须要对记录进行排序。

Indexes as constraints(索引做为约束):

    索引并不全是用于查询定位匹配行的。惟一索引指定特定值只能在给定列中出现一次。在电话本的例子中,你能够对电话号码建立一个惟一索引,以确保每一个电话号码只出现一次。

ALERT TABLE phone_book ADD UNIQUE (phone_number)

    惟一索引具备双重目的,当你根据一个电话号查询的时候他的功能和其余索引同样。

SELECT * FROM phone_book WHERE phone_number = '555-7271'

    不过,他在插入和更新每条记录的同时还能检查以确保这条值是否已经存在。惟一索引以这种方式进行约束。

    惟一索引和非惟一索引使用的空间同样多。记录每一个字段的值和位置。若是使用惟一索引做为约束而从不做为索引那就是一种浪费。换句话说,你能够依靠惟一索引强制执行惟一性,可是不要写使用惟一键的查询。在这个例子中,MySQL没有必要将每一个记录的位置存储在索引中:由于你永远不会使用他们。

    不幸的,没有办法向MySQL表示你的意图。将来,咱们将特别介绍这个具体的例子。MyISAM存储引擎已经支持了不带索引的惟一字段(它使用基于散列的系统),可是该机制还没有在SQL级别公开。

Clustered and secondary indexes(集群和二级索引):

    使用MyISAM表,索引保持在一个彻底隔离的文件中,包含了主键(也可能有二级键)列表和表示记录的字节偏移量的值。这些确保了MySQL能够找到而且快速跳到在数据库中的那个点来查找记录。MySQL必须用这种方法存储索引,由于记录基本上是以乱序进行存储的。

    集群索引,主键和记录自己汇集在一块儿,而且记录都以主键顺序存储。InnoDB使用汇集的索引。在Oracle世界,汇集的索引被称为索引组织表。它将帮助你记录主键和行顺序的关系。

    当你的数据几乎都经过他的主键查找时,汇集的索引可使查找快得难以置信。使用标准MyISAM索引,有两个查找,一个到索引,另外一个到表自身,经过索引指定得位置。使用汇集的索引,有一个直接指向相关记录的查找。

    有些操做使用集群索引是低效的。例如,当使用一个二级索引时,回到电话本的例子,当你须要将last_name做为主索引,phone_number做为二级索引,你作如下查询:

SELECT * FROM phone_book WHERE phone_number = '555-7271'

    MySQL遍历了phone_number索引去查找555-7271项,包含了主键项Zawodny,由于电话本的主键是last name,MySQL会在数据库自身中跳到相关项。

    换句话说,基于主键的查询将会很是快,基于二级索引的查找速度基本上和MyISAM索引是一致的。

    可是在正确(或者说错误)的状况下,汇集索引事实上可能会下降性能。当你和二级索引一块儿使用时,你必须考虑这种结合对存储的影响。二级索引指向主键而不是特定的行。因而,若是你在一个很大的值上设索引,而且同时有一些二级索引,你将最终得到不少主索引的许多重复副本,首先做为与记录一块儿存储的汇集索引,而后再次以相同的次数与次级索引指向这些汇集索引。使用小的值做为主键,可能就不会这么差了,可是若是你使用某些个别数据将特别长的东西做为索引,例如URL,主键在磁盘上的重复存储将会致使存储问题。

    另外一个不常见的可是一样会发生问题的条件是:当数据在主键插入时主键在记录中被修改。这是汇集索引最昂贵的功能。一些事情的发生可使这样的操做有更多的性能冲击。

根据查询的结果在问题中插入记录

基于插入数据的记录,为记录决定新的主键

搬迁存储的记录一次这个问题中的记录将在表空间中被移动到正确的位置

更新指向主键的全部二级索引

    你可能在想,若是你为一些记录插入了主键,在执行UPDATE命令时将会花费至关一部分时间,特别是在更大的表中。更明智地选择主键。尽可能使用几乎不会改变的值,例如使用社会安全帐号而不是姓,使用序列号而不是产品名等等。

Unique indexes versus primary keys(惟一索引与主键):

    若是你用过其余关系型数据库,你可能会好奇在MySQL中主键和惟一索引之间有什么不一样。一般这取决于,在MyISAM 表这并无什么区别。主键中惟一不一样的是它的值不能是NULL的。主键只是一个不为空的惟一索引(NOT NULL UNIQUE INDEX)命名为键(PRIMARY)。MyISAM表不要求你声明一个主键。

    InnoDB和BDB要求每一个表都有主键,不过没有要求你指定其中某一个。若是你不指定,存储引擎将自动为你建立一个隐藏的主键。在这两种例子下,主键只是简单的增长数值,相似于一个自动增长列。若是你稍后要加一个主键,只须要使用ALTER TABLE添加一个。两个存储引擎将会丢弃它们本身内部的密钥去使用你的密钥。堆表不要求一个主键可是也会为你建立一个。事实上你能够建立一个无锁的堆表。

Indexing NULLs:

    SQL在执行逻辑操做时使用三态逻辑是很难记住的。除非一个字段声明了NOT NUL,有三种可能的结果在逻辑比较中。这个比较可能因为它们的值是等价的因此结果是对的;也有可能因为它们的值是不等价的因此结果是错误的;又或者它会由于值是NULL而不会匹配。不管什么时候比较的一个值是NULL那么结果也是NULL。

    程序员常常认为NULL就至关于没有被定义或未知的。这是一种告诉数据库服务器“一个未知的数据来了”的方式。因此NULL值的数据怎么能影响索引呢?

    NULL值将会使用常规索引(即非惟一索引),全部数据库服务器都是这样。然而,不像狠多数据库服务器,MySQL容许你在惟一索引上使用NULL值[6]。你能够在惟一索引上存储你想存储尽量多的NULL值。这看起来感受有点违反直觉,但这就是NULL的本质。由于NULL表明了一个没有被定义的值。若是它在惟一索引只容许单个值,MySQL须要宣称全部的NULL值都是同样的。

    去作一些更有意思的事,一个NULL值在主键中可能值出现一次。为何呢?由于SQL标准主宰了这一行为。这是在MySQL少数方法中惟一不一样于惟一键的主键。并且若是你想知道,在索引中容许NULL值并不会影响性能。


[1]这个有点误差。数据库不仅是存储匹配记录的位置,咱们很快久知道为何。

[2]这也是一种过分简化,MySQL使用一些策略来减小索引的大小,同时付出一些代价。

[3]在UNION中,每一个逻辑是单独运行而后进行合并的。

[4]除了NULL,固然了,NULL老是特殊的状况。

[5]然而在现实世界中,这或许是一个不好的例子,任何人均可以和几个室友共用一个电话和你打电话。

[6]MySQL版本3.23及之前的版本不支持这个功能,4.0及之后的版本支持。

原文来自:https://www.safaribooksonline.com/library/view/high-performance-mysql/0596003064/ch04.html#ftn.hpmysql-CHP-4-FNOTE-4


另外的问题:

1.多列索引如:ALERT TABLE phone_table ADD INDEX (last_name(4), first_name(4)),下面各类状况都可以利用这个多列索引进行快速查找吗

(a)select * from phone_book where last_name = 'Woodward' and first_name = 'Josh';

此时这个查询能够利用多列索引进行快速查找。

(b)select * from phone_book where last_name = 'Woodward';

此时这个查询能够利用给出的多列索引进行快速查找

(c)select * from phone_book where first_name = 'Josh';

此时该查询不能利用给出的多列索引进行快速查询,由于这个多列索引是根据last_name找到它对应的first_name的。

2.数据库的索引是怎么工做的。。。这个问题就比较复杂啦,目前只了解了个大概,我须要时间研究一下

相关文章
相关标签/搜索