索引是存储在一张表中特定列上的数据结构,索引是在列上创建的。并且,索引是一种数据结构。
在索引列上,除了有序查找之外,数据库利用各种各样的快速定位技术,能够大大提高查询效率。特别是当数据量非常大,查询涉及多个表时,使用索引往往能使查询速度加快成千上万倍。
主键索引:PRIMARY KEY,PRIMARY KEY索引和UNIQUE索引非常类似。事实上,PRIMARY KEY索引仅是一个具有名称PRIMARY的UNIQUE索引。这表示一个表只能包含一个PRIMARY KEY,因为一个表中不可能具有两个同名的索引。
唯一索引:UNIQUE,单列或多列
普通索引
复合索引
前缀索引
全局索引(FULLTEXT):全局索引,MyISAM 引擎支持全局索引, Innodb(MySQL 5.6以上支持),它的出现是为了解决针对文本的模糊查询效率较低的问题。
哈希索引(HASH):哈希索引是 MySQL 中用到的唯一 key-value 键值对的数据结构,很适合作为索引。HASH 索引具有一次定位的好处,不需要像树那样逐个节点查找,但是这种查找适合应用于查找单个键的情况,对于范围查找,HASH 索引的性能就会很低。
B-Tree 索引:B 就是 Balance 的意思,BTree 是一种平衡树,它有很多变种,最常见的就是 B+ Tree,它被 MySQL 广泛使用。Mysql中主要的索引类型。
R-Tree 索引:R-Tree 在 MySQL 很少使用,仅支持 geometry 数据类型,支持该类型的存储引擎只有MyISAM、BDb、InnoDb、NDb、Archive几种,相对于 B-Tree 来说,R-Tree 的优势在于范围查找。
表的主键、外键必须有索引;
数据量超过300的表应该有索引;
经常与其他表进行连接的表,在连接字段上应该建立索引
经常出现在Where子句中的字段,特别是大表的字段,应该建立索引;
索引的字段必须是经常作为查询条件的字段;
索引应该建在小字段上,对于大的文本字段甚至超长字段,不要建索引,实在要建的话可以用前缀索引;
复合索引的建立需要进行仔细分析;尽量考虑用单字段索引代替:复合索引在处理包含索引中所有字段的查询时效果最佳,但如果查询经常只使用其中一部分字段,那么单字段索引可能更有效。
正确选择复合索引中的主列字段,一般是选择性较好的字段;
复合索引的几个字段是否经常同时以AND方式出现在Where子句中?单字段查询是否极少甚至没有?如果是,则可以建立复合索引;否则考虑单字段索引;
如果复合索引中包含的字段经常单独出现在Where子句中,则分解为多个单字段索引;
如果复合索引所包含的字段超过3个,那么仔细考虑其必要性,考虑减少复合的字段;
如果索引多个字段,第一个字段是要经常作为查询条件的。如果只有第二个字段作为查询条件,这个索引不会起到作用;
频繁进行数据插入、更新操作的表,不要建立太多的索引;
删除无用的索引,避免对执行计划造成负面影响;
不要过度索引。不要以为 索引 “ 越多越好 ” ,什么东西都用索引是错的。每个额外的 索引都要占用额外的磁盘空间,并降低写操作的性能。如果想给已索引的表增加索引,应该考虑所要增加的索引是否是现有多列索引的最左索引。如果是,则就不要费力去增加这个索引了,因为已经有了。
考虑在列上进行的比较类型。索引可用于 “ < ” 、 “ <= ” 、 “ = ” 、 “ >= ” 、 “ >” 、"IN()" 和 BETWEEN 运算。在模式具有一个直接量前缀时(第一个字符不是通配符的情况下),索引也用于 LIKE 运算(xxx%), LIKE %abc% 不会走索引。
"!=" 、 "<>"、"not in" 在主键上会使用索引但在普通字段上不一定会走索引,尽量避免在 where 子句中使用这几种操作符。
对于值唯一不重复的列要添加唯一索引,可以更快速的通过该索引来确定某条记录。唯一索引是最有效的。
WHERE字句的查询条件里使用了函数(如:WHERE DAY(column)=...),MYSQL将无法使用索引;
查询语句中索引的列如果是表达式的一部分(如where num/2=100)或者是函数的参数,则索引失效。要在代码中计算好字段值带入到SQL语句中,不要在SQL语句中计算。
如果某个数据列里包含着许多重复的值,就算为它建立了索引也不会有很好的效果。比如说,如果某个数据列里包含了净是些诸如“0/1”或“Y/N”等值,就没有必要为它创建一个索引。
索引对于插入、删除、更新操作是会增加处理上的额外开销的。
重复索引没必要,如(A,B)和(A)重复。
索引在where条件查询和group by语法查询的时候特别有效。
将范围查询放在条件查询的最后,防止范围查询导致的右边索引失效的问题。
索引列不要出现null。
对于索引,若表字段类型为 varchar 用 int 取查询则整个语句都不会使用索引;反之 int 类型当做 varchar 类型去查询,索引是有效的。
建立索引index(part1,part2,part3),相当于建立了 index(part1),index(part1,part2)和index(part1,part2,part3)三个索引。
实例:
CREATE TABLE log_day_url_body (
id int(11) NOT NULL AUTO_INCREMENT,
body varchar(255) NOT NULL DEFAULT '',
p_id int(11) NOT NULL DEFAULT '0',
plt_id int(3) NOT NULL DEFAULT '1',
PRIMARY KEY (id),
KEY body (body),
KEY categories (body,plt_id,p_id),
UNIQUE KEY url (id,plt_id,p_id),
UNIQUE KEY url_3 (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='注释语句'
CREATE INDEX可对表增加普通索引或UNIQUE索引。
CREATE INDEX index_name ON table_name (column_list)
CREATE UNIQUE INDEX index_name ON table_name (column_list)
table_name、index_name和column_list具有与ALTER TABLE语句中相同的含义,索引名不可选。另外,不能用CREATE INDEX语句创建PRIMARY KEY索引。
ALTER TABLE用来创建普通索引、UNIQUE索引或PRIMARY KEY索引。
ALTER TABLE table_name ADD INDEX index_name (column_list)
ALTER TABLE table_name ADD UNIQUE (column_list)
ALTER TABLE table_name ADD PRIMARY KEY (column_list)
#添加FULLTEXT(全文索引)
ALTER TABLE table_name ADD FULLTEXT ( column)
ALTER TABLE table_name ADD INDEX index_name ( column1, column2, column3 )
column_list指出对哪些列进行索引,多列时各列之间用逗号分隔。索引名index_name可选,缺省时,MySQL将根据第一个索引列赋一个名称。另外,ALTER TABLE允许在单个语句中更改多个表,因此可以在同时创建多个索引。
可利用ALTER TABLE或DROP INDEX语句来删除索引。DROP INDEX可以在ALTER TABLE内部作为一条语句处理,语法如下。
DROP INDEX index_name ON talbe_name
ALTER TABLE table_name DROP INDEX index_name
ALTER TABLE table_name DROP PRIMARY KEY
其中,前两条语句是等价的,删除掉table_name中的索引index_name。
第3条语句只在删除PRIMARY KEY索引时使用,因为一个表只可能有一个PRIMARY KEY索引,因此不需要指定索引名。
如果没有创建PRIMARY KEY索引,但表具有一个或多个UNIQUE索引,则MySQL将删除第一个UNIQUE索引。
如果从表中删除了某列,则索引会受到影响。对于多列组合的索引,如果删除其中的某列,则该列也会从索引中删除。
如果删除组成索引的所有列,则整个索引将被删除。
show index from tblname;
'或'
show keys from tblname;
查询结果列:
*************************** 1. row ***************************
Table: gift # 表的名称
Non_unique: 0 # 如果索引不能包括重复词,则为0。如果可以,则为1。
Key_name: PRIMARY # 索引的名称
Seq_in_index: 1 # 索引中的列序列号,从1开始。
Column_name: id # 列名称。
Collation: A # 列以什么方式存储在索引中。在MySQL中,有值‘A’(升序)或NULL(无分类)。
Cardinality: 258 # 索引中唯一值的数目的估计值。通过运行ANALYZE TABLE或myisamchk -a可以更新。
# 基数根据被存储为整数的统计数据来计数,所以即使对于小型表,该值也没有必要是精确的。
# 基数越大,当进行联合时,MySQL使用该索引的机会就越大。
Sub_part: NULL # 如果列只是被部分地编入索引,则为被编入索引的字符的数目。
# 如果整列被编入索引,则为NULL。
Packed: NULL # 指示关键字如何被压缩。如果没有被压缩,则为NULL。
Null: # 如果列含有NULL,则含有YES。如果没有,则该列含有NO。
Index_type: BTREE # 用过的索引方法(BTREE, FULLTEXT, HASH, RTREE)。
Comment: # 一些关于索引的常用语句
Index_comment:
Visible: YES
Expression: NULL
*************************** 2. row ***************************
Table: gift
Non_unique: 1
Key_name: gift_group_id_status_index
Seq_in_index: 1
Column_name: group_id
Collation: A
Cardinality: 7
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
Visible: YES
Expression: NULL
2 rows in set (0.01 sec)
在MySQL中,一条查询SQL语句中使用多个索引的情况主要通过“索引合并”(Index Merge)技术实现。索引合并是指MySQL查询优化器在处理复杂的查询条件时,会选择使用多个索引来优化查询性能。这种技术允许MySQL将来自不同索引的结果集合并起来,从而减少需要扫描的数据量。
假设有一个表 employees
,该表有两个字段 department_id
和 salary
,并且在这两个字段上分别创建了索引。如果执行如下查询:
SELECT * FROM employees WHERE department_id = 10 OR salary > 50000;
在这个例子中,MySQL可能不会选择单一的索引来满足整个查询条件,而是选择使用两个索引:
一个索引用于查找 department_id = 10
的记录。
另一个索引用于查找 salary > 50000
的记录。
然后,MySQL会将这两个索引的结果集进行合并,最终得到符合查询条件的所有记录。
绝大多数情况下一条SQL查询语句都是使用一个最优索引,对于上面 or 语句可以理解为执行了两条语句,分别用自己的索引然后再合并
SELECT * FROM employees WHERE department_id = 10;
SELECT * FROM employees WHERE salary > 50000;
你可以使用 EXPLAIN
命令来查看查询的执行计划,了解MySQL是否使用了索引合并。例如:
EXPLAIN SELECT * FROM employees WHERE department_id = 10 OR salary > 50000;
在 EXPLAIN
输出中,如果你看到 type
列显示为 index_merge
,则表示MySQL使用了索引合并技术。
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: employees
partitions: NULL
type: index_merge
possible_keys: idx_department_id,idx_salary
key: idx_department_id,idx_salary
key_len: 8,766
ref: NULL
rows: 357
filtered: 100.00
Extra: Using union(idx_department_id,idx_salary); Using where
1 row in set, 1 warning (0.00 sec)
在这个输出中:
type: index_merge
表示使用了索引合并。
possible_keys
列出了可能使用的索引。
key
列显示了实际使用的索引。
Extra
列中的 Using union(idx_department_id,idx_salary)
表示MySQL使用了这两个索引的结果集的并集。
在 MySQL 中,创建前缀索引可以帮助提高索引的性能,特别是在处理长字符串列(BLOB/TEXT类型的字段)时。前缀索引只对列的前几个字符建立索引,而不是整个字符串,这样可以减少索引的大小,提高查询性能。
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
INDEX idx_username_prefix (username(10)) -- 创建前缀索引,前缀长度为10
);
# 或
ALTER TABLE users ADD INDEX idx_username_prefix (username(10));
在这个例子中,idx_username_prefix
是索引的名称,username(10)
表示只对 username
列的前 10 个字符建立索引。
前缀长度应根据实际数据的分布情况来选择。一般来说,前缀长度应足以区分大部分不同的值,但又不至于太长,以免浪费空间。
可以通过分析数据来确定合适的前缀长度。例如,可以使用 SUBSTRING
函数来统计前缀的唯一性
SELECT SUBSTRING(username, 1, 10), COUNT(*) FROM users
GROUP BY SUBSTRING(username, 1, 10)
ORDER BY COUNT(*) DESC;
前缀索引的有效性取决于前缀的区分度。如果前缀长度过短,可能会导致很多不同的值在前缀上相同,从而降低索引的效果。
如果前缀长度过长,可能会导致索引变得过大,影响性能。
explain select * from table where id=1;
EXPLAIN SELECT * FROM users WHERE id=6;
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | NULL |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
1 row in set (0.03 sec)
下面我们就来看看这个EXPLAIN分析结果的含义:
table:这是表的名字。
type:连接操作的类型。
主要包含如下几中类型:
◇ all:全表扫描
◇ const:读常量,且最多只会有一条记录匹配,由于是常量,所以实际上只需要读一次;
◇ eq_ref:最多只会有一条匹配结果,一般是通过主键或者唯一键索引来访问;
◇ fulltext:
◇ index:全索引扫描;
◇ index_merge:查询中同时使用两个(或更多)索引,然后对索引结果进行merge 之后再读
取表数据;
◇ index_subquery:子查询中的返回结果字段组合是一个索引(或索引组合),但不是一个
主键或者唯一索引;
◇ rang:索引范围扫描;
◇ ref:Join 语句中被驱动表索引引用查询;
◇ ref_or_null:与ref 的唯一区别就是在使用索引引用查询之外再增加一个空值的查询;
◇ system:系统表,表中只有一行数据;
◇ unique_subquery:子查询中的返回结果字段组合是主键或者唯一约束;
从好到差 system,const,eq_ref,ref,range,index,ALL
possible_keys:可能可以利用的索引的名字。
Key:它显示了MySQL实际使用的索引的名字。如果它为空(或NULL),则MySQL不使用索引。
key_len:索引中被使用部分的长度,以字节计。
ref:它显示的是列的名字(或单词“const”),MySQL将根据这些列来选择行。
rows:MySQL所认为的它在找到正确的结果之前必须扫描的记录数。显然,这里最理想的数字就是1。
Extra:这里可能出现许多不同的选项,其中大多数将对查询产生负面影响。
ID:Query Optimizer 所选定的执行计划中查询的序列号;
Select_type:所使用的查询类型