列选择原则:
整型 > date、time > char 、varchar > blob
因为整形、time类型运算快又节省空间;char、varchar要考虑字符集的转换与排序时的校对集(a B排序哪个优先)、速度慢;blob无法使用内存临时表,设计到排序必须在硬盘完成,速度慢。
因为大的字段浪费内存,影响速度,例如某字段varchar(30)能存下的内容,如果用varchar(100)的话,在联表查询时,varchar(100)要花跟多内存。
因为null不利于索引,要用多一个字节来特殊标注该字段值是否为null,在磁盘上占据多一个字节。一般声明字段时都带有not null define属性
enum列在内部是用整型来存储的
理想索引:
B-tree(多路搜索树,并不是二叉的)是一种常见的数据结构。使用B-tree结构可以显著减少定位记录时所经历的中间过程,从而加快存取速度。按照翻译,B通常认为是Balance的简称。这个数据结构一般用于数据库的索引,综合效率较高,常被用于对检索时间要求苛刻的场合。B-tree可以理解为已经排好序的快速查找数据结构。
btree的左前缀规则:
B-tree索引的误区:
在表中的常用的列都独立加上索引就以为常用的查询都完全用到索引了。例如user表中的gender和age列都独立加上索引,查询出表中大于60岁的女性的语句。
select * from user where gender=‘女’ and age>60;
注意:上面语句只能用上gender或age索引,因为是独立索引,同时只能用上一个,上面sql查询要完全使用索引,需要建立多列索引,例如:index(gender,age)
哈希索引包含以数组形式组织的Bucket集合。哈希函数将索引键映射到哈希索引中对应的Bucket,使用哈希索引必须要使用哈希集群。哈希索引可能是访问数据库中数据的最快方法(时间复杂度为O(1)),但它也有自身的缺点,只支持等值计算,不支持范围搜索或排序。
hash索引查找速度最快,为什么不用hash索引呢?
B-tree索引和hash索引区别:
聚簇索引:索引和数据混合一起,如InnoDB的id和数据是混合在一起的。有个缺点,当数据文件比较大时,查询数据时不断翻越磁盘扇区的页,使用时间会比较长。
回行:从索引定位到获取磁盘数据的过程,查询时回行时比较耗时,索引查找是快的,回行取数据是慢的。如果能直接从索引中获取数据,将会省去回行过程,提高查询速度,因为索引在内存运行的。
索引覆盖:查询的列恰好时索引的一部分,只在索引就能获取想要的数据,不需要回行到磁盘取数据。如果查询的数据能用到索引覆盖,速度是最快的。用explain 查询语句,看extra项目是否有Using index,如果有则使用到索引覆盖。
MyISAM的索引特点:指向的是数据在磁盘上的位置。
InnoDB的索引特点:
主键值为随机的插入行数据时,主键节点会分裂,对于MyISAM类型的表,影响不是很大,因为节点的包括的内容比较小(只有指向磁盘地址),在内存里完成,转移数据时耗时小;对于InnoDB类型的表影响比较大,因为表使用的聚簇索引,每个节点都包括行内容,节点分裂时需要转移的数据比较多,耗时也比较多。
高性能索引是使用自动递增的整型,例如定义主键类型id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT; 插入数据时不用指定主键值,让其有规律的自动增加,减少节点分裂的耗时。
聚簇索引排序慢原因分析
# 有一个表结构和引擎如下,有100000行数据。# 创建表
CREATE TABLE ts(id CHAR(30) PRIMARY KEY AUTO_INCREMENT,val INT NOT NULL DEFAULT 0,str1 VARCHAR(2000),str2 VARCHAR(2000),KEY idval(id,val)
)ENGINE innodb CHARSET utf8;# 查询速度慢
SELECT id FROM ts ORDER BY id;# 查询速度快
SELECT id FROM ts ORDER BY id,val;
查询速度慢的原因:表驱动为InnoDB,主键索引id属于聚簇索引,每个节点包含一行数据内容,而且字段内容比较大,储存时超越磁盘最小块,对主键id排序时会跨很多磁盘的数据块,导致速度很慢。
对联合索引排序快的原因:因为联合索引不是聚簇索引,节点内容很小(只有指向主键id地址),联合排序时使用了索引覆盖,不需要回行取数据,而且是在内存中完成,索引速度比较快。
当把str1和str2内容比较大的列删除后,两条语句执行速度差别不大。 如果表类驱动型为MyISAM两条语句执行速度差别不大。
在建立多列索引后,必须满足左前缀要求(从左到右按顺序,中间不能断开),索引才能发挥作用,例如多列索引index(a,b,c)
sql的where语句 | 索引是否发挥作用 |
---|---|
where a=1 | √ 使用a列 |
where a=1 and b=2 | √ 使用a、b列 |
where a=1 and b=2 and c=3 | √ a、b、c列都使用 |
where b=2 | × |
where c=3 | × |
where a=1 and c=3 | × |
where b=2 and c=3 | × |
where a=1 and b>2 and c=3 | √使用a、b列 |
where a=1 and b like ‘%2’ and c=3 | √ 使用a、b列 |
使用多列索引误区:查询哪个列索引都会发挥作用。
注意:多列索引一定要结合业务逻辑进行优化,例如查100~200元的男装商品,这条查询涉及到价格和商品栏目两列,可以把这两列作为一个多列索引。
在实际中的表的列有可能比较长,例如网址url的列,网址前面的几个字节http://www都是相同的,如果把url列作为索引,前面10个字节都是重复的,要区分网址至少截取16个字节以上,使得索引长度很长。不利于增删改查。
为了解决这个问题,在表中多添加一个url对应hash值的列(例如列名urlhash),即url值对应唯一hash值,然后使用列urlhash作为索引(整型),从而大大减少索引的长度,提高查询速度。
用大量数据分页优化说明延时索引技巧。 例如显示搜索结果有100000条,分页显示每页20条,sql语句为 SELECT filed FROM table LIMIT (N-1)*20,20;其中N表示第几页。
为什么要限制N的大小呢?因为limit的偏移量(offset)很大时,效率非常低,limit offset,num的工作机制是先查询,然后再跳过offset获取num条数据,当offset很大时,说明查询结果的数据很大,通过开启profile分析sql语句,发现大部分时间都用在Sending data(即回行传输的数据)上了,限制N的大小非常有必要,百度或谷歌都是现在查询结果的页数,一般都是看前几页搜索结果,极少人回去翻页到几十页去看搜索结果。
这种优化查询用到索引所以速度也很快,但是条件是要求数据完整性,不允许删除数据。否则会造成每次查询结果不一致,解决办法时不进行物理删除,用逻辑标记删除,最终在页面上显示时,逻辑删除的条目不显示结果即可。
首先查找取出主键id,然后再通过id来取数据,这种通过索引中间过程再从表中取数据过程叫延时索引,延时索引的好处时,节省了大量的回行时间(sending data时间),提高查询速度。
上面语句的工作过程是通过分页去取主键id,取主键id过程用到了索引覆盖,速度比较快,省去回行过程,获取id之后在回行取表中的内容,因为回行数量很少,sending data时间也少,索引查询速度也快,当数据量很大时,上面语句的据大多数时间都在取id过程上了。
当数据量很大时,利用延时索引比直接取数据速度要快4倍左右。
在某个列上可能存在多个索引,例如:
CREATE TABLE user(id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,first_name CHAR(5) NOT NULL DEFAULT '',last_name CHAR(10) NOT NULL DEFAULT '',KEY first_name(first_name),KEY last_name(last_name),KEY full_name(first_name,last_name)
)ENGINE myisam CHARSET utf8;
表中的列first_name既有独立作为索引,也有联合索引full_name,这叫冗余索引,实际中也会用到,单列查询时可以用到索引,多列查询时也可以用到索引。
注意index AB(A,B)和index BA(B,A)是不同的索引,对于列A或B来说,AB和BA属于冗余索引。
索引的长度和区分度是矛盾的,长度大区分度高,反之区分度小。到底选多少个字节作为索引长度比较合理呢,需要根据表内容的实际试验。 一般使用方法,针对表截取长度和区分度测试,例如一个中文单词表,共一万多行,有一个字到14个字的行都有。通过测试从1个词开始测试,使用sql语句SELECT count(distinct left(word,6))/count(*) FROM dict
;
对于一般系统,区别度能达到10%时,索引性能就可以接受。 针对列中的值,从左到右截取来建立索引:
对于带有排序的sql语句,例如下面语句,可能发生2种情况。
SELECT * FROM goods WHERE cat_id=3 ORDER BY shop_price DESC;
所以在碰到有排序的查询时,排序的列尽量作为索引(或联合索引),目的是取出来的数据本身就是有顺序的,避免排序这个耗时的过程,从而提高查询速度。
索引碎片的形成原因:在长期的数据更改中,索引文件和数据文件都将会产生空洞,形成碎片,索引碎片会对查询速度有影响。
维护索引碎片有两种方法:
# 修改表的nop操作,这个操作不影响表数据
ALTER TABLE 表名 ENGINE 驱动名;# 释放表空间
OPTIMIZE TABLE 表名# 注意:当数据比较大时,维护是很耗时间的,通常在访问量比较少的夜里维护,当数据修改不频繁可以按年来做维护,如果数据修改比较多可以按月来修复。