MySQL索引原理与算法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013862108/article/details/83721299

B+ 树索引

B 代表 balance 平衡;

B+ 树索引  通过键值(如 id=3 ) 并不能直接找到具体的行。 它是把 行(数据行 记录)所在的页,读入内存,再从内存中查找, 最后得到要找的记录(数据)。

二分查找法:(折半查找法)

首先  是 有一组  排好顺序的 记录。 如 10, 20,30, 40,50,60,70, 80,90,100

问题是 从这样的一组排好顺序的记录中 查找 某一个指定 记录。?

采取的方法是:1. 首先将 中间位置的记录 作为比较对象。 2; 要找的元素和 比较对象 比较 ,如果小于比较对象 缩小到左半部分;如果大于比较对象 缩小到右半部分。

结论: 平均来说 二分查找法 比顺序查找法要好,效率高。

二叉树:/(二叉查找树)。

二叉树有哪些遍历方法?

前序遍历:  先访问根结点, 再访问左子树, 再访问右子树;

中序遍历: 先访问左子树,在父节点, 再右子树;

后序遍历:先访问左子树,再右子树; 最后是访问根结点;

层序遍历: 先访问根结点, 从上到下逐层遍历。同一层中从左到右访问。

二叉排序树 /   二叉查找树。 binary sort tree

它 特点1:   左子树上 所有结点的值 均小于 它的 根结点的值;

2. 右子树上 所有结点的值,均大于 它的根结点的值;

平衡二叉树  (Self -Balancing Binary  Search Tree)  :   首先是一种二叉排序树; 其中每一个结点 的左子树 和 右子树的 高度差 小于等于 1;

维护一个平衡二叉树 ,比如 插入,更新 和删除操作。 这些都是通过 左旋 或右旋 来实现的, 这都是开销。

B+  树: 是一种 平衡查找树。 叶子结点上 从小到大排序顺序排序。各个叶子结点 使用指针连接。

示意图: todo;

B+ 树 插入操作:  3种情况 ; Leaf Page满;  Index Page满   操作   表 todo;   有拆页的可能

旋转发生在Leaf Page已满。 但是其左右结点没有满的情况下,这时 B+树 不急于拆分 页的操作,而是将记录移到页的 兄弟结点上。

旋转 使B+ 树减少了一次 拆分操作。

B+ 树的删除操作。

B+ 树 使用  填充因子 fill factor  来控制 树的删除; /依据填充因子来 决定怎么删除; 填充因子 >= 50%

叶子节点 小于填充因子, 中间节点小于 填充因子 , 操作 三种情况  表 todo;  有合并页的可能。

B+ 树 索引 ; B+ 树 在数据库的应用/实现。

B+ 树 索引 特点 :高 扇出性,B+ 树 的高度 一般 2-4 层。 查找一行记录(ID = xxx, ID 是主键) 最多需要2到4次 IO; 假如机械硬盘每秒100次IO, 则查询一次需要时间 0.02——0.04 秒。

B+ 树 索引 分为 : 聚集索引(clustered index) ;    辅助索引(secondary index) /  非聚集索引(non-clustered index);

叶子节点 存放数据; 聚集索引的 叶子节点 存放时一整行的数据(完整的记录);

聚集索引, 中每个叶子节点 都是一个页; 叶子节点 之间使用 双向链表来进行链接。

可以使用 py_innodb_page_info.py 工具来分析表空间。

使用  hexdump 工具来 查看数据。

图: 5-14   todo;

注意:存储方式: 首先页不是 物理上连续的;通过双向链接; 再者。页中的记录 也是通过双向链表进行维护的。

聚集索引 好处 : 对 主键的 排序 查找 ; 和  范围查找   查找速度非常快。

mysql > explain select * from  Profile order by id limit 10;

mysql > explain select * from Profile  where id > and id < 10000\G;

辅助索引:

叶子节点 包含 键值(索引列字段值); 还包含 bookmark(主键的值)

每张表中可以有多个辅助索引。

