Mysql索引

SQL性能下降的原因

  • 查询语句写的差。
  • 索引失效:索引建了,但是没有用上。
  • 关联 查询太多join(设计缺陷或者不得已的需求)。
  • 服务器调优以及各个参数的设置(缓冲、线程数等)。

SQL执行顺序

select              # 5
	... 
from                # 1
	... 
where               # 2
	.... 
group by            # 3
	... 
having              # 4
	... 
order by            # 6
	... 
limit               # 7
	[offset]

索引的简介

  • MySQL官方对索引的定义为:索引(INDEX)是帮助MySQL高效获取数据的数据结构。
  • 从而可以获得索引的本质:索引是排好序的快速查找数据结构。
  • 重点:索引会影响到MySQL查找(WHERE的查询条件)和排序(ORDER BY)两大功能!
  • 除了数据本身之外,数据库还维护着一个满足特定查找算法的数据结构,这些数据结构以某种方式指向数据,这样就可以在这些数据结构的基础上实现高级查找算法,这种数据结构就是索引。

二叉树弊端之一:二叉树很可能会发生两边不平衡的情况。
B-TREE: (B:balance) 会自动根据两边的情况自动调节,使两端无限趋近于平衡状态。可以使性能最稳定。(myisam使用的方式)
B-TREE弊端:(插入/修改操作多时,B-TREE会不断调整平衡,消耗性能)从侧面说明了索引不是越多越好。
B+TREE:Innodb 所使用的索引
一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储在磁盘上。
我们平常所说的索引,如果没有特别指明,都是指B树(多路搜索树,并不一定是二叉的)结构组织的索引。
其中聚集索引,次要索引,覆盖索引,复合索引,前缀索引,唯一索引默认都是使用B+树索引,统称索引。
当然,除了B+树这种类型的索引之外,还有哈稀索引(hashindex)等。

索引的优势和劣势

优势

  • 查找:类似大学图书馆的书目索引,提高数据检索的效率,降低数据库的IO成本。
  • 排序:通过索引対数据进行排序,降低数据排序的成本,降低了CPU的消耗。

劣势

  • 实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占用空间的。
  • 虽然索引大大提高了查询速度,但是同时会降低表的更新速度,例如对表频繁的进行INSERT、UPDATE和DELETE。因为更新表的时候,MySQL不仅要保存数据,还要保存一下索引文件每次更新添加的索引列的字段,都会调整因为更新所带来的键值变化后的索引信息。
  • 索引只是提高效率的一个因素,如果MySQL有大数据量的表,就需要花时间研究建立最优秀的索引。

mysql索引结构

BTree索引

  • Myisam普通索引
  • 原理图
    在这里插入图片描述
    【初始化介绍】
    一颗b树,浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示),
    如磁盘块1包含数据项17和35,包含指针P1、P2、P3,
    P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。
    真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。
    非叶子节点不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中。

    【查找过程】
    如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,
    在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,内存时间因为非常短(相比磁盘的IO)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,
    29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找找到29,结束查询,总计三次IO。

    真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常非常高。

B+Tree索引

  • innodb的普通索引

  • 原理图
    在这里插入图片描述

  • B+TREE 第二级的 数据并不能直接取出来,只作索引使用。在内存有限的情况下,查询效率高于 B-TREE

  • B-TREE 第二级可以直接取出来,树形结构比较重,在内存无限大的时候有优势。

1)B-树的关键字和记录是放在一起的,叶子节点可以看作外部节点,不包含任何信息;
B+树叶子节点中只有关键字和指向下一个节点的索引,记录只放在叶子节点中。(一次查询可能进行两次i/o操作)

2)在B-树中,越靠近根节点的记录查找时间越快,只要找到关键字即可确定记录的存在;
而B+树中每个记录的查找时间基本是一样的,都需要从根节点走到叶子节点,而且在叶子节点中还要再比较关键字。
从这个角度看B-树的性能好像要比B+树好,而在实际应用中却是B+树的性能要好些。
因为B+树的非叶子节点不存放实际的数据,这样每个节点可容纳的元素个数比B-树多,树高比B-树小,这样带来的好处是减少磁盘访问次数。
尽管B+树找到一个记录所需的比较次数要比B-树多,但是一次磁盘访问的时间相当于成百上千次内存比较的时间,因此实际中B+树的性能可能还会好些,
而且B+树的叶子节点使用指针连接在一起,方便顺序遍历(例如查看一个目录下的所有文件,一个表中的所有记录等),这也是很多数据库和文件系统使用B+树的缘故。

思考:为什么说B+树比B-树更适合实际应用中操作系统的文件索引和数据库索引?

  1. B+树的磁盘读写代价更低
    B+树的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B 树更小。
    如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。
    一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了。
  2. B+树的查询效率更加稳定
    由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。
    所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。

聚簇索引与非聚簇索引

聚簇索引并不是一种单独的索引类型,而是一种数据存储方式。
术语‘聚簇’表示数据行和相邻的键值进错的存储在一起。
如下图,左侧的索引就是聚簇索引,因为数据行在磁盘的排列和索引排序保持一致。
在这里插入图片描述

  • 聚集索引和非聚集索引的根本区别是表记录的排列顺序和与索引的排列顺序是否一致。
  • 聚簇索引的好处
    按照聚簇索引排列顺序,查询显示一定范围数据的时候,由于数据都是紧密相连,数据库不用从多个数据块中提取数据,所以节省了大量的io操作。
  • 聚簇索引的限制:
    对于mysql数据库目前只有innodb数据引擎支持聚簇索引,而Myisam并不支持聚簇索引。
    由于数据物理存储排序方式只能有一种,所以每个Mysql的表只能有一个聚簇索引。一般情况下就是该表的主键。
    为了充分利用聚簇索引的聚簇的特性,所以innodb表的主键列尽量选用有序的顺序id,而不建议用无序的id,比如uuid这种。(参考聚簇索引的好处。)
  • 这里说明了主键索引为何采用自增的方式:1、业务需求,有序。2、能使用到聚簇索引
  • 除开 innodb 引擎主键默认为聚簇索引 外。 innodb 的索引都采用的 B+TREE
    myisam 则都采用的 B-TREE索引

full-text全文检索

  • 全文索引(也称全文检索)是目前搜索引擎使用的一种关键技术。它能够利用【分词技术】等多种算法智能分析出文本文字中关键词的频率和重要性,然后按照一定的算法规则智能地筛选出我们想要的搜索结果。
