btree所有的节点都存储数据,而b+tree只有叶子节点才存储数据
而内存每次加载数据的大小是有限的,而有数据的时候,加载的量就会很小,如果都是索引,就会加载更多的索引值
mysql> show global status like 'Innodb_page_size';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| Innodb_page_size | 16384 |
+------------------+-------+
1 row in set
所以在InnoDB中B+树高度一般为1-3层,它就能满足千万级的数据存储。
在查找数据时一次页的查找代表一次IO,所以通过主键索引查询通常只需要1-3次IO操作即可查找到数据。
之所以Btree能加快访问数据的速度,是因为存储引擎不再需要进行全表扫描
只需要从索引的根节点开始进行搜索,根节点的槽中存放了指向子节点的指针,
存储引擎会根据这些指针向下层查找,而这些指针实际上定义了子节点页中值的上限和下线
比如:
create table people(
xing varchar(20) not null,
ming varchar(20) not null,
birthday date not null,
gender enum('male','unknow','female') not null,
key(last_name ,first_name,birthday)
)
索引如果查询多条数据,也是可以排序的
1. 如果不是按照最左列开始查找,无法使用索引
例如无法用于查找 ming为XXX的,也无法查找 birthday为XXX的
ABC三个组成的索引,只能A 、AB、 ABC
其它的组合都是无法用到索引的
2. 如果中间有一列是范围查询,则之后的列不能用于索引查询
比如
WHERE A =XXX AND B like 'XXX%' and C =XXX
这个索引只能用到前两列
在InnoDB上面创建HASH索引
我们利用
先查询当前数据库的版本
mysql> select version();
查询当前实际表结构
show create table peopleHash ;
mysql> show create table peopleHash ;
| peopleHash | CREATE TABLE `peoplehash` (
`xing` varchar(20) COLLATE utf8mb4_general_ci NOT NULL,
`ming` varchar(20) COLLATE utf8mb4_general_ci NOT NULL,
`birthday` date NOT NULL,
`gender` enum('male','unknow','female') COLLATE utf8mb4_general_ci NOT NULL,
KEY `ming` (`ming`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci |
结果并没有出现我们想要的USING HASH ,也就是说创建HASH索引失败
为什么呢?
下面是MYSQL官方的回答:
1.不支持HASH索引(但是InnoDB在内部利用哈希索引来实现其自适应哈希索引功能。)
2.也就是InnoDB会根据表的使用情况自动为表生成hash索引,不能人为干预是否在InnoDB一张表中创建HASH索引
3.或者说,如果InnoDB注意到某些索引值被使用的特别频繁时,
它会在内存中基于Btree的索引之上再创建一个HASH索引,这样BTREE索引也具备了HASH索引的一些优点
但是也没有支持HSAH索引的引擎呢?
答案是肯定的:
MEMORY引擎是支持的
1. HASH索引本身只存储对应的HASH值和行指针,而不是存储字段值
2. HASH索引并不是按照索引顺序来存储的,因此无法排序
3. HASH索引不支持部分索引列查找,因为HASH索引是使用全部的内容来计算HASH值的
例如在(A,B)两列建立索引,只查询A无法使用索引
4.HASH索引只支持等值比较查询,包括 =、IN()不能进行任何的范围查询
5. 最严重的是既然HASH值是数字,肯定会出现相同的,也就是HASH冲突
6. 出现HASH冲突的时候,存储引擎必须遍历链表中所有的行指针,逐行进行比较,直到找到所有符合条件的行
7. 当HASH冲突特别多的时候,维护操作的成本就会变大,比如一次数据的删除
引擎需要遍历对应HASH值链表上的每一行,找到并删除对应的引用,冲突越多,代价越大
比如只有两个字段 ID 和URL
我们想查询URL是什么的 select id from XXX where url ='http://XXXXXXXX'
如果用BTREE来存储URL,则需要用到大量的空间,那我们还想用索引,怎么办呢?
删除URL原来的索引,新增一个字段 url_crc 使用CRC32做HASH
select id from XXX where url ='http://XXXXXXXX' and url_crc =CRC32('http://XXXXXXXX')
1.只要在插入和更新数据的时候,对url_crc进行更新就可以了
2. 但是如果用这个方式,记住不要用SHA1() MD5()作为HASH函数
3. 因为那两个函数的hash值非常长,会浪费大量的空间,但是如果数据量非常大,CRC32()会出现大量冲突
4. 自定义64位的哈希函数
简单的做法是:用MD5()函数返回值的一部分作为自定义哈希函数
select CONV(RIGHT(MD5('http://XXXXXXX'),16),16,10)AS HASH64;
插入和更新的时候都可以用这个了
insert into XXX
(id,url,url_crc) values
(XXX,XXX ,CONV(RIGHT(MD5('http://XXXXXXX'),16),16,10));
当然不是
1.对于数据量特别小的表,全表扫描更高效,没必要索引
2.中到大型的表,索引很高效,没有的话,速度很低
3.超大型表,由于数据量大,索引使用的代价会很大,这种情况下可以使用
分区技术
teacherNo
使用到了索引,检索到teacherNo 时候,可以直接将索引中的teacherNo 值返回,不需要进入数据区。Select teacherNo from teacher where teacherNo = ?
按照原页面中50%的数据量进行分裂,针对当前这个分裂操作,3,4记录保留在原有页面,5,6记录,移动到新的页面。最后将新纪录7插入到新的页面中;
50%分裂策略的优势:
分裂之后,两个页面的空间利用率是一样的;如果新的插入是随机在两个页面中挑选进行,那么下一次分裂的操作就会更晚触发;
50%分裂策略的劣势:
空间利用率不高:按照传统50%的页面分裂策略,索引页面的空间利用率在50%左右;
分裂频率较大:针对如上所示的递增插入(递减插入),每新插入两条记录,就会导致最右的叶页面再次发生分裂;
5. 传统50%分裂的策略,有不足之处,如何优化?接着往下看。
6. B+树分裂操作的优化?
由于传统50%分裂的策略,有不足之处,因此,目前所有的关系型数据库,包括Oracle/InnoDB/PostgreSQL,都针对B+树索引的递增/递减插入进行了优化。经过优化,以上的B+树索引,在记录6插入完毕,记录7插入引起分裂之后,新的B+树结构如下图所示:
对比上下两个插入记录7之后,B+树索引的结构图,可以发现二者有很多的不同之处:
新的分裂策略,在插入7时,不移动原有页面的任何记录,只是将新插入的记录7写到新页面之中;
原有页面的利用率,仍旧是100%;
优化分裂策略的优势:
索引分裂的代价小:不需要移动记录;
索引分裂的概率降低:如果接下来的插入,仍旧是递增插入,那么需要插入4条记录,才能再次引起页面的分裂。相对于50%分裂策略,分裂的概率降低了一半;
索引页面的空间利用率提高:新的分裂策略,能够保证分裂前的页面,仍旧保持100%的利用率,提高了索引的空间利用率;
优化分裂策略的劣势:
如果新的插入,不再满足递增插入的条件,而是插入到原有页面,那么就会导致原有页面再次分裂,增加了分裂的概率。
官方解读