例如: 通过辅助索引 怎么找到一行数据 ?

例如: 在一棵高度 为3的 辅助索引树 中查找数据,首先需要对这颗辅助索引遍历3次 找到 指定主键, 如果

指定的聚集索引树的高度 同样 为 3, 那么还需要对  聚集索引树进行3次 查找。最终找到一个完整的数据行所在的页。算下来,一共需要6次逻辑IO得到最终的一个数据页。

例子分析  图  5-16  todo;

B+ 树索引的分裂( 拆分页)

InnoDB存储引擎 的  Page Header 中有几个部分来保存插入的顺序信息。  PAGE_LAST_INSERT   PAGE_DIRECTION   PAGE_N_DIRECTION

增值插入 时 分裂点就是插入记录本身(如果要分裂的话);其他插入情况 暂时不深究。

索引创建和删除:

两种方式:

一:

ALTER  TABLE   tbl_name

ADD  {INDEX| KEY }   [index_name]   [index_type]  (index_col_name ,…)  [index_option] …

ALTER TABLE tbl_name

DROP PRIMARY KEY

| DROP  FOREIGN KEY  fk_symbol

| DROP  {INDEX| KEY} index_name

二:

CREATE  【UNIQUE  | FULLTEXT | SPATIAL ] INDEX index_name

[index_type]

ON  tbl_name  (index_col_name,…)   [index_option]   [algorithm_option | lock_option]  …

DROP INDEX index_name  ON  tbl_name  [algorithm_option | lock_option]  …

algorithm_option :

ALGORITHM [=]  {DEFAULT | INPLACE | COPY}

lock_option :

LOCK [=]  {DEFAULT |NONE  |SHARED | EXCLUSIVE}

查看索引:

SHOW INDEX FROM  tbl_name;

例子: 用户可以设置整个列的数据进行索引,也可以只索引一个列的开头部分数据, 如 b  为 varchar(8000) , 用户可以只索引 前 100个字段,如:

ALTER TABLE t       ADD  KEY idx_b (b(100));

SHOW  INDEX  结果 每一列的含义。

Collation:  列以什么方式存储在索引中, B+ 树 总是 A

Cardinality:  索引中 唯一值的数目的估计值。;  它不是 实时更新的 ,是个大概的值。

优化器 会 根据 Cardinality 的值来选择是否使用这个索引。

ANALYZE  TABLE  操作 会跟新 Cardinality 的值。

对 现有的数据表 (有很多数据) 进行 索引的 创建 或删除 , 会造成 什么影响,效率怎么样?以前是怎么做的,现在是怎么做的?

InnoDB  1.0.x   开始支持  快速索引创建 Fast Index Creation   简称: FIC。 针对的是辅助索引。

对于辅助索引的创建;  InnoDB存储引擎 会 对创建索引的表 加上一个 S 锁。 在创建过程中不需要重新建表。

辅助索引的删除:  更新内部视图, 将辅助索引的空间标记为可读, 同时删除内部视图上 对该表的索引定义。

主键的创建 和删除 同样需要重建一张表。

在线数据定义  Online DDL

MySQL 5.6版本开始 支持 Online DDL 在线数据定义 操作;  允许辅助索引创建的同时,还可以允许其他 像 INSERT  UPDATE  DELETE  这类DML 操作,

这极大地提高了Mysql 数据库在 生成环境中的可用性。

还支持的“在线”操作如:

辅助索引的创建与 删除

改变自增长值

添加或删除外键约束

列的重命名。

CREATE  【UNIQUE  | FULLTEXT | SPATIAL ] INDEX index_name

[index_type]

ON  tbl_name  (index_col_name,…)   [index_option]   [algorithm_option | lock_option]  …

ALGORITHM  指定了 创建 或删除索引的算法  可以取值如: COPY  INPLACE  DEFAULT;    默认 采用 DEFAULT  方式。

LOCK      创建或删除索引  添加锁的情况 ,可以取值如:

NONE,   //不加锁,这种模式可以获得最大的并发度

