赞
踩
B+树特征:
【参考】
1、中间节点和叶子节点都没满 → 直接插入
2、叶子节点满了,中间节点没满 → 叶子节点分裂
3、中间节点和叶子节点都满了 → 先分裂叶子节点后分裂中间节点直至结构不违规
以上方式的分裂带来的问题?
叶子节点已经满了,但是其兄弟节点没满,会将记录移动到其兄弟节点上,默认先向左移动
相关字段名 | 描述 |
---|---|
PAGE_LAST_INSERT | 最后插入记录的位置 |
PAGE_DIRECTION | 记录的操作方向,PAGE_LEFT PAGE_RIGHT PAGE_SAME_REC PAGE_SAME_PAGE PAGE_NO_DIRECTION |
PAGE_N_DIRECTION | 同一方向连续插入的记录数 |
在特定的插入情况下,InnoDB的索引页利用率极低
bug的详细描述: https://bugs.mysql.com/bug.php?id=67718
这时候在插入9,由于InnoDB上层节点是记录的下级节点最小值,及1和100,则这时候9会被插入到索引页P1,再次发生分裂
同样,若在插入8,会再次分裂索引页:
导致索引页的空间利用率极低
目前mysql已修复
【参考】https://blog.csdn.net/weixin_35044595/article/details/113168956
B+树的删除是根据填充因子(最小50%)来控制的
1、中间节点和叶子节点都不小于填充因子
直接删除,若该元素还是中间节点的元素,用该叶子节点最右边的元素代替
2、只有叶子节点小于填充因子
合并叶子节点和他的兄弟节点,同时更新中间节点
3、中间节点和叶子节点都小于填充因子
合并叶子节点和他的兄弟节点,更新中间节点,合并中间节点和他的兄弟节点
CREATE TABLE t (
a INT NOT NULL,
b VARCHAR(8000),
c INT NOT NULL,
PRIMARY KEY (a),
KEY idx_c (c)
) ENGINE=INNODB;
INSERT INTO t SELECT 1, REPEAT('a', 7000), -1;
INSERT INTO t SELECT 2, REPEAT('b', 7000), -2;
INSERT INTO t SELECT 3, REPEAT('c', 7000), -3;
INSERT INTO t SELECT 4, REPEAT('d', 7000), -4;
mysql的每页只有16kb,也就是每页最多存放两条记录,其聚集索引构造图如下图所示:
比如查询a=1的数据,通过根节点(索引页)找到a=3的数据所在页是0005,读取0005页,在此页读取a=3的数据(通过B+树索引找到数据所在数据页,在数据页中通过二分查找搜索指定数据)
对于辅助索引,叶子节点并不包含行记录的所有数据,叶子节点除了包含键值以外,每个叶子节点的索引行中还包含了相应行数据的聚集索引键。
方式1:ALTER TABLE table_name ADD index_type index_name(index_col_name,...);
方式2:CREATE index_type index_name ON table_name(index_col_name,...);
方式1:ALTER TABLE table_name DROP index_type index_name;
方式2:DROP index_type index_name ON table_name;
SHOW INDEX FROM table_name;
SHOW INDEX 字段 | 描述 |
---|---|
Table | 索引所在表明 |
Non_unique | 是否为唯一索引,0-是,1-不是 |
Key_name | 索引名字 |
Seq_in_index | 索引中该列的位置,比如idx_a_c,c在该索引的位置是2 |
Column_name | 索引列名 |
Collation | 列以什么方式存储在索引中,可以是A/NULL,对于B+树索引总是A |
Cardinality | 索引中唯一值的数目的估计值,Cardinality/表的行数越接近于1,说明索引的区分度越大 |
Sub_part | 是否是列的部分被索引,比如用某个字段的前10个字符作为索引 |
Packed | 关键字如何被压缩,若未被压缩则为NULL |
NULL | 是否索引的列含有NULL值,若有则为YES |
Index_type | 索引结构类型,InnoDB只支持B+树索引,显示为BTREE |
Comment | 注释 |
Cardinality值是索引中不重复记录数量的预估值,Cardinality除以表的行数越接近于1,说明索引的选择性越高。
Cardinality的大小mysql优化器对语句执行计划进行判定时依据,如果Cardinality的值越小,说明该索引的区分度越小, 优化器会认为,这个索引对语句没有太大帮助,而不使用索引, 如果Cardinality值越大,就意味着,使用索引能排除越多的数据,执行也更为高效, 这个值越大在做表连接的时候,就越有机会选择这个索引。
生产环境,索引的更新非常频繁,如果每次索引更新都更新Cardinality值,将会给数据库带来很大的负担,另外如果表非常大,更新一次Cardinality值的时间会很长。
上述统计方法说明,每次得到的Cardinality值可能是不同的,若每次得到的Cardinality值都是一样的,说明表索引的叶子节点数量小于等于8个,每次采样都会取到这些页,每次得到的Cardinality值相同。
联合索引是指对表上的多个列进行索引。
CREATE TABLE t (
a INT,
b INT,
c INT,
PRIMARY KEY (a),
KEY idx_b_c (b,c)
) ENGINE = INNODB
建立联合索引 | Using where; Using index |
---|---|
分别建立b和c的索引 | Using index condition; Using filesort |
注:Using index condition是MySQL5.6引入,含义在取出索引的同时判断是否可以进行WHERE条件过滤,将WHERE部分的过滤操作放在了存储引擎层。
SELECT key2 FROM table WHERE key1 = xxx;
SELECT primary key1, primary key2, key1, key2 FROM table WHERE key1 = xxx;
在用户选取的数据是整行数据时,辅助索引不能覆盖到全部信息,通过辅助索引查询到指定数据后,还需要通过聚集索引进行一次查询获取全部数据,虽然辅助索引中的数据是顺序存放的,但是再一次通过聚集索引查找的数据是无序的,这就变成了磁盘上的离散度操作。
如果访问的数据量比较小,优化器仍然会选择辅助索引,但是当数据量比较大时(一般占整张表数据量20%),优化器会选择聚集索引或者不使用索引进行查找。
MySQL支持索引提示(INDEX HINT),显示告诉优化器使用哪个索引。
命令:
EXPLAIN命令字段名 | 描述 |
---|---|
id | select的序列号,有几个select就有几个id,并且id的顺序是按select出现的顺序增长的。 id越大执行优先级越高,id相同则从上往下执行,id为NULL最后执行。 |
select_type | 查询类型: (1)SIMPLE-简单查询。查询不包含子查询和union (2)PRIMARY-复杂查询中最外层的 select (3)SUBQUERY-包含在 select 中的子查询(不在 from 子句中) (4)DERIVED-包含在 from 子句中的子查询。MySQL会将结果存放在一个临时表中,也称为派生表(derived的英文含义) (5)UNION-在 union 中的第二个和随后的 select (6)MYSQL5.6.3以后就可以EXPLAIN SELECT,UPDATE,DELETE |
table | 表示当前语句查询那张表 |
type | 表示关联类型或访问类型,即MySQL决定如何查找表中的行 从最优到最差分别为:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL |
possible_keys | 可能使用哪些索引来查找 |
key | mysql实际采用哪个索引来优化对该表的访问 |
key_len | mysql在索引里使用的字节数,key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的 |
ref | 在key列记录的索引中,表查找值所用到的列或常量,常见的有:const(常量),func,NULL,字段名 |
rows | mysql认为必须要逐行去检查和判断的记录的条数 |
filtered | 表示返回结果的行数占需读取行数的百分比,filtered的值越大越好 |
Extra | 额外信息,常见的有:Using where; Using index;Using index condition; Using filesort;using temporary |
【参考】https://www.mysqlzh.com/doc/66/292.html
适用场景:
【example】对于id分页第一页,
SELECT * FROM job_assign_detail WHERE job_id = 1100292 AND create_date > '2021-07-27 12:00:00' AND id >= 1 limit 20
由于create_date不在所用辅助索引中,需通过聚集索引读取到数据后进行过滤,若从id>=1开始,则通过辅助索引定位到聚集索引后,进行二次读取时有397825条不符合条件的数据被读取过滤,且数据量很大,会有离散读的情况,会造成慢查。
因此,先查通过辅助索引查出minId,SELECT min(id) FROM job_assign_detail WHERE job_id = 1100292 AND create_date > ‘2021-07-27 12:00:00’;查询minId只需要覆盖索引即可完成,不需要二次读取,执行速度快。
然后再进行查询SELECT * FROM job_assign_detail WHERE job_id = 1100292 AND create_date > ‘2021-07-27 12:00:00’ AND id >= #{minId} limit 20;
同样对于id分页最后一页,也有类似的情况。
(四 1)中已举例
【example】可以看到第一张图的key_len为5,使用了全部的联合索引idx_create_source_status_end_date,未出现Using filesort,而第二张图所示的查询方式,需要进行额外的排序操作。
普通索引和唯一索引的效率比较
而对于唯一索引,其不会使用Insert Buffer和Change Buffer(因为每次都需要判断唯一性的约束,需要把数据读取到内存才能判断,数据已经在内存了,也就不需要Insert Buffer和Change Buffer了)。
因此对于内存中存在的数据,普通索引和唯一索引的更新都很快,而对于没在内存中的数据,唯一索引的插入和更新需要访问磁盘,而普通索引通过Insert Buffer和Change Buffer减少磁盘的访问。将数据从磁盘读入内存涉及随机 IO 的访问,是数据库里面成本最高的操作之一。因此在写多的情况下,普通索引的写入性能是优于唯一索引的。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。