最近有个需求,要修改现有存储结构,涉及查询条件和查询效率的考量,看了几篇索引和HBase相关的文章,回忆了相关知识,结合项目需求,说说本身的理解和总结。sql
错过的朋友能够先回顾下前3篇文章:数据库
上一篇详细介绍了explain命令,经过该命令,能够定位出在哪一步出现了性能问题,下一步就是经过优化索引来解决它。缓存
部份内容摘录了几个博友的文章,最后会给出文章连接,感谢他们的精彩分析。微信
复合索引遵照「最左前缀」原则,查询条件中,使用了复合索引前面的字段,索引才会被使用,若是不是按照索引的最左列开始查找,则没法使用索引。函数
好比在(a,b,c)三个字段上创建联合索引,那么它可以加快a|(a,b)|(a,b,c)三组查询的速度,而不能加快b|(b,a)这种查询顺序。post
另外,建联合索引的时候,区分度最高的字段在最左边。性能
不要在列上使用函数,这将致使索引失效而进行全表扫描。优化
例以下面的 SQL 语句:ui
select * from artile where YEAR(create_time) <= '2018';
复制代码
即便 date 上创建了索引,也会全表扫描,能够把计算放到业务层,这样作不只能够节省数据库的 CPU,还能够起到查询缓存优化效果。spa
负向条件有:!=、<>、not in、not exists、not like 等。
select * from artile where status != 1 and status != 2;
复制代码
可使用in进行优化:
select * from artile where status in (0,3)
复制代码
所谓覆盖索引,是指被查询的列,数据能从索引中取得,而不用经过行定位符再到数据表上获取,可以极大的提升性能。
能够定义一个让索引包含的额外的列,即便这个列对于索引而言是无用的。
当查询条件左右两侧类型不匹配的时候会发生强制转换,强制转换可能致使索引失效而进行全表扫描。
若是phone字段是varchar类型,则下面的SQL不能命中索引:
select * from user where phone=12345678901;
复制代码
能够优化为:
select * from user where phone='12345678901';
复制代码
范围条件有:<、<=、>、>=、between等。
范围列能够用到索引,可是范围列后面的列没法用到索引,索引最多用于一个范围列,若是查询条件中有两个范围列则没法全用到索引。
更新会变动B+树,更新频繁的字段创建索引会大大下降数据库性能。
「性别」这种区分度不大的属性,创建索引没有意义,不能有效过滤数据,性能与全表扫描相似。
区分度可使用 count(distinct(列名))/count(*) 来计算,在80%以上的时候就能够创建索引。
单列索引不存null值,复合索引不存全为null的值,若是列容许为 null,可能会获得不符合预期的结果集。
应该尽可能避免在 where 子句中使用 or 来链接条件,由于这会致使索引失效而进行全表扫描,虽然新版的MySQL可以命中索引,但查询优化耗费的 CPU比in多。
前导模糊查询不能使用索引,非前导查询能够。
MySQL 并非跳过 offset 行,而是取 offset+N 行,而后返回放弃前 offset 行,返回 N 行。
当 offset 特别大的时候,效率很是低下,要么控制返回的总页数,要么对超过特定阈值的页数进行SQL改写。
能够先快速定位须要获取的id段,而后再关联:
selecta.* from 表1 a,(select id from 表1 where 条件 limit 1000000 ,10 ) b where a.id=b.id
复制代码
虽然本身知道只有一条结果,但数据库并不知道,明确告诉它,让它主动中止游标移动。
where a=1 and b=1
where b=1
where b=1 order by time desc
复制代码
建议创建两个索引,即 idx_ab(a,b) 和 idx_b_time(b,time)
MySQL 的查询优化器会自动调整where子句的条件顺序以使用适合的索引,对于上面的第一条 SQL,若是创建索引为idx_ba(b,a) 也是能够用到索引的。
假若有联合索引(empno、title、fromdate),下面的 SQL 是否能够用到索引,若是能够的话,会使用几个?
select * from employee.titles
where emp_no between '10001' and'10010'
and title='软件工程师'
and from_date between '2008-01-01'and '2018-01-01'
复制代码
可使用索引,能够用到索引所有三个列,这个 SQL看起来是用了两个范围查询,但做用于empno上的between实际上至关于in,也就是说empno 实际是多值精确匹配。
在 MySQL 中要谨慎地区分多值匹配和范围匹配,不然会对 MySQL 的行为产生困惑。
假如创建联合索引(a,b,c),下列语句是否可使用索引,若是能够,使用了那几列?
where a= 3 // 是,使用了a列
where a= 3 and b = 5 // 是,使用了ab列
where a = 3 and c = 4 and b = 5 // 是,使用了 a,b,c 列
where b= 3 // 否
where a= 3 and c = 4 // 是,使用了a列
where a = 3 and b > 10 and c = 7 // 是,使用了 a,b 列
where a = 3 and b like 'xx%' andc = 7 // 是,使用了 a,b 列
复制代码
有以下查询语句,查找指定产品已审核(status=1)的评论:
SELECT user_id,title,content FROM `comment`
WHERE status=1 AND product_id=1
LIMIT 0,5 ;
复制代码
能够创建联合索引,status和product_id,可是哪一个放左边就要计算区分度:
SELECT COUNT(DISTINCT status)/COUNT(*) AS audit_rate,
COUNT(DISTINCT product_id)/COUNT(*) AS product_rate
FROM comment;
复制代码
通常product的区分度会高点,能够建立以下索引:
CREATE INDEX idx_productID_Status ON comment(product_id,status)
复制代码
查看某个用户最近20条登陆记录,按时间排序:
select * from login_history where uid = $uid order by create_time desc limit 20;
复制代码
创建uid+timeline复合索引,将排序引入到索引结构中,数据库负载骤降。
参考文章:
欢迎扫描下方二维码,关注个人我的微信公众号,查看更多文章 ~