SHARE,    // S 锁, 并发的读可以,遇到写的事务,写事务就要等待。

EXCLUSIVE,   //  X 锁, 对目标 表 加上一个X 锁。 读写事务都不能进行。

DEFAULT ,  //  1, 首先判断能不能 使用 NONE,  2, 能不能使用 SHARE  , 3 能不能使用  EXCLUSIVE  .

Online DDL的原理: 在执行 创建或删除操作的同时,将 INSERT ,UPDATE,  DELETE,  这类DML操作日志写入到一个缓存中,等到完成索引创建后,再将重做应用到表上。  这个缓存默认大小是 128M  (由参数  innodb_online_alter_log_max_size 参数控制)。

在索引的创建过程中,SQL 优化器 不会 选择 正在创建中的索引。

什么样的情况下,适合加索引? 哪些字段适合加索引?

像 性别, 地区, 类型 字段 ,他们的取值范围很小,低选择性; 所以没必要加索引

像 姓名 就可以加索引。

Cardinality/ n_rows_in_table 应尽可能接近1。  如果非常小,那么用户需要考虑是否有必要加索引。

Cardinality 是怎么统计的?  是怎么计算的?

统计时通过采样 来完成的; Cardinality统计更新发生在 INSERT  和 UPDATE 。

策略: 1. 表中 1/16 的数据 已发生过 变化。

2.  stat_modified_counter > 2 000 000 000 .

默认采样数量 是 8

当 执行SQL 语句:

ANALYZE  TABLE;

SHOW  TABLE STATUS;

SHOW  INDEX;

以及 访问 information_schema  下的表 tables   和  statistics

时, 会导致InnoDB 存储引擎去重复计算索引 Cardinality 值。

如果表中 数据量 很大,并且表中有多个辅助索引,执行上述操作可能会非常慢。

不同应用中B+ 树索引的应用?

OLTP  应用  一般只从数据库中取得一小部分数据,一般 10条 ,这种建立 B+树索引有意义。

OLAP  应用, 都需要访问大量数据,多是面向分析的查询。 这个时候通常对时间字段进行索引。因为大多数统计需要根据时间维度来进行数据的筛选。

联合索引:

create table t(

a int,

b int,

primary key (a),

key idx_a_b (a,b)

)engine = innoDB

图  5-22  todo;

select  * from  t  where  a=xxx  and b=xxx   //可以使用到索引;

select  *  from t where  b=xxx;       //使用不到这棵索引;

select *  from  t  where a=xxx  order by b;      //可以使用到联合索引

联合索引的好处是 : 已经对 第二个键值进行了 排序处理。例如:

create table buy_log(

userid  int unsigned  not null,

buy_date date

)engine=InnoDB

alter  table buy_log add key(userid);

alter  table buy_log  add  key(userid,  buy_date);

select *  from  buy_log  where userid=2;

//分析 有两个索引 可以使用; 最终选择的是索引 userid;

select * from buy_log  where  userid=1  order by  buy_date  desc  limit 3;

//分析 可以用使用 userid, (userid, buy_date) 两个索引; 最终选择了联合索引  userid_2;  因为 联合索引中buy_date已经排好了, 根据联合索引取出数据,无须对buy_date做一次额外的排序操作。

对 a,  b,  c添加 联合 索引(a, b, c);  如下:

select … from  table   where a=xxx  order by  b;   //可以使用索引

select …  from table  where a=xxx  and  b=xxx  order by  c;   //可以使用索引。

覆盖索引: /  索引覆盖 (covering index):

从辅助索引中可以得到查询的话; 就不需要查询聚集索引中的记录了。  使用 覆盖索引的好处 是辅助索引中不包含整行记录的所有信息),所以大小要远小于聚集索引,因此可以减少大量的IO操作。

若 叶子节点 存放的数据 为 (primary key1,  primary key2, …,   key1, key2, ….).  下面语句都可以仅使用 一次辅助联合索引来完成查询。

select key2  from  table   where   key1=xxx;

