mysql索引简单分析

索引对查询的速度有着至关重要的影响,理解索引也是进行数据库性能调优的起点。考虑如下情况,假设数据库中一个表有10^6条记录,DBMS的页面大小为4K,并存储100条记录。如果没有索引,查询将对整个表进行扫描,最坏的情况下,如果所有数据页都不在内存,需要读取10^4个页面,如果这10^4个页面在磁盘上随机分布,需要进行10^4次I/O,假设磁盘每次I/O时间为10ms(忽略数据传输时间),则总共需要100s(但实际上要好很多很多)。如果对之建立B-Tree索引,则只需要进行log100(10^6)=3次页面读取,最坏情况下耗时30ms。这就是索引带来的效果,很多时候,当你的应用程序进行SQL查询速度很慢时,应该想想是否可以建索引。

磁盘IO&预读

磁盘读取数据靠的是机械运动,每次读取数据花费的时间可以分为寻道时间、旋转延迟、传输时间三个部分,寻道时间指的是磁臂移动到指定磁道所需要的时间,主流磁盘一般在5ms以下;旋转延迟就是我们经常听说的磁盘转速,比如一个磁盘7200转,表示每分钟能转7200次,也就是说1秒钟能转120次,旋转延迟就是1/120/2 = 4.17ms;传输时间指的是从磁盘读出或将数据写入磁盘的时间,一般在零点几毫秒,相对于前两个时间可以忽略不计。那么访问一次磁盘的时间,即一次磁盘IO的时间约等于5+4.17 = 9ms左右。

考虑到磁盘IO是非常高昂的操作,计算机操作系统做了一些优化,当一次IO时,不光把当前磁盘地址的数据,而是把相邻的数据也都读取到内存缓冲区内,因为局部预读性原理告诉我们,当计算机访问一个地址的数据的时候,与其相邻的数据也会很快被访问到。每一次IO读取的数据我们称之为一页(page)。具体一页有多大数据跟操作系统有关,一般为4k或8k,也就是我们读取一页内的数据时候,实际上才发生了一次IO


B+树

如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,(内存时间因为非常短),通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找找到29,结束查询,总计三次IO。真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常非常高。

通过上面的分析我们知道,IO次数取决于树的高度,假设表中的数据为N,每页存储的数据量为m,如果N量一定则m越大数的高度越低IO越少,速度越快,所以我们索引的字段尽量小。

当B+TREE的数据项为符合结构的时候,例(name,age,city)B+TREE是从左往右查找,比如当(张三,20,上海)查找的时候,会先比较name来确定下来的搜索方向,如果name相同在依次比较age和city。
如果是(20,上海)这样子没有name的数据过来,B+树就不知道改查那个节点,因为简历搜索树的时候name是第一个比较因子,所以必须要先根据name来搜索才知道。这个也是最左前缀的逻辑

创建索引的原则

最左前缀原则,非常重要的原则,mysql会一直向右匹配知道遇到(> < between like)就停止,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到。

=和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式,b,d的顺序可以任意调整。

尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0。

尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可,因为创建(a,b)相当于创建了a索引,(a,b)索引

索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(’2014-05-29’);

补充:

mysql两种类型索引,主键索引也就是聚簇索引,数据记录就存在索引的最底层的叶子节点上,我们的表都会加一个主键。还有一种索引是二级索引或者说是普通索引,这个索引的最底层叶子节点上存的是对应行的主键索引值,所以通过非主键索引找到主键索引值,然后再去主键索引中找数据记录。 对于(A,B,C)组合索引怎么存储这个我不太清楚,对于查询中给出了a,b,c的值如果把a,b,c看成一个整体那么就和单列索引查找b树没有区别,如果只给出了a,应该也可以缩小查找范围不会全表扫描,再给出b再次缩小查找范围。

innodb操作磁盘的单位是否应该是innodb的页面大小,而不是磁盘块大小,也就是说索引数的叶子节点是一个innodb页面,如果要想索引树层次少,应该是要这个innodb的页面大,有些做法改了mysql源码把页面大小增大了,应该就是为了存更多数据同时保持索引数不会太高,提升性能,只是页面碎片变多,磁盘利用率下降。

原文地址:https://www.cnblogs.com/sidesky/p/6811852.html