索引
索引的主要作用是加快数据的查找,但是索引本身也是需要存储的,由于索引文件可能很大,因此不能常驻内存,在用到的时候需要从硬盘种加载,常识性我们都知道,计算机io是非常耗时的操作,那么如何减少io便是索引设计的重点。
mysql的索引使用的是b+树(数据结构不详细介绍,请自行查阅资料),那么为什么要用b+树,不用b-树或者红黑树。假设树每访问一层需要一次io,那这样首先把红黑树给排除掉。再来对比b-tree和其变形b+tree,首先b-tree路由节点也可能保存数据本身,这就导致同样大小的路由节点保存的路由信息就会减少,一个节点的数据太大,一次io读取不了完整的节点信息,那么就需要多次io耗时增加。由于b+tree路由节点只保存路由信息,那么相同的空间能放更多的路由信息,这也就意味着更少的io。
mysql索引属于存储引擎,不同存储引擎实现方式不同。
MyISAM引擎使用b+tree作为索引,叶子节点data存放的是记录地址。MyISAM中主索引与辅助索引形式是一样的,主索引要求key不能重复,辅助索引key可以重复。这种索引方式叫非聚集索引。
InnoDB引擎,与MyISAM索引与数据分开存放不同的是,InnoDB引擎数据文件本身就是一个索引,InnoDB引擎数据存放是按b+tree结构组织存放的,叶子节点包含全部的数据信息,这个树的key就是主键,这样的设计会导致数据按照主键聚集,所以这种方式叫聚集索引,正是因为这样,InnoDB引擎要求表必须有主键,这样的设计可以使按主键的查找非常高效。
InnoDB引擎辅助索引叶子节点存放的是主键,正式这个设计,所以不建议主键使用长字段,因为会导致辅助索引data占用空间太大。
创建二张测试表
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
`user_id` int(11) NOT NULL AUTO_INCREMENT,
`user_name` varchar(255) NOT NULL,
`user_password` varchar(255) NOT NULL,
`phone` varchar(255) NOT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=113418 DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `t_details`;
CREATE TABLE `t_details` (
`details_id` bigint(8) NOT NULL AUTO_INCREMENT,
`details_name` varchar(255) DEFAULT NULL,
`details_phone` varchar(255) DEFAULT NULL,
`details_gender` varchar(255) DEFAULT NULL,
PRIMARY KEY (`details_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
写个函数往t_user表里插入2万条数据
begin /*开始*/
declare num001 , num002 int(11); /*定义num001和num002的数据类型为int*/
declare sessionid VARCHAR(50);
set num001 = 137 , num002 = 10020000; /*设置num001=137,num002=10000001*/
SET sessionid = 'CD9A5B83DB39E152CBE7EE';
while num002 > 10000000 do /*num002大于10000000时一直添加*/
insert into t_user(user_name,user_password,phone)
values(concat("克隆人" , num001) ,sessionid, concat(num001 , num002)); /*在t_user表中插入数据*/
set num002 = num002-1; /*设置num002=num002+1*/
end while; /*循环2万次*/
end
把t_user表数据同步到t_details中
insert into t_details(details_name,details_phone,details_gender)
select user_name,phone,'男' from t_user
测试了二次未加索引时平均查询时间44秒
现在对二张表分别加上索引
0.1秒中不到
在二个关联字段为纯数字类型时,加索引会提高很高效率
中间表
把t_user表和t_details表数据清空,手机号存VARCHAR类型的数据
继续用函数添加20万条t_user表数据
begin /*开始*/
declare num001 VARCHAR(50);
declare num002 int(11);
declare sessionid VARCHAR(50);
SET num001 = '13***';
SET num002 = 1200000;
SET sessionid = 'CD9A5B83DB39E152CBE7EE';
while num002 > 1000000 do /*num002一直大于0 所以无限循环添加*/
insert into t_user(user_name,user_password,phone)
values(concat("克隆人" , num002) ,sessionid, concat(num001 , num002)); /*在t_user表中插入数据*/
set num002 = num002-1; /*设置num002=num002+1*/
end while; /*当num<1000000时,不再执行插入操作循环结束*/
end
和上面一样,把t_user表数据同步到t_details中
通过details_phone = phone关联查询二张表的数据,本次直接加了索引后的开始
创建中间表
DROP TABLE IF EXISTS `user_details_ext`;
CREATE TABLE `user_details_ext` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL,
`phone_crc_32` bigint(20) NOT NULL,
`phone` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `phone_crc_32` (`phone_crc_32`) USING BTREE,
KEY `user_id` (`user_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=270332 DEFAULT CHARSET=utf8mb4;
t_user表中的phone转换成crc32存储到user_details_ext中
CRC是什么东西呢?
其实我们大家都不应该会对它陌生,回忆一下?你用过RAR和ZIP等压缩软件吗?它们是不是常常会给你一个恼人的“CRC校验错误”信息呢?我想你应该明白了吧,CRC就是块数据的计算值,它的全称是“Cyclic Redundancy Check”,中文名是“循环冗余码”,“CRC校验”就是“循环冗余校验”。
CRC有什么用呢?
在数据存储和数据通讯领域,为了保证数据的正确,就不得不采用检错的手段。
在诸多检错手段中,CRC是最著名的一种。
它的应用范围很广泛,最常见的就是在网络传输中进行信息的校对。其实我们大可以把它应用到软件保护中去,因为它的计算是非常非常非常严格的。严格到什么程度呢?你的程序只要被改动了一个字节(甚至只是大小写的改动),它的值就会跟原来的不同。所以只要给你的“原”程序计算好CRC值,储存在某个地方,然后在程序中随机地再对文件进行CRC校验,接着跟第一次生成并保存好的CRC值进行比较,如果相等的话就说明你的程序没有被修改/破解过,如果不等的话,那么很可能你的程序遭到了病毒的感染,或者被Cracker用16进制工具暴力破解过了。
CRC的原理。由于CRC实现起来有一定的难度,所以具体怎样用它来保护文件,留待下一节再讲。
insert into user_details_ext(user_id,phone_crc_32,phone)
select user_id,crc32(phone),phone from t_user
user_details_ext中加入索引
再次查询
然后我就懵逼了,为啥反而还慢了?可能crc32(details_phone)转换时占用的时间和数据量不够吧