您的位置:首页 > 数据库 > MySQL

Mysql学习--索引(一)

2015-11-04 19:05 369 查看
推荐一篇博文,关于索引和引擎的:http://www.cnblogs.com/hustcat/archive/2009/10/28/1591648.html

对于mysql优化相关的信息,是对着韩顺平老师的mysql优化的视频来看的。现在把自己所了解的相关知识整理一下。

索引

一、索引的概念

索引是一种数据结构,是数据库中的对象,它用来快速地访问数据库中表或视图中的数据。索引对查询的速度有着至关重要的影响,理解索引也是进行数据库性能调优的起点。考虑如下情况,假设数据库中一个表有10^6条记录,DBMS的页面大小为4K,并存储100条记录。如果没有索引,查询将对整个表进行扫描,最坏的情况下,如果所有数据页都不在内存,需要读取10^4个页面,如果这10^4个页面在磁盘上随机分布,需要进行10^4次I/O,假设磁盘每次I/O时间为10ms(忽略数据传输时间),则总共需要100s(但实际上要好很多很多)。如果对之建立B-Tree索引,则只需要进行log100(10^6)=3次页面读取,最坏情况下耗时30ms。这就是索引带来的效果,很多时候,当你的应用程序进行SQL查询速度很慢时,应该想想是否可以建索引。

二、索引的分类

索引分为聚簇索引和非聚簇索引两种,聚簇索引是按照数据存放的物理位置为顺序的,即该索引中键值的逻辑顺序与表中相应行的物理顺序一致,一个表中只能包含一个聚集索引,而非聚簇索引就不一样了,该索引中的逻辑顺序与磁盘上行的物理存储顺序不同;聚簇索引能提高多行检索的速度,而非聚簇索引对于单行的检索很快。

三、选择索引的数据类型

MySQL支持很多数据类型,选择合适的数据类型存储数据对性能有很大的影响。通常来说,可以遵循以下一些指导原则:

a. 越小的数据类型通常更好:越小的数据类型通常在磁盘、内存和CPU缓存中都需要更少的空间,处理起来更快。

b. 简单的数据类型更好:整型数据比起字符,处理开销更小,因为字符串的比较更复杂。在MySQL中,应该用内置的日期和时间数据类型,而不是用字符串来存储时间;以及用整型数据类型存储IP地址。

c. 尽量避免NULL:应该指定列为NOT NULL,除非你想存储NULL。在MySQL中,含有空值的列很难进行查询优化,因为它们使得索引、索引的统计信息以及比较运算更加复杂。你应该用0、一个特殊的值或者一个空串代替空值。

另外选择合适的标识符是非常重要的。选择时不仅应该考虑存储类型,而且应该考虑MySQL是怎样进行运算和比较的。一旦选定数据类型,应该保证所有相关的表都使用相同的数据类型。

a. 整型:通常是作为标识符的最好选择,因为可以更快的处理,而且可以设置为AUTO_INCREMENT。

b. 字符串:尽量避免使用字符串作为标识符,它们消耗更好的空间,处理起来也较慢。而且,通常来说,字符串都是随机的,所以它们在索引中的位置也是随机的,这会导致页面分裂、随机访问磁盘,聚簇索引分裂(对于使用聚簇索引的存储引擎)。

 

四、索引的创建和添加

创建索引方式:

自动:当表上定义主键约束、唯一、外键约束时,该表会被系统自动添加上索引。

手动:手动在相关表或列上增加索引,提高查询速度。

Mysql中4中索引类型:

1.主键索引

建表时,将一个类设为主键,则该列就是主键索引(自动)。

① 创建表的时候同时创建主键索引

#创建表的时候同时创建索引:向atable中添加主键索引 id列(自动创建)



CREATE TABLE atable(
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT ,
name VARCHAR(32) NOT NULL DEFAULT '');




② 修改表结构方式添加主键索引

格式:ALTER TABLE 表名 ADD PRIMARY KEY(列名);

CREATE TABLE atable(
id INT UNSIGNED ,
name VARCHAR(32) NOT NULL DEFAULT ''
);
ALTER TABLE atable ADD PRIMARY KEY(id);


2.普通索引

① 创建表的时候同时指定普通索引

