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;
如 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
语法:
USE index 只是告诉优化器可以选择索引, 实际上优化器还是根据自己的判断进行操作。 可以使用 FORCE index 来强制使用索引。
Multi-Range Read 优化 / MRR 优化;(InnoDB MyISAM 都支持)
MySQL 5.6 开始支持 MRR 优化; MRR 适用于 range, ref, eq_ref 类型 的查询。
MRR 的工作原理:/方式:
之所以称为 优化,就是因为 避免了 离散读取。
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技术内幕: