mysql索引归纳

MySQL的索引数据结构

MySQL的索引数据结构

MySQL 的存储引擎

MySQL 的存储引擎

MySQL中的索引

MySQL中的InnoDB引擎使用B+Tree结构来存储索引,可以尽量减少数据查询时磁盘IO次数,同时树的高度直接影响了查询的性能,一般树的高度维持在 3~4 层。

B+Tree由三部分组成:根root、枝branch以及Leaf叶子,其中root和branch不存储数据,只存储指针地址,数据全部存储在Leaf Node,同时Leaf Node之间用双向链表链接
每个Leaf Node是三部分组成的,即前驱指针p_prev,数据data以及后继指针p_next,同时数据data是有序的,默认是升序ASC,分布在B+tree右边的键值总是大于左边的,同时从root到每个Leaf的距离是相等的,也就是访问任何一个Leaf Node需要的IO是一样的,即索引树的高度Level + 1次IO操作。

我们可以将MySQL中的索引可以看成一张小表,占用磁盘空间,创建索引的过程其实就是按照索引列排序的过程,先在sort_buffer_size进行排序,如果排序的数据量大,sort_buffer_size容量不下,就需要通过临时文件来排序,最重要的是通过索引可以避免排序操作(distinct,group by,order by)。

分类

数据结构维度

B+树索引:所有数据存储在叶子节点,复杂度为O(logn),适合范围查询。
哈希索引: 适合等值查询,检索效率高,一次到位。
全文索引:MyISAM和InnoDB中都支持使用全文索引,一般在文本类型char,text,varchar类型上创建。
R-Tree索引: 用来对GIS数据类型创建SPATIAL索引

物理存储维度

聚集索引:聚集索引就是以主键创建的索引,在叶子节点存储的是表中的数据。
非聚集索引:非聚集索引就是以非主键创建的索引,在叶子节点存储的是主键和索引列。

逻辑维度

主键索引:一种特殊的唯一索引,不允许有空值。
普通索引:MySQL中基本索引类型,允许空值和重复值。
联合索引:多个字段创建的索引,使用时遵循最左前缀原则。
唯一索引:索引列中的值必须是唯一的,但是允许为空值。
空间索引:MySQL5.7之后支持空间索引,在空间索引这方面遵循OpenGIS几何数据模型规则。

聚集索引

sql数据库是行数据库,数据是一行一行存储的,而聚集索引是个特殊的索引,相当于这一行行记录的物理编号,描述这一行行数据的物理存储顺序。所以,一张表只会有一个聚集索引。

聚集索引的叶子节点指向的就是实际数据页

拿mysql来说,聚集索引通常是表的主键,若无主键则为表中第一个非空的唯一索引,还是没有就采用innodb存储引擎为每行数据内置的ROWID作为聚集索引。

聚集索引与非聚集索引区别

聚集索引一个表只能有一个,而非聚集索引一个表可以存在多个
聚集索引存储记录是物理上连续存在,而非聚集索引是逻辑上的连续,物理存储并不连续
聚集索引:物理存储按照索引排序;聚集索引是一种索引组织形式,索引的键值逻辑顺序决定了表数据行的物理存储顺序。
非聚集索引:物理存储不按照索引排序;非聚集索引则就是普通索引了,仅仅只是对数据列创建相应的索引,不影响整个表的物理存储顺序。
索引是通过二叉树的数据结构来描述的,我们可以这么理解聚簇索引:索引的叶节点就是数据节点。而非聚簇索引的叶节点仍然是索引节点,只不过有一个指针指向对应的数据块。

索引是通过二叉树的形式进行描述的,我们可以这样区分聚集与非聚集索引的区别:聚集索引的叶节点就是最终的数据节点,而非聚集索引的叶节仍然是索引节点,但它有一个指向最终数据的指针。

回表查询

InnoDB聚集索引的叶子节点存储行记录,因此, InnoDB必须要有,且只有一个聚集索引:

(1)如果表定义了PK,则PK就是聚集索引;

(2)如果表没有定义PK,则第一个not NULL unique列是聚集索引;

(3)否则,InnoDB会创建一个隐藏的row-id作为聚集索引;

InnoDB普通索引的叶子节点存储主键值。

画外音:注意,不是存储行记录头指针,MyISAM的索引叶子节点存储记录指针。

有表:

t(id PK, name KEY, sex, flag);

id是聚集索引,name是普通索引。

表中有四条记录:

1, shenjian, m, A