CREATE TABLE btable(
id INT NOT NULL AUTO_INCREMENT ,
title VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NU
4000
LL ,
content TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL ,
time DATETIME NOT NULL,
PRIMARY KEY (id),
INDEX index_name(title)
);



② 直接创建普通索引

格式:CREATE INDEX 索引名 ON 表名(列名);

DROP TABLE btable;
CREATE TABLE btable(
id INT NOT NULL AUTO_INCREMENT ,
title VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
content TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL ,
time DATETIME NOT NULL,
PRIMARY KEY (id)
);
CREATE INDEX index_name ON btable(title);


③ 修改表结构方式添加普通索引

格式:ALTER TABLE 表名 ADD INDEX 索引名(列名);

ALTER TABLE btable ADD INDEX myindex (title);


3.全文索引

全文索引主要是针对文本、文字的检索,文章居多,全文索引只对MySIAM引擎有用。

① 创建表的时候同时指定全文索引

CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200),
body TEXT,
FULLTEXT myindex  (title,body)
)engine=MyISAM CHARSET=utf8;


② 直接创建全文索引

格式: CREATE FULLTEXT INDEX 索引名 ON 表名(列名);

CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200),
body TEXT
)engine=MyISAM CHARSET=utf8;
CREATE FULLTEXT INDEX myindex ON articles (title,body);


③ 修改表结构方式添加全文索引

格式:ALTER TABLE 表名 ADD FULLTEXT INDEX 索引名(列名); 

ALTER TABLE articles ADD FULLTEXT INDEX myindex(title,body);


④ 使用全文索引查询

全文索引中有一个停止词的概念:在全文索引中,一些常用词和字符,就不会创建索引,这些词成为停止词。

查询文章中包含forever关键字的记录:

SELECT * FROM articles WHERE MATCH(title,body) AGAINST(‘forever’);

4.唯一索引

当表中某列被指定unique约束时,这列就是一个唯一索引列。

① 创建表的时候直接指定唯一索引

CREATE TABLE ctable(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(32) UNIQUE
);




CREATE TABLE ctable(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(32),
UNIQUE myindex(name)
);


② 直接创建唯一索引

格式: CREATE UNIQUE INDEX 索引名 ON 表名(列名);

CREATE TABLE ctable(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(32)
);
CREATE UNIQUE INDEX myindex ON ctable(name);


③ 修改表结构方式添加唯一索引

格式:ALTER TABLE 表名 ADD UNIQUE INDEX 索引名(列名);

ALTER TABLE ctable ADD UNIQUE INDEX myindex(name);


5.单列索引、多列索引

多个单列索引与单个多列索引的查询效果不同,因为执行查询时,MySQL只能使用一个索引,会从多个索引中选择一个限制最为严格的索引。

6.组合索引(最左前缀)

平时用的SQL查询语句一般都有比较多的限制条件,所以为了进一步榨取MySQL的效率,就要考虑建立组合索引。例如articles表中针对title和body建立一个组合索引:

CREATE FULLTEXT INDEX myindex ON articles (title,body);


建立这样的组合索引,其实是相当于分别建立了下面两组组合索引:

–title,body

–title

为什么没有body这样的组合索引呢?这是因为MySQL组合索引“最左前缀”的结果。简单的理解就是只从最左面的开始组合。并不是只要包含这两列的查询都会用到该组合索引,如下面的几个SQL所示:

使用到上面的索引:

SELECT * FROM articles WHREE title='database' AND body=’something’;
SELECT * FROM articles WHREE title='database' ;


不使用上面的索引:

SELECT * FROM articles WHREE body=’something’;


如何得知是否使用了索引?可以使用explain关键字分析的SQL语句的执行情况,explain 可以帮助我们在不真正执行某个sql语句时,就知道mysql怎样执行,这样有利于我们去分析sql指令:

会产生如下信息:

select_type:表示查询的类型。
table:输出结果集的表
type:表示表的连接类型
possible_keys:表示查询时,可能使用的索引
key:表示实际使用的索引
key_len:索引字段的长度
rows:扫描出的行数(估算的行数)
Extra:执行情况的描述和说明(例如排序等信息)

五、索引的查看和删除

1. 查看

SHOW INDEX FROM 表名;

SHOW INDEX FROM articles;