select  primary key2, key2  from  table  where  key1=xxx;

select  primary key1, key2  from  table  where  key1=xxx;

select primary key1,  primary key2, key2  from table  where key1=xxx;

  1. 对于某些统计 问题 也可以 仅使用 辅助索引。

如  select count(*)  from  buy_log;           //Extra  Using index 代表使 优化器  进行了覆盖索引操作。

select count(*)  from buy_log  where  buy_date>=’2011-01-01’  and  buy_date<’2011-02-01’;   //

//(a, b) 的这种联合索引,一般是b  作为查询条件 是使用不到索引的,但是 如果是统计操作则 优化器 会进行选择。

什么情况下 使用不到索引?  什么情况下优化器 不使用索引。

多 发生在 范围查找 , join链接 等情况下。

select *  from  orderdetails  where  orderid > 10000  and  orderid  < 102000;

如果 要求访问的数据量很小, 则优化器还是会选择辅助索引;  如果当访问的数据占整个表中数据蛮大一部分(20% 左右),优化器会选择聚集索引来来查找数据。因为 顺序读取的速度远远快于离散读。

索引提示:index  hint

以下两种情况 可以用到 index hint

  1. MySQL 数据库的优化器错误地选择了某个索引。 很少见
  2. 某个SQL语句可以使用的索引很多,这时 查询优化器执行计划时间的开销可能会大于 SQL语句本身。

语法:

USE index  只是告诉优化器可以选择索引, 实际上优化器还是根据自己的判断进行操作。 可以使用 FORCE index 来强制使用索引。

Multi-Range  Read 优化 /  MRR 优化;(InnoDB  MyISAM 都支持)

MySQL 5.6 开始支持 MRR 优化;  MRR 适用于  range, ref,  eq_ref 类型 的查询。

MRR 的工作原理:/方式:

  1. 将 查询得到的辅助索引 键值存放于一个 缓存中(  默认 256k), 这时缓存中的数据是安装辅助索引  的 键值 进行排序的。
  2. 将缓存中的键值 根据 rowID(主键ID)进行排序。
  3.   根据RowID 的排序顺序来访问实际的数据文件。

之所以称为 优化,就是因为  避免了 离散读取。

select * from  salaries  where salary > 10000  and  salary < 40000;

开不开 差 10倍。

Multi-Range Read 还可以将某些范围查询 ,拆分为键值 对, 来进行批量查询。如:

select * from t  where  key_part1 >=1000  and  key_part1 < 2000   and  key_part2 = 10000;

//优化器 会将 查询条件 拆分为(1000, 1000), (1001, 1000), (1002, 1000)…, (1999, 1000);

总是开启MRR:

mysql >  set @@optimizer_switch=‘mrr=on, mrr_cost_based=off’;

//查看缓存的大小

mysql > select @@read_rnd_buffer_size\G;

Index Condition Pushdown  ICP  优化;

msyql5.6 开始支持:  开启 ICP 后, 会在取出索引的同时,判断是否可以进行where 条件的过滤。

ICP 优化支持 range ,ref,  eq_ref,  ref_or_null  类型的查询。

如: 某表有联合 索引

开启 ICP  后 执行时间的对比 表5-5  todo;

哈希表:

一般来说都将关键字转换为自然树,然后通过除法散列表。  h(k)  = k  mod  m

例如: innodb_buffer_pool_size  的大小 为 10M ,则共有 640个  16KB的页。 对 哈希表来说 需要 640 X 2 = 1280个槽, 但不是质数,应该 是 1399;

在InnoDB 存储引擎 的  缓冲池中 对于其中的 页 是怎么进行查找的呢?

关键字  K=  space_id<<20  +  space_id  + offset;

自适应哈希:

hash 索引只能用来搜索等值的查询。范围查找是不能使用哈希索引的

select * from table where index_col=‘xxx’;

全文索引 【暂时不深入研究】

参考书: MySQL技术内幕:

猜你喜欢

转载自blog.csdn.net/u013862108/article/details/83721299