3, zhangsan, m, A

5, lisi, m, A

9, wangwu, f, B

两个B+树索引:

(1)id为PK,聚集索引,叶子节点存储行记录;

(2)name为KEY,普通索引,叶子节点存储PK值,即id;

既然从普通索引无法直接定位行记录,那普通索引的查询过程是怎么样的呢?

通常情况下,需要扫码两遍索引树。

例如:

select * from t where name=‘lisi’; 
需要扫码两遍索引树:

(1)先通过普通索引定位到主键值id=5;

(2)在通过聚集索引定位到行记录;

这就是所谓的回表查询,先定位主键值,再定位行记录,它的性能较扫一遍索引树更低。

二、索引覆盖(Covering index)

explain的输出结果Extra字段为Using index时,能够触发索引覆盖。

只需要在一棵索引树上就能获取SQL所需的所有列数据,无需回表,速度更快。

常见的方法是:将被查询的字段,建立到联合索引里去。

主键为什么建议选择自增主键

从性能方面, 避免页分裂;
如果选用自增主键的话,每次新增数据时,都是以追加的形式进行存储,在本页索引写满之后, 只需申请一个新页继续写入即可, 不会产生页分裂问题。
如果说采用业务字段作为主键的话,新增数据不一定是顺序的,需要挪动数据, 页快满时还要去分裂页, 保持索引的有序性,造成写数据成本较高。
从存储方面, 节省空间;
除了性能方面有区别外,再来看看存储方面。假设你的表中确实有一个唯一字段,比如字符串类型的身份证号,那应该用身份证号做主键,还是用自增字段做主键呢?
表中如果使用自增主键的话,若使用bigint做主键,占8个字节;如果使用身份证号作为主键的话,占用20字节。

所以,主键长度越小,普通索引的叶子节点就越小,普通索引占用的空间也就越小,页存储索引越多。

上述从性能和存储空间进行分析采用自增主键和采用业务字段作为主键的优缺点,也推断出采用自增主键是比较合理的方案。

什么是最左前缀原则?什么是最左匹配原则

最左前缀原则:

顾名思义,就是最左优先,在创建多列索引时,要根据业务需求,where子句中使用最频繁的一列放在最左边。

最左匹配原则:

MySQL 建立多列索引(联合索引)有最左匹配的原则,即最左优先:如果有一个 2 列的索引 (a, b),则已经对 (a)、(a, b) 上建立了索引;如果有一个 3 列索引 (a, b, c),则已经对 (a)、(a, b)、(a, b, c) 上建立了索引;
mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
=和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式.

为什么要求MySQL采用非空字段

节省空间
从数据存储角度分析,NULL 列需要更多的存储空间,需要一个额外字节作为判断是否为NULL的标志位。

减少空值计数错误
有 NULL COLUMN存在的情况下,COUNT(NULL COLUMN)需要格外注意,NULL 值不会参与统计。

对索引友好

索引效率高的前提是列的区分度大,包含NULL的列很难优化,而且对表索引时不会存储 NULL 值。如果索引字段可以为 NULL,索引的效率会下降。因为它们使得索引、索引的统计信息以及比较运算更加复杂,所以要求列值非空。
NOT IN NOT LIKE <>查询
NOT IN NOT LIKE <>等条件查询在有 NULL 值的情况下,返回为空结,查询数据存在问题。

代码角度分析
NULLPOINTEREXCEPTION异常问题
代码使用时,对于映射属性处理可以在能减少NULLPOINTEREXCEPTION异常情况。

为什么不建议直接使用SELECT *

  1. 不必要的磁盘I/O
    我们知道 MySQL 本质上是将用户记录存储在磁盘上,因此查询操作就是一种进行磁盘IO的行为(前提是要查询的记录没有缓存在内存中)。

查询的字段越多,说明要读取的内容也就越多,因此会增大磁盘 IO 开销。尤其是当某些字段是 TEXT、MEDIUMTEXT或者BLOB 等类型的时候,效果尤为明显。

  1. 加重网络时延
    承接上一点,虽然每次都是把socket send buffer中的数据发送给客户端,单次看来数据量不大,可架不住真的有人用*把TEXT、MEDIUMTEXT或者BLOB 类型的字段也查出来了,总数据量大了,这就直接导致网络传输的次数变多了。

无法使用覆盖索引

可能拖慢JOIN连接查询

猜你喜欢

转载自blog.csdn.net/liuerchong/article/details/123885323
今日推荐