原语句(金融云trade库)
SELECT
sum( amount )
FROM
tra_trade
WHERE
STATUS = 'success'
AND partner_id != '*********************00008'
AND partner_id != '*********************00074'
AND partner_id != '*********************00009'
AND date_format( update_time, '%y' ) = date_format( now( ), '%y' );
存在问题
1.存在!=,而且是3个
2.存在对时间函数的隐式转换
分析
1.date_format存在函数的隐式转换,无法优化
2.status列和partner_id列的选择性分别只有0.0000003和0.0001
3.连续3个!=,相当于not in
结论
对于索引列,及时创建联合索引也不会走;
实际测试也是如此;
所以在前面3.13的记录里面,认为无法对其进行优化。
转折
忽略了查询列,查询列的选择性达到了0.11
创建索引列和查询列的联合索引
ALTER TABLE `trade`.`tra_trade` ADD INDEX idx_status_partnerid_amount (`status`, `partner_id`, `amount`);
通过执行计划获知扫描行由24W行变成了4W行!
延伸
索引准则有一句:
不等空值还有or,索引失效要少用
这里很明显存在不等,反过来如果是3个等于的话,那么很明显也会存在or,但是索引确没有失效!
首先需要明白这句话对应的条件
tra_trade表是trade库中一张行数为24W的表,其中id,gid,trade_no选择性为1,partner_id选择性为0.0001
create table tra_trade_test as select id,gid,merchant_order_no,trade_no,amount,seller_user_id,payer_user_id,trade_name,status,partner_id
from tra_trade;
create index idx_test_0 on tra_trade_test(gid);
create index idx_test_1 on tra_trade_test(trade_no);
create index idx_test_2 on tra_trade_test(partner_id);
语句1
explain
select * from tra_trade_test where id>5000 and gid in ('*********************113e48fc','*********************113e48fe','*********************9b795a0','*********************113e4909');
毫无意外的走索引,即使索引列的2个条件都是范围查询,扫描行数仅仅为4行
删除gid索引
explain
select * from tra_trade_test where id>5000 and gid in ('*********************113e48fc','*********************113e48fe','*********************9b795a0','*********************113e4909');
仍然走索引,扫描行数暴增到12W,key_len只有8,说明只有id走了索引,此时虽然gid上面没有了索引,但走(id的)索引代价仍然比全表要小
语句2
将之前删掉的索引补回去
create index idx_test_0 on tra_trade_test(gid);
explain
select * from tra_trade_test where id>5000 or gid in ('*********************113e48fc','*********************113e48fe','*********************9b795a0','*********************113e4909')
执行计划中的type类型是index_merge,扫描行数为12W,index_merge的意思是索引执行了合并,这是因为我们此时应该把id和gid组成联合索引,这样效率会更高;但此时也没有走全表
删除gid索引
explain
select * from tra_trade_test where id>5000 or gid in ('*********************113e48fc','*********************113e48fe','*********************9b795a0','*********************113e4909')
毫无疑问的走全表了(所以可以看出,准则适用的其实是这里)
语句3
将之前删掉的索引补回去
create index idx_test_0 on tra_trade_test(gid);
explain
select * from tra_trade_test
where gid = '*********************113e48fc'
or gid = '*********************113e48fe'
or gid = '*********************9b795a0'
or gid = '*********************113e4909';
该语句等效于
explain
select * from tra_trade_test where gid in ('*********************113e48fc','*********************113e48fe','*********************9b795a0','*********************113e4909');
毫无疑问的走了范围索引(可以看出,单列上存在索引,无论是否存在or,都会走索引,准则所说的情况并非这类语句)
关于not in(!=)
语句4
explain
select gid from tra_trade_test
where gid != '*********************113e48fc'
and gid != '*********************113e48fe'
and gid != '*********************9b795a0'
and gid != '*********************113e4909';
explain
select id from tra_trade_test
where gid != '*********************113e48fc'
and gid != '*********************113e48fe'
and gid != '*********************9b795a0'
and gid != '*********************113e4909';
上述两条都走了索引
explain
select trade_no from tra_trade_test
where gid != '*********************113e48fc'
and gid != '*********************113e48fe'
and gid != '*********************9b795a0'
and gid != '*********************113e4909';
trade_no列上也建有索引,而且选择性为1,但是这条语句走了全表;
网友评论