1.mysql 的存储形式是b+tree 。
2.使用索引时机
1.哪些情况需要创建索引
1).主键自动建立唯一索引
2).频繁作为查询查询条件的字段应该创建索引
3).查询中与其它表关联的字段,外键关系建立索引
4).频繁更新的字段不适合创建索引
5).where条件里用不到的字段不创建索引
6).单键/组合索引的选择问题(在高并发下倾向创建组合索引)
7).查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度
8).查询中统计或者分组字段
2.哪些情况不要创建索引
1).表记录太少
2).经常增删改的表(因为不仅要保存数据,还要保存一下索引文件)
3).数据重复且分布平均的表字段,因此应该只为最经常查询和最经常排序的数据列建立索引。
注意:如果某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果。
二张表
性能优化:left join是由左边决定的,左边一定都有,所以右边是我们的关键点,建立索引要建右边边。当然如果索引在左边,可以用右连接。
1
2
select * from atable
left join btable on atable.aid=btable.bid; // 最好在bid上建索引
结论:
尽可能减少Join语句中的NestedLoop的循环次数:“永远用小结果集驱动大的结果集”
避免索引失效
2.最佳左前缀法则:如果索引了多列,要尊守最左前缀法则,指的是查询从索引的最左前列开始并且不跳过索引中的列。
3.不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描。
4.存储引擎不能使用索引中范围条件右边的列。
如这样的sql: select * from user where username='123' and age>20 and phone='1390012345',其中username, age, phone都有索引,只有username和age会生效,phone的索引没有用到。
5.尽量使用覆盖索引(只访问索引的查询(索引列和查询列致)),如select age from user减少select *
6.mysql在使用不等于(!= 或者 <>)的时候无法使用索引会导致全表扫描。
7.is null, is not null 也无法使用索引。
8.like 以通配符开头(‘%abc..’)mysql索引失效会变成全表扫描的操作。
所以最好用右边like 'abc%'。如果两边都要用,可以用select age from user where username like '%abc%',其中age是索引列
假如index(a,b,c), where a=3 and b like 'abc%' and c=4,a能用,b能用,c不能用
9.字符串不加单引号索引失效
10.少用or,用它来连接时会索引失效
11.尽量避免子查询,而用join
一般性建议
对于单键索引,尽量选择针对当前query过滤性更好的索引
在选择组合索引的时候,当前Query中过滤性最好的字段在索引字段顺序中,位置越靠前越好
在选择组合索引的时候,尽量选择可以能够包含当前query中的where子句中更多字段的索引
尽可能通过分析统计信息和调整query的写法来达到选择合适索引的目的。
left/right join注意
1).on与 where的执行顺序
ON 条件(“A LEFT JOIN B ON 条件表达式”中的ON)用来决定如何从 B 表中检索数据行。如果 B 表中没有任何一行数据匹配 ON 的条件,将会额外生成一行所有列为 NULL 的数据,在匹配阶段 WHERE 子句的条件都不会被使用。仅在匹配阶段完成以后,WHERE 子句条件才会被使用。它将从匹配阶段产生的数据中检索过滤。
所以我们要注意:在使用Left (right) join的时候,一定要在先给出尽可能多的匹配满足条件,减少Where的执行。如:
2).注意ON 子句和 WHERE 子句的不同
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
mysql> SELECT * FROM product LEFT JOIN product_details
ON (product.id = product_details.id)
AND product_details.id=2;
+----+--------+------+--------+-------+
| id | amount | id | weight | exist |
+----+--------+------+--------+-------+
| 1 | 100 | NULL | NULL | NULL |
| 2 | 200 | 2 | 22 | 0 |
| 3 | 300 | NULL | NULL | NULL |
| 4 | 400 | NULL | NULL | NULL |
+----+--------+------+--------+-------+
4 rows in set (0.00 sec)
mysql> SELECT * FROM product LEFT JOIN product_details
ON (product.id = product_details.id)
WHERE product_details.id=2;
+----+--------+----+--------+-------+
| id | amount | id | weight | exist |
+----+--------+----+--------+-------+
| 2 | 200 | 2 | 22 | 0 |
+----+--------+----+--------+-------+
1 row in set (0.01 sec)
从上可知,第一条查询使用 ON 条件决定了从 LEFT JOIN的 product_details表中检索符合的所有数据行。第二条查询做了简单的LEFT JOIN,然后使用 WHERE 子句从 LEFT JOIN的数据中过滤掉不符合条件的数据行。
1.永远小表驱动大表
在java程序里,两个for循环,循环次数不管谁先谁后都是两者次数相乘。
但在mysql的语句中,一定要小表驱动大表,因为小表跟Mysql连接和释放数量少
如in与exists
1
2
3
4
5
6
7
8
9
10
11
12
13
select * from A where id in (select id form B)
等价于
for select id from B
for select * from A where A.id=B.id
结论:当B表的数据集必须小于A表的数据集时,用in优于exists。
select * from A where exists (select 1 from B where B.id=A.id) // 这里的1用任何常量都行
等价于
for select * from A
for select * from B where B.id=A.id
结论:当A表的数据集必须小于B表的数据集时,用in索引优化分析 | liucw's blog链接1优于。
注意:A表与B表的ID字段应建立索引
exists通用语法select ... from table where exists (subquery)
该语法可以理解为:将主查询的数据,放到子查询中做条件验证,根据验证结果(true或false)来决定主查询的数据结果是否得以保留。
提示:
1).exists(subquery)只返回true或false, 因此子查询中select *也可以是select 1或select 'X', 官方说法是实际执行会忽略select 清单,因此没有区别。
2).exists 子查询的实际执行过程可能经过了优化而不是我们理解上的逐条对比,如果担忧效率问题,可进行实际检验。
3).exists 子查询往往也可以用条件表达式、其他子查询或者JOIN来替代,何种最优需要具体问题具体分析。
链接1
链接2