ORM(对象关系映射)只能产生合理的查询,而非常非常非常非常难产生适合索引的查询。
虽然叫它B-Tree索引,但是不同的存储引擎会使用不同的数据结构。有的使用T-Tree,而InnoDB使用B+Tree。
不同的存储引擎实现B-Tree的方式也各不相同。MyISAM使用前缀压缩技术使得索引更小,一页可以放下更多数据,InnoDB则是按原数据格式存储。MyISAM使用行的物理位置引用被索引的行,InnoDB使用行的主键。
使用了B-Tree索引,存储引擎不再需要扫全表来查询数据,而直接从树的根结点开始查询,对比索引值来找到子节点,从而找到对应数据或数据不存在。
根结点与叶子节点之间可能存在很多层节点页,取决于表的大小,InnoDB默认一页为16KB。
设有如下数据表:
create table people
(
last_name varchar(50) not null,
first_name varchar(50) not null,
brith date not null,
gender enum('m','f') not null,
name_brith_key(last_name,first_name,brith) # 三列的联合索引
);
last_name, (last_name, first_name)
(last_name, brith), (first_name, brith)
where last_name='fan' and first_name like '%a' and brith='1997-01-01'
。first_name使用了模糊匹配,所以brith不会使用索引查询。哈希索引基于哈希表实现,只适用于精确查询。
若使用了哈希索引,存储引擎会根据 索引列的值和主键,计算出一个哈希值,将哈希值存储下来,并将指向行的物理位置存储到哈希表中。
在mysql中,只有Memory引擎显示支持哈希索引。InnoDB为自适应性哈希索引,即当InnoDB注意到某些索引所用的非常频繁时,会在内存中基于B+Tree之上再创建一个哈希索引。
只需存储哈希值,存储紧凑,查询速度快
索引并不总是最好的工具。对于特别小的表,通常全表扫描更高效。对于中到大型的表,建立索引更高效。而对于非常大的表,建立和使用索引的代价将随之增长。
如果查询中的列不是独立的列,则Mysql不会使用索引。也就是若查询中含有表达式、函数式,则对该列不会使用索引。
select name from people where age + 1 = 20; 表达式
select name from people where to_days(current_date) - to_days(brith) <= 100; 函数式
有些时候,若我们想为很长的varchar和text等字段加索引,会让索引又大又慢。通常我们可以选择前缀索引,即索引前n个字符,这样可以大大节约空间,又提高效率。索引选择性即不重复的索引条数/数据条数。
如果确定n的大小呢?自然是又要节省空间,又要保证索引选择性。可以通过两种方式来选择n
1.
// 获得原来文本的重复度
select count(*) as cnt,city
from people
group by city order by cnt DESC limit 0,10;
// 试探前缀的文本重复度,若数据分布与原来文本相差不大,则ok
select count(*) as cnt, left(city,4) as pref
from people
group by pref order by cnt DESC limit 0,10;
2.
// 计算原来文本的索引选择性
select count(distinct(city)) / count(*) from people;
// 计算n个前缀的索引选择性,若相差不大,则ok
select count(distinct(left(city,4))) / count(*) from people;
// 添加前缀索引
alter table people add key (city,4);
对一张表,若不创建联合索引,而对多个列都创建单独的索引,对与大部分查询都不友好。
select city,name from people
where gender=1 or age=18;
在mysql<5.0时,对于以上的查询会进行全表扫描。而在mysql>=5.0时,mysql对这类查询会使用 索引合并,同时使用两个单列索引进行查询,并将结果合并,会浪费很多的cpu和内存资源在合并、排序上。有三种情况:OR条件的联合、AND条件的相交、组合前两种情况的联合和相交。
当我们在explain一条sql语句时,若extra字段出现了索引合并,证明索引建的非常糟糕。
若我们确定要为多个列加索引,那么索引的顺序是如何的呢?一般考虑如下几个方面。
若不考虑order by、group by、distinct操作,则选择性最高且过滤行数最多的,应该在前面。
若考虑order by、group by、distinct操作,由于B-Tree树建立是以索引顺序进行排序的,则应综合考虑。
聚簇索引不是一种索引类型,而是一种数据存储方式,其实就是一张表。不是所有的存储引擎都支持聚簇索引,InnoDB的聚簇索引,是一棵B+Tree,key为表的主键,叶子节点存储数据行。若该表没有主键,则会选择一个唯一非空索引,若也没有,则会隐式定义一个主键。
聚簇索引的意义,就是将数据按照某一字段聚集在一起,从而减少磁盘IO操作,使查询更快。
聚簇索引可能对性能有帮助,也可能导致严重的性能问题。
优点
缺点
对于二级索引而言:
按主键顺序插入行
如果正在使用的InnoDB表没有什么数据需要聚集,那么可以定义一个自增键作为主键。这样可以保证数据行是按顺序写入的,性能也会更好。
为什么自增主键性能会更好呢?因为自增主键会使数据按顺序写入,直接插入最后面即可,不会产生页分裂,若当前页满了,则直接写入下一页。
而如果是非自增主键,主键值 离散 且 分布范围很大。那么一页内的数据,主键不是连续的,如:一页内主键有1,5,18,此时有一个主键为4的数据行插入,则需要移动5,18位置,还有可能会产生页分裂。
因此,使用自增主键 与 使用离散且分布范围很大的主键相比,查询、插入等等性能是低效很多的。 id bigint(20) unsigned NOT NULL AUTO_INCREMENT (自增锁)
包含查询中所出现的所有字段(不仅仅只是where之后的字段) 的索引称为覆盖索引。覆盖索引可以让查询只需查找二级索引,而无需再查找聚簇索引。减少了数据的访问量,减少了缓存压力,提高了查询速度。
extra字段中的using index表示使用了覆盖索引;using where 表示索引覆盖了where条件。
MySIAM在B-Tree的key值存储上使用了前缀压缩技术,默认只压缩字符串。如:第一个key值为"fancy",第二个key值为"fancy cute",那么第二个存储为"5, cute"类似的形式。由此可见,前缀压缩使得每个索引都依赖前面的索引值,对倒序查询相当不友好,并且要花更多的时间,来换取将更多的索引加载到内存中。
mysql只有在访问行的时候才会为行加锁,索引可以减少存储引擎访问行的次数。
如果索引无法过滤掉行,那么存储引擎层会为读取的行都加锁,返回给服务器。在服务器过滤掉某一行之后,再释放锁,更早的版本甚至要到事务结束之后,再为所有行解锁。