为何索引这么快,一个好的索引能将检索速度提高几个量级,这种效率离不开这个数据结构node
为何须要"索引" ?
咱们总得依据什么才能去找你想查的东西,那么咱们就依据 id=1去寻找一条记录,怎么找呢? 难道是"顺序检索" ? sql
数据库里的东西如今大都大到分布在不少个磁盘页上。若是顺序检索基本就是噩梦。你知道"二分法" 比较快,那是由于数据已经被排过序了。 一样的若是如今存在一种排过序的数据结构,使得咱们能快速去找出咱们想要的东西在哪儿,这样必定很方便对吧。数据库
因此归根到底,如今最大的问题的就是"这条记录到底存在哪"。 解决办法是这样的:为何叫它"索引" ?
在上面的介绍中咱们已经看到了,实现快速找到内容的关键就是这个 "序号" 。这个所谓的序号就很是像"索引"这个名词bash
为何不用二分法查找?
理论上,二分法已是 O(logN), 很是快了。 实际上索引可能不少,多到你都没办法把索引所有加载到内存里,因此这些索引基本上都在磁盘里,依靠磁盘IO,分批次加载到内存里。数据结构
既然提到磁盘IO,就应该知道磁盘IO效率很是低,低到实际上就,若是谁能减小磁盘IO次数,谁就会是最好的索引实现方案。优化
1.2.1 Balance-Tree中的节点为何是这样子 ? 查询数据的过程ui
刚刚,咱们就走完了一个节点,咱们完成了一轮查找。每一轮开始以前咱们会先执行磁盘IO,把下一个节点对应内容从磁盘加载到内存里,而后再在组内查找,找获得就退出,找不到继续spa
根据上述结论,咱们也能发现一个重要结论: 既然咱们不能一次性把全部索引都加载到内存里,既然咱们要分批次作磁盘IO。那树的高度其实就是咱们IO的次数,那么矮树就会是最快的方案3d
func balance_tree_search (node *Node , key int) (*Data,error) {
if node == nil {
return nil,fmt.Errorf("最终也没能找到对应序号")
}
for _, pair := range node {
if (pair.Key == key){
return pair.Data,nil
}
if (pair.Key > key) {
// 考虑到节点内键值对是已经排序,从小到大的
// 那既然上个键值对不知足,这个键值对又过于大
// 那说明没有符合的,前往下一个节点
return balance_tree_search( pair.NextNode, key )
}
}
// 比节点内全部键值对都大,直接前往下一个
return balance_tree_search( pair.NextNode, key )
}
复制代码
1.2.2 Balance-Tree的插入过程指针
自调整的过程虽然很漫长,看起来也很麻烦,可是这个刚好是知足了BT的自调整性质
1.2.3 Balance-Tree的删除过程
1.2.4 Balance-Tree的定义
假设咱们定义出A+B+C做为索引列,哈希索引就是针对每一条记录计算出hash(A,B,C) 对应的值是这条记录存储的位置,哈希索引很是快,可是也有自身对应的一些弊端
2.2.1 哈希冲突
假设如今两条记录能哈希出同一个值,这种时候:
// 若是只依赖hash 则返回两条记录
SELECT * FROM users WHERE hash(name) = 1;
>> liangxiaohan 23 M
zhangxiaoming 24 F
// 最好的办法是不只使用hash同时也指定索引列自身的值
// hash冲突下,造成链表,存储引擎遍历链表全部行
SELECT * FROM users WHERE hash(name) = 1 and name = "liangxiaohan"
>> liangxiaohan 23 M
复制代码
2.2.2 自创索引
InnoDB支持哈希,但他的支持是指,它会自优化你的B树索引成为"某种程度上的"哈希索引。针对这一点,你能够本身实现一个简单的哈希索引
// 更新表,新建一列用于存放哈希值
ALTER TABLE ADD COLUMN name_crc VARCHAR(20)
// 关于哈希值,你可使用 TRIGGER 实现自动插入
// 你只负责插入name就好了,关于crc32哈希值它每次会本身计算
CREATE TRIGGER crc_create BEFORE INSERT ON users
FOR EACH ROW SET NEW.name_crc = crc32(NEW.name)复制代码
2.3.1 关于聚簇索引,你须要知道
2.3.2 聚簇索引的优势 & 缺点
背景: 获取主页面, 查询前10条记录,耗时2~3秒, 被描述为" 不可忍受"的时间
1.1 咱们目前使用了那些数据库索引?
MySQL[user] > SHOW INDEX FROM jobs;
*************************** 1. row ***************************
Table: jobs
Non_unique: 0
Key_name: PRIMARY <主键索引>
Seq_in_index: 1
Column_name: id
Collation: A
Cardinality: 13701
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
*************************** 2. row ***************************
Table: jobs
Non_unique: 1
Key_name: userId <惟一索引>
Seq_in_index: 1
Column_name: user_id
Collation: A
Cardinality: 105
Sub_part: NULL
Packed: NULL
Null: YES
Index_type: BTREE复制代码
1.2 目前语句的查询状况是怎样的?
MySQL [compile]>
EXPLAIN SELECT COUNT(j.id)
FROM
(SELECT * FROM jobs WHERE user_id = 123 AND deleted_at is NULL) j
LEFT JOIN
(SELECT * FROM builds WHERE deleted_at is NULL) b
ON
b.id = j.latest_build_id \G
*************************** 1. row ***************************
id: 1
select_type: PRIMARY
table: <derived2>
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 2
Extra: NULL
*************************** 2. row ***************************
id: 1
select_type: PRIMARY
table: <derived3>
type: ref
possible_keys: <auto_key0>
key: <auto_key0>
key_len: 4
ref: j.latest_build_id
rows: 1187
Extra: NULL
*************************** 3. row ***************************
id: 3
select_type: DERIVED -- 内嵌表
table: builds -- 表名
type: ALL -- 全表扫描,效率 ALL < index < range < ref < const
possible_keys: NULL -- 可能用到的索引
key: NULL -- 实际用到的索引
key_len: NULL
ref: NULL
rows: 118713 -- 预期扫描行数
Extra: Using where -- 使用Where作过滤,效率 filesort < temp < where < index
*************************** 4. row ***************************
id: 2
select_type: DERIVED
table: jobs
type: ref
possible_keys: userId
key: userId
key_len: 5
ref: const
rows: 1
Extra: Using where
MySQL [compile]> SELECT COUNT(j.id) FROM (SELECT * FROM jobs WHERE user_id = 4 AND deleted_at is NULL) j LEFT JOIN (SELECT * FROM builds WHERE deleted_at is NULL) b ON b.id = j.latest_build_id;
+-------------+
| COUNT(j.id) |
+-------------+
| 1280 |
+-------------+
1 row in set (0.93 sec)
复制代码
1.3 查询成本估计解读