MySQL索引
Posted ych9527
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL索引相关的知识,希望对你有一定的参考价值。
1.索引带来的价值
索引带来的价值,是提高查找的效率,如果有大量的插入、更新、删除则不建议使用索引
2.mysql与磁盘
2.1mysql与磁盘交互的基本单位
- MySQL 作为一款应用软件,可以想象成一种特殊的文件系统。它有着更高的IO场景,所以,为了提高基本的IO效率, MySQL 进行IO的基本单位是 16KB (InnoDB 存储引擎)
- 这个基本单位在mysql之中叫做page,即只要发生了数据的交互,哪怕只有1bit,也是需要进行16KB的数据进行交互
- mysql在服务器启动的时候,会预先加载一大块空间自己进行内存管理,这块空间被称为Buffer Pool(mysql 5.7之中为128KB)
- page的大小是固定的,数据量有限,不存储数据,就能够存储更多的索引信息,目录page能够管理更多的page,否则目录page管理的页数太少,整颗树的层数就越多,更深,也就意味着从根节点到叶子节点的page更多,即需要更多的IO
2.2为什么交互的基本单位为page
- 由冯诺依曼体系可知,IO的时间远远大于CPU运算的时间,如果每次要那个数据就加载那个数据,这样IO的次数就提高了,程序所需要的时间就会提高,效率也就降低了
- 因此,mysql提高效率的本质是减少IO
- 根据局部性原理,我们查找的数据有很大的概率会在同一个区间之内,即同一个page之内,因此我们进行IO的时候就加载一个page到内存之中,如果下次数据在这个区间之间,就不需要进行IO,提高了程序的效率
2.3默认索引的创建
- 由下述代码可知
- 没有设置主键的时候,mysql会生成默认的索引(可不见的)
- 当设置了主键,mysql会生成默认的主键索引
mysql> create table t(id int,name varchar(10));
mysql> insert into t values(3,'小红');
mysql> insert into t values(1,'小白');
mysql> insert into t values(2,'小蓝');
mysql> select *from t;
+------+--------+
| id | name |
+------+--------+
| 3 | 小红 |
| 1 | 小白 |
| 2 | 小蓝 |
+------+--------+
//将id设置为主键 -> 生成主键索引,并且进行排序
mysql> create table t2(id int primary key,name varchar(10));
mysql> insert into t2 values(3,'小红');
mysql> insert into t2 values(1,'小白');
mysql> insert into t2 values(2,'小蓝');
mysql> select *from t2;
+----+--------+
| id | name |
+----+--------+
| 1 | 小白 |
| 2 | 小蓝 |
| 3 | 小红 |
+----+--------+
2.4理解单个page
- mysql中需要管理很多数据文件,管理一个文件,就需要描述和组织这个文件,可以将文件理解成是由一个或者多个page组成的
- 在mysql之中,不同的page的大小是相同的,都是16KB,page中的数据构成双向链表
- 当有主键存在时,mysql会默认按照主键进行排序存储
- 插入数据时,排序可以优化查询的效率,页内部存放数据的模块,实质上也是一个链表的结构,链表的特点也就是增删快,查询修改慢,所以优化查询的效率是必须的
- 正式因为有序,在查找的时候,从头到后都是有效查找,没有任何一个查找是浪费的,如果运气好,是可以提前结束查找过程的
2.5理解多个page
- page之中采用的也是链表的形式,如果有很多条数据的话,实际上也是采用的遍历的方式搜索数据,那么应该如何提高搜索的效率呢
- 通过给数据建立目录的方式来提高搜索的效率(不断的缩减范围,筛选)
- MySQL 中每一页的大小只有 16KB ,单个Page大小固定,所以随着数据量不断增大, 16KB 不可能存下所有的数据,那么必定会有多个页来存储数据
- 当对应的页多起来的时候,如果加载每个页去遍历检测的话,时间会非常慢,这时可以给每个页建立起对应的目录,这就是索引
- 使用一个目录项来指向某一页,而这个目录项存放的就是将要指向的页中存放的最小数据的键值
- 和页内目录不同的地方在于,这种目录管理的级别是页,而页内目录管理的级别是行
- 其中,每个目录项的构成是:键值+指针
- 通过索引,在查找的时候,筛选出满足条件的页加载到内存之中进行查找,这样大大的提高了效率
- 存在一个目录页来管理页目录,目录页中的数据存放的就是指向的那一页中最小的数据。有数据,就可通过比较,找到该访问那个Page,进而通过指针,找到下一个Page
- 其实目录页的本质也是页,普通页中存的数据是用户数据,而目录页中存的数据是普通页的地址
- 小结
- Page分为目录页和数据页。目录页只放各个下级Page的最小键值
- 查找的时候,自顶向下找,只需要加载部分目录页到内存,即可完成算法的整个查找过程,大大减少了IO次数
- 每一层IO一次,再对加载的页进行二分查找,寻找下一层的页的地址,或者对应的数据的地址
3.innoDB在建立索引结构来管理数据的时候,除了B+树,可以使用其他结构吗?
- 链表
- 链表是线性结构的,查找的时候需要线性遍历
- 二叉搜索树?
- 在某些场景下,二叉搜索树会退化成链表
- AVL &&红黑树
- 和B+树相比,B+树的层数更低,每层进行一次IO,树越矮,IO的次数越少,AVL和红黑树相对来说,比B+树更改
- Hash
- 官方的索引实现方式中, MySQL 是支持HASH的,不过 InnoDB 和 MyISAM 并不支持.Hash跟进其算法特征,决定了虽然有时候也很快(O(1)),不过,在面对范围查找就明显不行
- B树
- B树节点,既有数据,又有Page指针,而B+,只有叶子节点有数据,其他目录页,只有键值和Page指针
- B+树节点不存储data,这样一个节点就可以存储更多的key。可以使得树更矮,所以IO操作次数更少
- B+叶子节点,全部相连,而B没有
- 叶子节点相连,更便于进行范围查找
- B树节点,既有数据,又有Page指针,而B+,只有叶子节点有数据,其他目录页,只有键值和Page指针
4.InnoDB 和 MyISAM索引的区别
4.1聚簇索引 VS 非聚簇索引
- MyISAM 最大的特点是,将索引Page和数据Page分离,也就是叶子节点没有数据,只有对应数据的地址。这种用户数据与索引数据分离的索引方案,叫做非聚簇索引
- InnoDB 的数据和索引是放在一起的,这种用户数据与索引数据在一起索引方案,叫做聚簇索引
//存储引擎使用MyISAM
create table t3 (id int,name varchar(10))engine=myisam;
//查看索引和数据结构
root@VM-0-6-centos etc]# cd /var/lib/mysql
[root@VM-0-6-centos mysql]# cd base2
[root@VM-0-6-centos base2]# ls
//innoDB和 MyISAM引擎的索引区别
MyISAM -> t3.MYD(表中数据) t3.frm(表结构数据) t3.MYI(索引)
innoDB -> t2.ibd(索引和用户数据) t2.frm(表结构数据)
4.2辅助索引
- MySQL 除了默认会建立主键索引外,我们用户也有可能建立按照其他列信息建立的索引,一般这种索引可以叫做辅助(普通)索引
- 对于 MyISAM ,建立辅助(普通)索引和主键索引没有差别,无非就是主键不能重复,而非主键可重复
- InnoDB 的非主键索引中叶子节点并没有数据,而只有对应记录的key值
- 所以通过辅助(普通)索引,找到目标记录,需要两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。这种过程,就叫做回表查询
- 不给辅助索引的叶子节点附上数据,有利于空间的节省
- 小结
- MyISAM相对于innoDB 删除和修改的效率会低一点,MyISAM删除和修改除了对数据进行更新外,还需要对内容进行更新。innoDB可以直接在B+树中进行修改,因为叶子节点中存储的是数据
- MyISAM查找的效率高一些,innoDB有可能需要进行回表查询,所以有可能会慢一点
5.索引操作
5.1创建索引
- 主键索引(指定主键,mysql自定创建)
- 一个表中,最多有一个主键索引
- 主键索引的效率高(主键不可重复)
- 创建主键索引的列,它的值不能为null,且不能重复
- 主键索引的列基本上是int
- 唯一建索引(添加唯一建,mysql自动创建)
- 一个表中,可以有多个唯一索引
- 查询效率高
- 如果在某一列建立唯一索引,必须保证这列不能有重复数据
- 如果一个唯一索引上指定not null,等价于主键索引(主键和唯一建的区别,主键不可以为空,唯一建可以为空)
- 创建普通索引
- 创建方法
- 在表的最后定义、指定某列为索引
- 创建完成表后指定某列为普通索引
- 直接创建一个索引
- 特点
- 一个表中可以有多个普通索引,普通索引在实际开发中用的比较多
- 如果某列需要创建索引,但是该列有重复的值,那么我们就应该使用普通索引
- 创建方法
mysql> create table t6(id int ,name char(10),grade int,index(id));在表的最后定义、指定某列为索引
mysql> alter table t6 add index(name); 创建完成表后指定某列为普通索引
mysql> create index idx_name on t6(grade); 直接创建一个索引
- 全文索引的创建
- 当对文章字段或有大量文字的字段进行检索时,会使用到全文索引。MySQL提供全文索引机制,但是有要求,要求表的存储引擎必须是MyISAM,而且默认的全文索引支持英文,不支持中文。如果对中文进行全文检索,可以使用sphinx的中文(coreseek)。innoDB不支持,主要使用在查询多,修改、插入少的时候
- 只有字段的数据类型为 char、varchar、text 及其系列才可以建全文索引
- fulltext(关键字,关键字…) -> 创建全文索引
//不能使用id作为全文索引,因为id为int类型
mysql> create table t1(id int primary key auto_increment,name char(10),body text,fulltext(name,body))engine=myisam;
//插入数据
mysql> select *from t1;
+----+------+-------------------+
| id | name | body |
+----+------+-------------------+
| 1 | jim | how are you |
| 2 | tom | im fine |
| 3 | bob | today is good day |
+----+------+-------------------+
- explain查看是否使用全文索引
- explain 预执行指令,查看结果
mysql> select *from t1 where name like '%j%';
+----+------+-------------+
| id | name | body |
+----+------+-------------+
| 1 | jim | how are you |
+----+------+-------------+
1 row in set (0.01 sec)
mysql> explain select *from t1 where name like '%j%'\\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t1
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL -> 没有使用索引
key_len: NULL
ref: NULL
rows: 3
- 如何使用全文索引
- 关键字 match(全文索引) against(匹配内容)
mysql> select *from t1 where match(name,body) against('today');
+----+------+-------------------+
| id | name | body |
+----+------+-------------------+
| 3 | bob | today is good day |
+----+------+-------------------+
mysql> explain select *from t1 where match(name,body) against('today')\\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t1
partitions: NULL
type: fulltext
possible_keys: name
key: name -> 使用了全文索引
key_len: 0
ref: const
rows: 1
- 全文索引注意点
- 需要注意索引有最小长度和最大长度,小于或者大于规定长度都不会进行索引
5.2查询索引
-
第一种方法: show keys from 表名
-
第二种方法: show index from 表名
-
第三种方法:desc 表名
mysql> show keys from t4\\G;
*************************** 1. row ***************************(第一个主键)
Table: t4 <- 表名
Non_unique: 0 <- 如果索引不能包括重复值则为0,如果可以则为1。也就是平时所说的唯一索引
Key_name: PRIMARY <- 索引的名字
Seq_in_index: 1 <- 索引中的列序列号,从1开始
Column_name: id <- 索引是那个(索引的列名)
Collation: A <- 列以什么方式存储在索引中,大概意思就是字符序
Cardinality: 0 <- 基数的意思,表示索引中唯一值的数目的估计值
Sub_part: NULL <-前置索引的意思,如果列只是被部分地编入索引,则为被编入索引的字符的数目。如果整列被编入索引,则为NULL
Packed: NULL <-指示关键字如何被压缩。如果没有被压缩,则为NULL
Null: <-如果列含有NULL,则含有YES
Index_type: BTREE <- 以二叉树的形式构建索引
Comment:
Index_comment: <- 注释的意思
mysql> desc t6;
+-------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+----------+------+-----+---------+-------+
| id | int(11) | YES | MUL | NULL | | (key MUL)表示索引
| name | char(10) | YES | MUL | NULL | |
| grade | int(11) | YES | MUL | NULL | |
+-------+----------+------+-----+---------+-------+
5.3删除索引
- 第一种方法:删除主键索引:alter table 表名 drop primary key(本质就是删除主键)
- 第二种方法:其他索引的删除:alter table 表名 drop index 索引名,索引名就是show keys from 表名中的key_name字段
- 第三种方法:drop index 索引名 on 表名
mysql> alter table t6 drop index id; //删除索引
mysql> desc t6;
+-------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+----------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | char(10) | YES | MUL | NULL | |
| grade | int(11) | YES | MUL | NULL | |
+-------+----------+------+-----+---------+-------+
mysql> drop index name on t6;
mysql> drop index idx_name on t6;
mysql> desc t6;
+-------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+----------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | char(10) | YES | | NULL | |
| grade | int(11) | YES | | NULL | |
+-------+----------+------+-----+---------+-------+
5.4索引创建的原则
- 查询比较频繁的字段应该创建索引
- 唯一性太差的字段,不适合单独创建索引(插入新的数据,如果重复会需要进行索引更新)
- 更新非常频繁的字段不适合作为索引
- 不会再where子句中出现的字段不该创建索引
5.5其它索引了解
- 复合索引
- 索引最左匹配原则
- 索引覆盖
6.总结-什么是索引
- 数据都是存储在磁盘上的,当使用mysql的时候,会将mysql加载到内存之中
- mysql会申请大块内存自己进行调配,这块内存叫做buffer pool,mysql5.7中,这个大小为128KB
- mysql与硬盘交互的大小为16KB,称之为页
- 如果每次对数据进行查找或者修改都将所有的页加载至内存之中,那么当数据量很大次,就会需要IO很多次,这样程序的效率也会直线降低
- 索引的价值就在于,将对应的页加载至buff pool中,对这个页进行二分查找,找到范围内的关键字,然后寻找下一个页或者提取数据。这样就可以有选择性的读取某些页,这就是索引
- 通常索引用的数据结构是B+树,这样,每一层只需要进行一次IO数据都在叶子节点之中,因此树的高度约低,进行的IO次数越少,程序的效率越高
以上是关于MySQL索引的主要内容,如果未能解决你的问题,请参考以下文章
javascript UV Index Monitor App订阅PubNub并显示UV索引值。博文的代码片段。在这里查看项目:https:// githu
c_cpp UV Index Indicator订阅PubNub并使用颜色显示UV索引值。博文的代码片段。在这里查看项目:https:/