昨天做一个企业的笔试题,对数据库这块了解很浅,所以还是记录一下吧。
B-Tree 索引和 Hash 索引的对比
对于 B-tree 和 hash 数据结构的理解能够有助于预测不同存储引擎下使用不同索引的查询性能的差异,尤其是那些允许你选择 B-tree 或者 hash 索引的内存存储引擎。
B-Tree 索引的特点
B-tree 索引可以用于使用 =, >, >=, <, <= 或者 BETWEEN 运算符的列比较。如果 LIKE 的参数是一个没有以通配符起始的常量字符串的话也可以使用这种索引。
有时,即使有索引可以使用,MySQL 也不使用任何索引。发生这种情况的场景之一就是优化器估算出使用该索引将要求 MySql 去访问这张表的绝大部分记录。这种情况下,一个表扫描可能更快,因为它要求更少量的查询。但是,如果这样的一个查询使用了 LIMIT 来检索只是少量的记录时,MySql 还是会使用索引,因为它能够更快地找到这点记录并将其返回。
Hash 索引的特点
Hash 索引有着与刚才所讨论特点的相比截然不同的特点:
Hash 索引只能够用于使用 = 或者 <=> 运算符的相等比较(但是速度更快)。Hash 索引不能够用于诸如 < 等用于查找一个范围值的比较运算符。依赖于这种单值查找的系统被称为 “键-值存储”;对于这种系统,尽可能地使用 hash 索引。
优化器不能够使用 hash 索引来加速 ORDER BY 操作。这种类型的索引不能够用于按照顺序查找下一个条目。
MySql 无法使用 hash 索引估计两个值之间有多少行(这种情况由范围优化器来决定使用哪个索引)。如果你将一张 MyISAM 或 InnoDB 表转换成一个 hash 索引的内存表时,一些查询可能会受此影响。
查找某行记录必须进行全键匹配。而 B-tree 索引,任何该键的左前缀都可用以查找记录。
最左前缀原则
通过实例理解单列索引、多列索引以及最左前缀原则
实例:现在我们想查出满足以下条件的用户id:
mysql>SELECT `uid` FROM people WHERE lname`=’Liu’ AND `fname`=’Zhiqun’ AND `age`=26
因为我们不想扫描整表,故考虑用索引。
单列索引:
ALTER TABLE people ADD INDEX lname (lname);
将lname列建索引,这样就把范围限制在lname=’Liu’的结果集1上,之后扫描结果集1,产生满足fname=’Zhiqun’的结果集2,再扫描结果集2,找到 age=26的结果集3,即最终结果。
由 于建立了lname列的索引,与执行表的完全扫描相比,效率提高了很多,但我们要求扫描的记录数量仍旧远远超过了实际所需 要的。虽然我们可以删除lname列上的索引,再创建fname或者age 列的索引,但是,不论在哪个列上创建索引搜索效率仍旧相似。
2.多列索引:
ALTER TABLE people ADD INDEX lname_fname_age (lame,fname,age);
为了提高搜索效率,我们需要考虑运用多列索引,由于索引文件以B-Tree格式保存,所以我们不用扫描任何记录,即可得到最终结果。
注:在mysql中执行查询时,只能使用一个索引,如果我们在lname,fname,age上分别建索引,执行查询时,只能使用一个索引,mysql会选择一个最严格(获得结果集记录数最少)的索引。
3.最左前缀:顾名思义,就是最左优先,上例中我们创建了lname_fname_age多列索引,相当于创建了(lname)单列索引,(lname,fname)组合索引以及(lname,fname,age)组合索引。
注:在创建多列索引时,要根据业务需求,where子句中使用最频繁的一列放在最左边。
拓展:在网上看到一个关于最左前缀原则提出这么一个例子。
多列字段做索引,state/city/zipCode,想要索引生效的话,只能使用如下的组合
state/city/zipCode
state/city
state
其他方式(如city,city/zipCode),则索引不会生效
这种现象是怎么导致的?和索引的存储方式有关吗?
本人页参考了下其他网友的观点,个人认为,所谓最左前缀原则就是先要看第一列,在第一列满足的条件下再看左边第二列,以此类推。有位网友描述得很形象:
你可以认为联合索引是闯关游戏的设计
例如你这个联合索引是state/city/zipCode
那么state就是第一关 city是第二关, zipCode就是第三关
你必须匹配了第一关,才能匹配第二关,匹配了第一关和第二关,才能匹配第三关
你不能直接到第二关的
索引的格式就是第一层是state,第二层才是city
索引是因为B+树结构 所以查找快 如果单看第三列 是非排序的。
多列索引是先按照第一列进行排序,然后在第一列排好序的基础上再对第二列排序,如果没有第一列的话,直接访问第二列,那第二列肯定是无序的,直接访问后面的列就用不到索引了。
所以如果不是在前面列的基础上而是但看后面某一列,索引是失效的。大家有不同的观点可以提出,这是个人理解的观点。
sql优化
http://blog.csdn.net/zly9923218/article/details/51007554
这里面讲解的很好。
以下转自:https://blog.csdn.net/wx145/article/details/82839419
背景知识:
- mysql中可以使用explain关键字来查看sql语句的执行计划。
- 最左前缀原则主要使用在联合索引中
- 数据库版本Mysql5.5.53
1.首先准备如下测试数据表
CREATE TABLE `student` (
`id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`cid` int(11) DEFAULT NULL,
`school` char(20) NOT NULL DEFAULT '',
KEY `name_cid_INX` (`name`,`cid`,`school`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into student values
(1,'weixin',12,'ningbo'),
(2,'weixin',13,'ningbo'),
(3,'weixin',14,'ningbo');
2.我看了很多网上对前缀原则的说明,例如abc联合索引,只有当a或者ab或者abc为条件时才能触发索引,这当然是毋容置疑的,但是我测试了发现ac也是会触发联合索引的,这是为什么呢?
我把重点放在了key_len这一项上,key_len是指索引类型的字节长度,那么上面768是怎么计算得到的呢?name列申明的是255,而且字符集是utf8,因此初步计算可以得到值为255*3=765,由于varchar有一个特性就是超过255(应该也包括255)会额外使用两个字节记录长度,因此765还需要加上2,也就是767,又因为name列为null,所以mysql需要1个字节来标识NULL,因此可以计算得出最后结果为768。同理cid列可以计算得到为4+1=5,school列计算得到为20*3=60,那么一个联合索引按照key_len的定义上理解的话,name_cid_INX应该是768+5+60=833,但是为什么ac条件下触发了联合索引,key_len却只是768,网上查了资料发现联合索引可以使用部分,这也是为什么要遵循前缀原则的原因。鉴于联合索引可以使用部分的原因,猜测ac条件之所以触发了联合索引是因为它与单独使用a条件相同,为此对比只使用a条件的情况,具体如下:
发现key_len与ac条件下一致,即验证猜测。接下来我们依次看一下ab和abc条件使用联合索引的情况:
根据以上表述,我们可以得出如下结论:当使用abc会完全使用联合索引的abc三列,使用ab只会使用联合索引中的两列,使用a或者ac只会使用联合索引中的a列,至于其他情况联合索引不会被使用。
3.注意点:细心的读者可能会发现,我们插入的测试数据name都是"weixin"但是查询的时候却没有使用name="weixin"而是使用了name="weixi",原因就是当name="weixin"时命中了表中的大部分数据,mysql查询优化器认为全表扫描比索引索引扫描效率更高而选择了全表扫描,如下:
还有关于mysql的查询优化器还有一点需要注意,sql语句中字段的顺序不需要和联合索引中定义的字段顺序一致,查询优化器会自己调整顺序: