如何构建高性能MySQL索引?
谢邀~
之前写过一篇关于针对开发人员数据库优化的文章,索引也是其中之一,那么今天就针对Mysql索引讲几点。
索引的类型及什么时候建立索引
说到MySQL的索引,大多数时候都是指B-Tree索引,M ySQL大部分引擎都是支持B-Tree索引的。B-Tree索引适用于全键值、范围、前缀的查找;
主键、外键必须有索引,当然很多系统都是逻辑外键(或需要经常和其他表关联),也需要建立索引;经常出现在where、order by、group by中的字段;尽量把索引建立到小字段上;对于文本字段或者很长字段,不要建索引;复合索引,文章第二部分再说明;
哈希索引,是基于哈希表,精确匹配索引所有列的查询才有效;只有Memory引擎支持。
全文索引、聚簇索引、聚簇索引等等,就不详细说了,因为...我也不太会,下面还是主要说B-Tree索引(后来说的索引,都是指B-Tree)。
联合索引的限制
很多同学都喜欢给多个字段建立联合索引,那么建立联合索引需要注意些什么呢:
索引的最左原则,如果不是按索引的最左列查找,那么将无法使用索引。最左原则:如果创建了一个联合索引(name,age,gender),相当于创建了三个索引(name)、(name,age)、(name,age,gender)。
联合索引,左边的列有范围查找,那么右边的列无法使用索引。比如index(age,gender),where age > 20 and gender = 'M';这时候就会有问题。解决办法也很简单,两个字段分别建立索引。
索引的一些小技巧
- 前导模糊查询,会导致索引失效:where name like '%三丰';
- 数据区分度不大,不建议使用索引:where gender = 'M';性别只有男、女、未知三种;
- 等号左边有函数,会索引失效:where LENGTH(col1) = 10;
- 隐式转换的问题:where col2 = '100',col2列是数字,等号左右类型不一致,col2会隐式转换成字符串;
- 尽量不好使用负向查询,例如:!=、not in、not exists;
- 索引不是越多越好。
我将持续分享Java开发、架构设计、程序员职业发展等方面的见解,希望能得到你的关注。
索引应该是各种数据库优化方案之中成本最低,见效最快的解决方案了,之前也发表过几篇关于索引原理的文章,这次就重点讲下:企业级应用如何构建高效索引,以及应该注意些什么。
索引类型
1、B-tree索引Myisam和innodb中,默认用B-tree索引,是一种平衡树。可以抽象一下---B-tree系统,可理解为"排好序的快速查找结构”
2、hash索引
在memory表里,默认是hash索引, hash的理论查询时间复杂度为O(1)
既然hash的查找如此高效,为什么不都用hash索引?
1:hash函数计算后的结果,是随机的,没有办法对范围查询进行优化.
2: 无法利用前缀索引. 比如 在btree中, field列的值“hellopworld”,并加索引
查询 xx=helloword,自然可以利用索引, xx=hello,也可以利用索引. (左前缀索引)
因为hash(‘helloword’),和hash(‘hello’),两者的关系仍为随机
3: 排序也无法优化.
4: 必须回行。就是说通过索引拿到数据位置,必须回到表中取数据
以下是建立索引时的注意事项
- 索引不是越多越好,单张表中索引数量不宜超过8个
- 合理创建联合索引,(a,b,c) 相当于 (a) 、(a,b) 、(a,b,c)
- 禁⽌冗余索引
索引是双刃剑,会增加维护负担,增⼤大IO压⼒力。(a,b,c)、(a,b),后者为冗余索引。可以利⽤用前缀索引来达到加速的目的,减轻维护负担
- SELECT只获取必要的字段,不建议使⽤ SELECT *
好处:减少网络带宽消耗;能有效利用覆盖索引,表结构变更对程序基本⽆无影响
- 合理使⽤用覆盖索引减少IO,避免排序
索引覆盖是指如果查询的列恰好是索引的一部分,那么查询只需要在索引文件上进行,不需要回行到磁盘再找数据。这种查询速度非常快,称为"索引覆盖”
理想的索引:1:查询频繁 2:区分度高 3:长度小 4: 尽量能覆盖常用查询字段
注:主键查询是天然的覆盖索引
- 尽量避免用NULL()
原因: NULL不利于索引,要用特殊的字节来标注。在磁盘上占据的空间其实更大。
索引的主键值,应尽量是连续增长的值,而不是要是随机值(不要用随机字符串或UUID),否则会造成大量的页分裂与页移动
不对过⻓的VARCHAR字段建⽴立索引。建议优先考虑前缀索引,或添加CRC32或MD5伪列并建⽴立索引
在常用的列上加上适当索引
例: where cat_id=3 and price>100 ; //查询第3个栏目,100元以上的商品
误: cat_id上,和, price上都加上索引.
错: 只能用上cat_id或Price索引,因为是独立的索引,同时只能用上1个
多列索引上,索引发挥作用,需要满足左前缀要求
- 选择区分度⼤大的列建⽴立索引。组合索引中,区分度大的字段放在最前
- 重要SQL必须被索引:update、delete的where条件列、order by、group by、distinct字段、多表join字段。
- 不建议使用%前导查询,例如:like“%abc”,无法利用到索引。
- 不建议使用负向查询,例如notin、!=、notlike。
- 使用EXPLAIN判断SQL语句是否合理使用索引,尽量避免extra列出现:Using File Sort、Using Temporary
第一步、
肯定要从业务背景开始出发啦。
比如业务信息是需要一个产品表,里面需要保存产品名称,产品库存,产品价格,产品分类,是否显示,备注信息。
转换成sql代码:
CREATE TABLE `product` (
`id` bigint(0) UNSIGNED NOT NULL AUTO_INCREMENT,
`productName` varchar(64) NULL,
`numCount` int(10) NULL,
`price` decimal(10, 2) UNSIGNED NULL,
`productClass` varchar(32) NULL,
`isShow` int(1) UNSIGNED NULL DEFAULT 1 COMMENT '0隐藏,1显示',
`remark` varchar(255) NULL,
PRIMARY KEY (`id`),
INDEX `web_show`(`productClass`, `isShow`),
INDEX `web_search`(`productName`, `numCount`, `Price`, `productClass`, `isShow`)
);
首先,web_show这个索引代表着可能页面会根据产品分类和是否显示进行分类查询,所以建立一个索引。
web_search 则代表在产品搜索页面,通过productName和numCount还有Price和productClass进行筛选,原则上isShow代表是否显示默认都需要的。
第二步、
若后期业务增加,或执行sql的方式不一样,通过desc select ****方式,来查看SQL执行过程,通过执行过程在对应地创建索引即可。
另外索引并非越多越好,需要合适地创建