CREATE TABLE `article` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(200) DEFAULT NULL,
  `content` text,
  PRIMARY KEY (`id`),
  FULLTEXT KEY `title` (`title`,`content`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
  • 不同于like方式的的查询:
SELECT * FROM article WHERE content LIKE '%查询字符串%';
  • 全文索引用match+against方式查询:
SELECT * FROM article WHERE MATCH(title,content) AGAINST (‘查询字符串’);
  • 明显的提高查询效率。
  • 限制:
    mysql5.6.4以前只有Myisam支持,5.6.4版本以后innodb才支持,但是官方版本不支持中文分词,需要第三方分词插件。
    5.7以后官方支持中文分词。
    随着大数据时代的到来,关系型数据库应对全文索引的需求已力不从心,逐渐被 solr,elasticSearch等专门的搜索引擎所替代。

Hash索引

  • Hash索引只有Memory, NDB两种引擎支持,Memory引擎默认支持Hash索引,如果多个hash值相同,出现哈希碰撞,那么索引以链表方式存储。
  • NoSql采用此中索引结构。

R-tree

  • R-Tree在mysql很少使用,仅支持geometry数据类型,支持该类型的存储引擎只有myisam、bdb、innodb、ndb、archive几种。

  • 相对于b-tree,r-tree的优势在于范围查找。

MySQL索引分类

主键索引

  • 设定为主键后数据库会自动建立索引,innodb为聚簇索引
随表一起建索引:
CREATE TABLE customer (id INT(10) UNSIGNED  AUTO_INCREMENT ,customer_no VARCHAR(200),customer_name VARCHAR(200),
  PRIMARY KEY(id) 
);
  • unsigned (无符号的)
  • 使用 AUTO_INCREMENT 关键字的列必须有索引(只要有索引就行)。
CREATE TABLE customer2 (id INT(10) UNSIGNED   ,customer_no VARCHAR(200),customer_name VARCHAR(200),
  PRIMARY KEY(id) 
);
单独建主键索引:
ALTER TABLE customer 
 add PRIMARY KEY customer(customer_no); 
删除建主键索引:
ALTER TABLE customer 
drop PRIMARY KEY ;  
修改建主键索引:
  • 必须先删除掉(drop)原索引,再新建(add)索引

单值索引

  • 一个索引只包含单个列,一个表可以有多个单列索引
随表一起建索引:
CREATE TABLE customer (id INT(10) UNSIGNED  AUTO_INCREMENT ,customer_no VARCHAR(200),customer_name VARCHAR(200),
  PRIMARY KEY(id),
  KEY (customer_name)  
);
  • 随表一起建立的索引 索引名同 列名(customer_name)
单独建单值索引:
CREATE  INDEX idx_customer_name ON customer(customer_name); 
删除索引:
DROP INDEX idx_customer_name ON customer;

唯一索引

  • 索引列的值必须唯一,但是允许空值
随表一起建索引:
CREATE TABLE customer (id INT(10) UNSIGNED  AUTO_INCREMENT ,customer_no VARCHAR(200),customer_name VARCHAR(200),
  PRIMARY KEY(id),
  KEY (customer_name),
  UNIQUE (customer_no)
);
  • 建立 唯一索引时必须保证所有的值是唯一的(除了null),若有重复数据,会报错。
单独建唯一索引:
CREATE UNIQUE INDEX idx_customer_no ON customer(customer_no); 
删除索引:
DROP INDEX idx_customer_no on customer ;

复合索引

  • 一个索引包含多个字段。
    在数据库操作期间,复合索引比单值索引所需要的开销更小(对于相同的多个列建索引)
    当表的行数远大于索引列的数目时可以使用复合索引
随表一起建索引:
CREATE TABLE customer (id INT(10) UNSIGNED  AUTO_INCREMENT ,customer_no VARCHAR(200),customer_name VARCHAR(200),
  PRIMARY KEY(id),
  KEY (customer_name),
  UNIQUE (customer_name),
  KEY (customer_no,customer_name)
);
单独建索引:
CREATE  INDEX idx_no_name ON customer(customer_no,customer_name); 
删除索引:
DROP INDEX idx_no_name  on customer ;

基本语法

创建索引
CREATE [UNIQUE] INDEX indexName ON tabName(columnName(length));
删除索引
DROP INDEX [indexName] ON tabName;
查看索引
  • 加上\G就可以以列的形式查看了 不加\G就是以表的形式查看
SHOW INDEX FROM tabName \G;

该索引知识来源:https://blog.csdn.net/cdx_akang/article/details/83900427

在这里插入图片描述

在这里插入图片描述

  • Table:表名
  • non_unique: 是否是唯一索引 1:是 0:不是
  • key_name:索引名称,如果名字相同则表明是同一个索引,而并不是重复,复合索引
  • seq_in_index:列 在索引中的 序列。针对符合索引(一个索引对应多个列)。针对同一个复合索引 按照创建复合索引时的顺序进行排序
  • Column_name:索引的列名
  • collation:列以什么方式存储在索引中,大概意思就是字符序。在MySQLSHOW INDEX语法中,有值’A’(升序)或NULL(无分类)。默认的类型是utf8_general_ci,这样的大小写不敏感,比如下面两个sql会出现相同的查询结果:
select * from Table where content = 'Yes'
select * from Table where content = 'yes'

这样可能不符合你的要求,你需要大小写敏感的情况,你可以修改字段字符集类型,如下sql

alter table bbs_posts modify column content varchar(5000) NOT NULL collate utf8_bin;

这样修改以后就OK了。

  • cardinality: 基数的意思,表示索引中唯一值的数目的估计值。基数根据被存储为整数的统计数据来计数,所以即使对于小型表,该值也没有必要是精确的。基数越大,当进行联合时,MySQL使用该索引的机会就越大。我们知道某个字段的重复值越少越适合建索引,所以我们一般都是根据Cardinality来判断索引是否具有高选择性,如果这个值非常小,那就需要重新评估这个字段是否适合建立索引。因为MySQL数据库中有各种不同的存储引擎,而每种存储引擎对于B+树索引的实现又各不相同。所以对Cardinality统计时放在存储引擎层进行的,至于它是如何统计更新的在这里就不再做更深入的介绍了。
  • sub_part:前置索引的意思,如果列只是被部分地编入索引,则为被编入索引的字符的数目。如果整列被编入索引,则为NULL。如上图所示,除了index_content那行显示4外,其他的都是NULL,表明index_content是一个长度为4的前置索引。对于BLOB,TEXT,或者很长的VARCHAR类型的列,必须使用前缀索引,因为MySQL不允许索引这些列的完整长度,这会让索引变得大且慢。选择长度的诀窍在于要选择足够长的前缀以保证较高的选择性,同时又不能太长以便节约空间。下面是计算前置索引长度的一般方法:
select count(distinct left(content,3))/count(*) from bbs_posts as sel3

select count(distinct left(content,4))/count(*) from bbs_posts as sel4

select count(distinct left(content,5))/count(*) from bbs_posts as sel5

最后算出来那个长度的基数接近完整列的选择行就OK了,完整列

select count(distinct content)/count(*) from bbs_posts
  • packed:指示关键字如何被压缩。如果没有被压缩,则为NULL。压缩一般包括压缩传输协议、压缩列解决方案和压缩表解决方案。
  • Null:是否允许 null 值
  • Index_type:索引类型,Mysql目前主要有以下几种索引类型:FULLTEXT,HASH,BTREE,RTREE
  • comment: 注释的意思。
  • index_comment: 注释的意思。
使用ALTER命令来为数据表添加索引
/* 1、该语句添加一个主键,这意味着索引值必须是唯一的,并且不能为NULL */
ALTER TABLE tabName ADD PRIMARY KEY(column_list);

/* 2、该语句创建索引的键值必须是唯一的(除了NULL之外,NULL可能会出现多次) */
ALTER TABLE tabName ADD UNIQUE indexName(column_list);

/* 3、该语句创建普通索引,索引值可以出现多次 */
ALTER TABLE tabName ADD INDEX indexName(column_list);

/* 4、该语句指定了索引为FULLTEXT,用于全文检索 */
ALTER TABLE tabName ADD FULLTEXT indexName(column_list);

哪些情况需要建索引

  • 主键自动建立主键索引(唯一 + 非空)。
  • 频繁作为查询条件的字段应该创建索引。
  • 查询中与其他表关联的字段,外键关系建立索引。
  • 查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度。
  • 查询中统计或者分组字段(group by也和索引有关)。

那些情况不要建索引

  • 记录太少的表。
  • 经常增删改的表。
  • 频繁更新的字段不适合创建索引。
  • Where条件里用不到的字段不创建索引。
  • 假如一个表有10万行记录,有一个字段A只有true和false两种值,并且每个值的分布概率大约为50%,那么对A字段建索引一般不会提高数据库的查询速度。
    索引的选择性是指索引列中不同值的数目与表中记录数的比。如果一个表中有2000条记录,表索引列有1980个不同的值,那么这个索引的选择性就是1980/2000=0.99。
    一个索引的选择性越接近于1,这个索引的效率就越高。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安澜仙王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值