所有MySQL列类型可以被索引。根据存储引擎定义每个表的最大索引数和最大索引长度。
所有存储引擎支持每个表至少16个索引,总索引长度至少为256字节。大多数存储引擎有更高的限制。
索引的存储类型目前只有两种(btree和hash),具体和存储引擎模式相关:
MyISAM btree
InnoDB btree
MEMORY/Heap hash,btree
默认情况MEMORY/Heap存储引擎使用hash索引
MySQL的btree索引和hash索引的区别
hash 索引结构的特殊性,其检索效率非常高,索引的检索可以一次定位,不像btree(B-Tree)索引需要从根节点到枝节点,最后才能访问到页节点这样多次的IO访问,所以 hash 索引的查询效率要远高于 btree(B-Tree) 索引。
虽然 hash 索引效率高,但是 hash 索引本身由于其特殊性也带来了很多限制和弊端,主要有以下这些。
(1)hash 索引仅仅能满足=,<=>,IN,IS NULL或者IS NOT NULL查询,不能使用范围查询。
由于 hash 索引比较的是进行 hash 运算之后的 hash 值,所以它只能用于等值的过滤,不能用于基于范围的过滤,因为经过相应的 hash 算法处理之后的 hash 值的大小关系,并不能保证和hash运算前完全一样。
(2)hash 索引无法被用来避免数据的排序操作。
由于 hash 索引中存放的是经过 hash 计算之后的 hash 值,而且hash值的大小关系并不一定和 hash 运算前的键值完全一样,所以数据库无法利用索引的数据来避免任何排序运算;
(3)hash 索引不能利用部分索引键查询。
对于组合索引,hash 索引在计算 hash 值的时候是组合索引键合并后再一起计算 hash 值,而不是单独计算 hash 值,所以通过组合索引的前面一个或几个索引键进行查询的时候,hash 索引也无法被利用。
(4)hash 索引在任何时候都不能避免表扫描。
前面已经知道,hash 索引是将索引键通过 hash 运算之后,将 hash运算结果的 hash 值和所对应的行指针信息存放于一个 hash 表中,由于不同索引键存在相同 hash 值,所以即使取满足某个 hash 键值的数据的记录条数,也无法从 hash 索引中直接完成查询,还是要通过访问表中的实际数据进行相应的比较,并得到相应的结果。
(5)hash 索引遇到大量hash值相等的情况后性能并不一定就会比B-Tree索引高。
对于选择性比较低的索引键,如果创建 hash 索引,那么将会存在大量记录指针信息存于同一个 hash 值相关联。这样要定位某一条记录时就会非常麻烦,会浪费多次表数据的访问,而造成整体性能低下
B-Tree 索引是 MySQL 数据库中使用最为频繁的索引类型,除了 Archive 存储引擎之外的其他所有的存储引擎都支持 B-Tree 索引。不仅仅在 MySQL 中是如此,实际上在其他的很多数据库管理系统中B-Tree 索引也同样是作为最主要的索引类型,这主要是因为 B-Tree 索引的存储结构在数据库的数据检 索中有非常优异的表现。
一般来说, MySQL 中的 B-Tree 索引的物理文件大多都是以 Balance Tree 的结构来存储的,也就是所有实际需要的数据都存放于 Tree 的 Leaf Node ,而且到任何一个 Leaf Node 的最短路径的长度都是完全相同的,所以我们大家都称之为 B-Tree 索引当然,可能各种数据库(或 MySQL 的各种存储引擎)在存放自己的 B-Tree 索引的时候会对存储结构稍作改造。
如 Innodb 存储引擎的 B-Tree 索引实际使用的存储结构实际上是 B+Tree ,也就是在 B-Tree 数据结构的基础上做了很小的改造,在每一个Leaf Node 上面出了存放索引键的相关信息之外,还存储了指向与该 Leaf Node 相邻的后一个 LeafNode 的指针信息,这主要是为了加快检索多个相邻 Leaf Node 的效率考虑。
在 Innodb 存储引擎中,存在两种不同形式的索引,一种是 Cluster 形式的主键索引( Primary Key ),另外一种则是和其他存储引擎(如 MyISAM 存储引擎)存放形式基本相同的普通 B-Tree 索引,这种索引在 Innodb 存储引擎中被称为 Secondary Index 。
在 Innodb 中如果通过主键来访问数据效率是非常高的,而如果是通过 Secondary Index 来访问数据的话, Innodb 首先通过 Secondary Index 的相关信息,通过相应的索引键检索到 Leaf Node之后,需要再通过 Leaf Node 中存放的主键值再通过主键索引来获取相应的数据行。
MyISAM 存储引擎的主键索引和非主键索引差别很小,只不过是主键索引的索引键是一个唯一且非空 的键而已。而且 MyISAM 存储引擎的索引和 Innodb 的 Secondary Index 的存储结构也基本相同,主要的区别只是 MyISAM 存储引擎在 Leaf Nodes 上面出了存放索引键信息之外,
再存放能直接定位到 MyISAM 数据文件中相应的数据行的信息(如 Row Number ),但并不会存放主键的键值信息。
我们这里建表
*/CREATE TABLE mytable(
id INT,
username VARCHAR(16),
city VARCHAR(16),
age INT
);
/*
索引分单列索引和组合索引。单列索引,即一个索引只包含单个列,一个表可以有多个单列索引,但这不是组合索引。组合索引,即一个索包含多个列。
MySQL索引类型包括:
(1)普通索引,这是最基本的索引,它没有任何限制。它有以下几种创建方式:
*/-- 创建索引CREATE INDEX indexName ON mytable(username(10)); -- 单列索引 一般是按照单个条件
-- CREATE INDEX indexName ON mytable(username(10),city(10)); -- 组合索引 一般按照多个条件顺序
-- indexName为索引名,mytable表名,username和city为列名,10为前缀长度,即索引在该列从最左字符开始存储的信息长度,单位字节
-- 如果是CHAR,VARCHAR类型,前缀长度可以小于字段实际长度;如果是BLOB和TEXT类型,必须指定 前缀长度,下同。-- 修改表结构来创建索引ALTER TABLE mytable ADD INDEX indexName (username(10));
-- ALTER TABLE mytable ADD INDEX indexName (username(10),city(10));
-- 此处 indexName 索引名可不写,系统自动赋名 username ,username_2 ,username_3,...-- 创建表的时候直接指定CREATE TABLE mytable(
id INT,
username VARCHAR(16),
city VARCHAR(16),
age INT,
INDEX indexName (username(10))-- INDEX indexName (username(10),city(10))
);
-- 此处 indexName 索引名同样可以省略/*
(2)唯一索引,它与前面的普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。它有以下几种创建方式(仅仅在创建普通索引时关键字 INDEX 前加 UNIQUE):
*/-- 创建索引CREATE UNIQUE INDEX indexName ON mytable(username(10));
-- 修改表结构来创建索引ALTER TABLE mytable ADD UNIQUE INDEX indexName (username(10));-- 也可简写成 ALTER TABLE mytable ADD UNIQUE indexName (username(10));
-- 创建表的时候直接指定CREATE TABLE mytable(
id INT,
username VARCHAR(16),
city VARCHAR(16),
age INT,
UNIQUE INDEX indexName (username(10)) -- 也可简写成 UNIQUE indexName (username(10))
);
/*
(3)主键索引,它是一种特殊的唯一索引,不允许有空值。在建表的时候同时创建的主键即为主键索引
主键索引无需命名,一个表只能有一个主键。主键索引同时可是唯一索引或者全文索引,但唯一索引或全文索引不能共存在同一索引
*/-- 修改表结构来创建索引ALTER TABLE mytable ADD PRIMARY KEY (id);
-- 创建表的时候直接指定CREATE TABLE mytable(
id INT,
username VARCHAR(16),
city VARCHAR(16),
age INT,
PRIMARY KEY(id)
);
/*
(4)全文索引,InnoDB存储引擎不支持全文索引
*/-- 创建索引CREATE FULLTEXT INDEX indexName ON mytable(username(10));
-- 修改表结构来创建索引ALTER TABLE mytable ADD FULLTEXT INDEX indexName (username(10));-- 也可简写成 ALTER TABLE mytable ADD FULLTEXT indexName (username(10));
-- 创建表的时候直接指定CREATE TABLE mytable(
id INT,
username VARCHAR(16),
city VARCHAR(16),
age INT,
FULLTEXT INDEX indexName (username(10)) -- 也可简写成 FULLTEXT indexName (username(10))
)ENGINE=MYISAM;
-- 建表时创建全文索引,要设置该表的存储引擎为MYISAM,新版mysql默认InnoDB存储引擎不支持全文索引-- 删除索引DROP INDEX indexName ON mytable;
/*
Mysql自动使用索引规则:
btree索引
当使用 <,<=,=,>=,>,BETWEEN,IN,!=或者<>,以及某些时候的LIKE才会使用btree索引,因为在以通配符%和_开头作查询时,MySQL不会使用索引。btree索引能用于加速ORDER BY操作
hash索引
当使用=,<=>,IN,IS NULL或者IS NOT NULL操作符时才会使用hash索引,并且不能用于加速ORDER BY操作,并且条件值必须是索引列查找某行该列的整个值
对where后边条件为字符串的一定要加引号,字符串如果为数字mysql会自动转为字符串,但是不使用索引。
mysql目前不支持函数索引,只能对列的前一部分(指定长度前缀)进行索引,对于char和varchar列,使用前缀索引(该列从起始字符到指定长度字符位置建立索引)将大大节省空间。
mysql列建议列是非null的。说是如果是允许null的列,对索引会有影响(索引不会包括有NULL值)。因为它们使得索引、索引的统计信息以及比较运算更加复杂。应该用0、一个特殊的值或者一个空串代替空值。
尽量不使用NOT IN和<>操作
username,city,age建立这三列的组合索引,其实是相当于分别建立了下面三组组合索引:
username,city,age
username,city
username
使用组合索引,比如where等条件,列名必须从组合索引最左列至右连续的列名做条件才可以,组合索引类似单一索引前缀
*/-- 调用组合索引SELECT * FROM mytable WHERE username = 'admin' AND city = 'DaLian';
-- 多表联查中的条件也可运用索引-- 全文索引的使用SELECT * , MATCH (username,city) AGAINST ('name100 name200 city500 thisisname') FROM mytable WHERE MATCH (username,city) AGAINST ('name100 name200 city500 thisisname');
-- 返回 mytable 表在 MATCH 的参数中的任意列中包含 AGAINST 字符串参数中被 "空格" "," 和 "." 分割的任意单词的行(断字的字符: "空格" "," 和 "." 但是不用这些符号断字的语言,如中文,就得自行手动断字。)
-- 表列中的单词也是以空格分割来区分
-- 这里指定 MATCH...AGAINST 两次。这不会引起附加的开销,因为 MySQL 优化器会注意到两次是同样的 MATCH...AGAINST 调用,并只调用一次全文搜索代码。
/*
函数 MATCH() 对照一个列名集(一个 FULLTEXT 索引中的一个或多个列的列名)。搜索字符串做为 AGAINST() 的参数给定。搜索以忽略字母大小写的方式执行,预设搜寻是不分大小写,若要分大小写,列的字符集设置要从utf8改成utf8_bin。
虽然同一个表格可以有不同字符集的字段,但是同一个FULLTEXT 索引里的字段必须是同一个字符集与collation。
任何在 stopword 列表上出现的,或太短的(3 个字符或更少的)的单词将被忽略。(可以覆写内建的 stopword 列表。可以修改最少四个字符的设定。 )
搜索的词有一个权重性,如果搜索的词在表中包含它的行太多,则这个搜索词将有较低的权重(可能甚至有一个零权重),否则,它将得到一个较高的权重。然后,权重将被结合用于计算行的相似性。
如果搜索词在表中超过一半的行中出现。则它被有效地处理为一个 stopword (即,一个零语义值的词),不搜索该词。
MATCH...AGAINST可以跟所有MySQL语法搭配使用,像是JOIN或是加上其他过滤条件。
对于表中的每行记录,MATCH...AGAINST 返回一个相关性值。即,返回的每行与搜索条件之间的相似性尺度。
当 MATCH() 被使用在一个 WHERE 子句中时,返回的结果被自动地以相关性从高到底的次序排序。如果即没有 WHERE 也没有 ORDER BY 子句,返回行是不排序的。
相关性值是正值的浮点数字。零相关性意味着不相似。
相关性的计算是基于:查找单词在表行中的数目、在行中唯一词的数目、在集中词的全部数目和包含一个特殊词的行的数目。
到 4.0.1 时,MySQL 也可以使用 IN BOOLEAN MODE 修饰语来执行一个逻辑全文搜索。
IN BOOLEAN MODE的特色:
不剔除50%以上符合的row。
不自动以相关性反向排序。
可以对没有FULLTEXT 索引的字段进行搜寻,但会非常慢。
限制最长与最短的字符串。
套用stopwords。
逻辑全文搜索支持下面的操作符:
+ 一个领头的加号表示,返回的结果的每行都必须包含有该单词。
- 一个领头的减号表示,包含该单词的行不能出现在返回的结果中。
> 操作符增加包含该单词返回行相似性值的基值。
< 操作符减少包含该单词返回行相似性值的基值。
() 被括号包含的多个词只相当一个词,即在查询时括号里只有一个词可代表该括号与括号外的词相结合做查询,但括号中每个词都会依次被轮到代表该括号,所以与括号外单词会产生多种结合形式,依次做查询条件。
~ 将其相关性由正转负,表示拥有该字会降低相关性,但不像 - 将之排除,只是排在较后面。
* 一个星号是截断操作符,它应该被追加到一个词后,不加在前面。作用类似 LIKE 语句中的 %
"" 把被双引号包含的多个词作为一个词
这里是一些示例,在返回结果中:
1.+apple +juice ... 两个词均在被包含
2.+apple macintosh ... 包含词 “apple”,但是如果同时包含 “macintosh”,它的排列将更高一些
3.+apple -macintosh ... 包含 “apple” 但不包含 “macintosh”
4.+apple +(>pie <strudel) ... 包含 “apple” 和 “pie”,或者包含的是 “apple” 和 “strudel” (以任何次序),但是“apple pie” 排列得比 “apple strudel” 要高一点
5.apple* ... 包含 “apple”,“apples”,“applesauce” 和 “applet”
6."some words" ... 可以包含 “some words of wisdom”,但不是 “some noise words”
*/SELECT *,MATCH (username,city) AGAINST ('>>name300 +thisisname -city100' IN BOOLEAN MODE) FROM mytable WHERE MATCH (username,city) AGAINST ('>>name300 +thisisname -city100' IN BOOLEAN MODE);
/*
全文索引的限制
MATCH() 函数的所有参数必须是从来自于同一张表的列,同时必须是同一个FULLTEXT 索引中的一部分,除非 MATCH() 是 IN BOOLEAN MODE 的。
MATCH() 列必须确切地匹配表的某一 FULLTEXT 索引中定义的列,除非 MATCH() 是 IN BOOLEAN MODE 的。
AGAINST() 的参数必须是一个常量字符串。
MySQL全文搜寻设定:
大部分的参数都是启动参数,也就是修改后必须重新启动MySQL。
有些参数修改必须重新产生索引文件。
mysql> SHOW VARIABLES LIKE 'ft%';
ft_boolean_syntax + -><()~*:""&|
ft_min_word_len 4
ft_max_word_len 84
ft_query_expansion_limit 20 ft_stopword_file (built-in)
ft_min_word_len:最短的索引字符串,默认值为4,修改后必须重建索引文件。
ft_max_word_len:最长的索引字符串,默认值因版本而不同,余同上一点。
[mysqld]
ft_min_word_len=1
ft_stopword_file:stopword档案路径,若留空白不设定表示要停用stopword过滤,修改后必须重新启动MySQL和重建索引;stopword档案内容可以用分行空白与逗号区隔stopword,但底线和单引号视为合法的字符串字符。
50%的门坎限制:配置文件在storage/myisam/ftdefs.h,将 #define GWS_IN_USE GWS_PROB 改为 #define GWS_IN_USE GWS_FREQ,然后重新编译MySQL,因为近低门坎会影响数据的精准度,所以不建议如此,可用IN BOOLEAN MODE即可以避开50%的限制。
ft_boolean_syntax:改变IN BOOLEAN MODE的查询字符,不用重新启动MySQL也不用重建索引。
修改字符串字符的认定,譬如说将「-」认定为字符串的合法字符:
方法一:修改storage/myisam/ftdefs.h的true_word_char()与misc_word_char(),然后重新编译MySQL,最后重建索引。
方法二:修改字符集档,然后在FULLTEXT index的字段使用该字符集,最后重建索引。
重建索引:
每个有FULLTEXT index的表格都要这么做。
mysql> REPAIR TABLE tbl_name QUICK;
要注意如果用过myisamchk,会导致上述的设定值回复成默认值,因为myisamchk不是用MySQL的设定值。
解法一:将修改过得设定值加到myisamchk的参数里。
shell> myisamchk --recover --ft_min_word_len=1 tbl_name.MYI
解法二:两边都要设定。
[mysqld]
ft_min_word_len=1
[myisamchk]
ft_min_word_len=1
解法三:用REPAIR TABLE、ANALYZE TABLE、OPTIMIZE TABLE与ALTER TABLE取代myisamchk语法,因为这些语法是由MySQL执行的。
中文全文索引可以建两个表,一个表字段里存中文,一个表对应字段存汉语拼音,两表行必须对应,数据一致,插入时中文转化下汉语拼音,两表都插入
查询时也转化下,全文索引查汉语拼音,然后找到中文表对应行
或者使用mysqlcft中文全文索引插件
查看索引使用情况
如果索引正在工作,Handler_read_key的值将很高,这个值代表了一个行被索引值读的次数,很低的值表明增加索引得到的性能改善不高,因为索引并不经常使用。
Handler_read_rnd_next的值高则意味着查询运行低效,并且应该建立索引补救。这个值的含义是在数据文件中读下一行的请求数。如果你正进行大量的表扫描,该值较高。通常说明表索引不正确或写入的查询没有利用索引。
语法:SHOW STATUS LIKE 'Handler_read%';
MyISAM表的数据文件和索引文件是自动分开的
InnoDB的数据和索引是存储在同一个表空间里面,但可以有多个文件组成
虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件。
建立索引会占用磁盘空间的索引文件。一般情况这个问题不太严重,但如果你在一个大表上创建了多种组合索引,索引文件的会膨胀很快。