2. 删除

DROP INDEX 索引名 ON  表名;

DROP INDEX myindex ON articles;


ALTER TABLE 表名 DROP INDEX 索引名; 

ALTER TABLE articles DROP INDEX myindex;


六、索引的使用和优化

1. 索引的使用场景

a. 在哪些列上适合添加索引?

① 较频繁的作为查询条件字段应该创建索引

select * from user where id= 1;


② 唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件

<pre name="code" class="sql">select * from user where sex = ‘女’;




③ 更新非常频繁的字段不适合创建索引

select * from user where logincount = 1;


b. 不会出现在WHERE子句中字段不该创建索引

总结: 满足以下条件的字段,才应该创建索引:

肯定在where条件中经常使用 

该字段的内容不是唯一的几个值

字段内容不是频繁变化

2. 索引使用的注意事项

查询要使用索引最重要的条件是查询条件中需要使用索引。

下列几种情况下有可能使用到索引:

a. 对于创建的组合索引,只要查询条件使用了最左边的列(最左前缀),索引一般就会被使用。

b. 对于使用like的查询,查询如果是’%aaa’ 不会使用到索引(%、_不能在开头),’aaa%’会使用到索引。即在like查询时,’关键字’最前面,不能使用 % 或者 _这样的字符模糊匹配,如果一定要前面有模糊匹配的值,则考虑使用全文索引。

下列的表将不使用索引:

a. 如果条件中有or,即使其中有条件带索引也不会使用(必须条件中使用到的所有字段都建有索引,建议尽量避免使用or关键字)。

b. 对于组合索引,不是使用的第一部分,则不会使用索引。

c. like查询是以%或_开头

d. 如果列类型是字符串,那一定要在条件中将数据使用引号引用起来。否则不使用索引。(添加时,字符串必须’ ’)

EXPLAIN SELECT * FROM user WHERE NAME=’nihao’;


e. 如果mysql估计使用全表扫描要比使用索引快,则不使用索引。

f. 使用group by 分组查询,默认分组后还会排序,可能会降低速度。如果查询中包括group by但用户想要避免排序结果的消耗,则可以使用order by null禁止
b980
排序。

g. 有些情况下,可以使用连接来替代子查询。因为使用join,MySQL不需要在内存中创建临时表。

SELECT *  FROM dept, emp WHERE dept.deptno=emp.deptno;


使用join代替:

SELECT *  FROM dept  LEFT JOIN  emp ON dept.deptno=emp.deptno;


3. 查看索引的使用情况

show status like ‘Handler_read%’;


大家可以发现:
handler_read_key:这个值越高越好,越高表示使用索引查询到的次数。

handler_read_rnd_next:这个值越高,说明查询低效。

七、索引的优化

前面也提到了一些,现在补充总结一下。

过多的使用索引将会造成滥用。索引的代价:

a. 占用磁盘空间

b. 对增删改操作有影响,变慢

1. 何时使用聚集索引或非聚集索引?

动作描述

使用聚集索引

使用非聚集索引

列经常被分组排序

使用

使用

返回某范围内的数据

使用

不使用

一个或极少不同值

不使用

不使用

小数目的不同值

使用

不使用

大数目的不同值

不使用

使用

频繁更新的列

不使用

使用

外键列

使用

使用

主键列

使用

使用

频繁修改索引列

不使用

使用

2. 索引不会包含有NULL值的列

只要列中包含有NULL值都将不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此复合索引就是无效的。所以我们在数据库设计时不要让字段的默认值为NULL。

3. 使用短索引

对串列进行索引,如果可能应该指定一个前缀长度。例如,如果有一个CHAR(255)的列,如果在前10个或20个字符内,多数值是惟一的,那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省磁盘空间和I/O操作。

4. 索引列排序

MySQL查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。

5.  like语句操作

一般情况下不鼓励使用like操作,如果非使用不可,如何使用也是一个问题。like “%aaa%” 不会使用索引而like “aaa%”可以使用索引。

6. 不要在列上进行运算

例如:select * from users where YEAR(adddate)<2007,将在每个行上进行运算,这将导致索引失效而进行全表扫描,因此我们可以改成:

select * from users where adddate<’2007-01-01’。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  mysql 数据库