保证数据完整性。
关注索引的两个点:树和有序(树可以定位索引的起点,有序可以定位索引的终点)
eg:建立一张表:
> create table t2(id int,name varchar(20),bir_th data, constraint primary key (id),constraint unique (name));
#对id列主键约束/id列作为主键,name列作唯一约束
> show create table t2 \G #看t2表建立时的语法
> show index from t2; #显示t2表的索引
> insert into t2 values(1,'skj','2011-11-12');
如果插入的新数据id列或者name列与已有的值相同,就不允许建立!!
> create table t3(id int,name varchar(20),bir_th data, constraint primary key (id),constraint foreign key (name) references t2 (name));
#给新建的t3表的name列建立外键索引,到t2表的name列。
给外表t3插入数据时,插入的name列的值必须是主表t2里已经存在的!!
对主表做delete时,外表是有影响的;
对主表update时,外表也是有影响的。
PS:级联删除:
删除主表的数据时,关联的从表数据也删除,则需要在建立外键约束的后面增加on delete cascade 或on delete set null,前者是级联删除,后者是将从表的关联列的值设置为null。
在主表删除一行数据并且从表有对主表的引用时:
一般将外键消灭,因为批量导入数据的时候要大量访问主表,而且会锁主表。
customer表引用了district表(区域表)和warehouse表(仓库):
select c.c_first,c.c_last,c.c_city,o.o_id,o_entry_d
from customer c,orders o
where o.o_d_id=c.c_d_id and o.o_w_id=c.c_w_id and o.o_c_id=c.c_id and c.c_id=1 and c_w_id=1;
select …
from 主表,外表
where 主表.主键=外表.外键(主外键关联条件)
and 主表的约束条件 | 外表的约束条件
没加 c_w_id=1 时 SQL的执行计划:
可以看到,走customer表时,没有走索引,而且访问的行巨大,必然执行起来慢。
explain 中id大的先执行,相同的按顺序执行
(因为customer表是三个列作主键索引。而我们只用了一个列,所以,再加一个试试)
加了 c_w_id=1 后:
很明显rows的值变小了很多,再执行,发现执行速度变快了好多!
其中一个表上总有严格的约束条件,这个表作为整个SQL的起点
针对这个模型,我们的索引优化规则:
select …
from …
where 主外键关系 and 限制条件 //交易系统来说,总是访问少量的数据
group by
having
order by
注意:一个sql,先执行from…where !!
作业:写出下面的SQL,审核索引,建立合适的索引
1、某一个用户所有的订单信息
SELECT c.c_first,c.c_last,c.c_cicy,c.c_credit,o.o_id,o.o_entry_id
FROM customer c,orders o
WHERE c.c_id=o.o_c_id AND c.c_d_id=o.o_d_id AND c.c_w_id=o.o_w_id AND c.c_first='5QFf7iBCexBVLSad' AND c.c_last='BARBARBAR';
然后explain看执行计划
发现改善了很多。
2、某一个用户所有的订单详情
3、某一个用户在某个仓库所有的订单以及订单详情
4、某一个地区的用户所有的订单和订单详情
5、某一个商品的库存信息
6、某一商品在某一仓库的商品信息
7、某一个物品在某一仓库的库存信息
8、某一个用户购买某一个商品的订单信息
在一个列上建立索引的几个必备条件:
建索引:
alter table customer add index i_customer_first on customer(c_first);
假如有三个列,按第一个列排序,第一列值相同的按第二列排序,第二列相同的按第三列排序,所以多列索引只有第一列是有序的!索引将选择性高的放到第一列!
有时使用a列作为where条件,有时用b列,有时用c列,有时用ac,有时用bc…问:怎样建索引?
错误方法:建立abc索引,这样只是对a做了索引!!
正确做法:ac建一个,bc建一个,c建一个,建立这三个即可。
对下面的sql添加了一条索引,大大提高了查询速度:
alter table customer add index (c_id);
使用索引去除排序:
在group by 分组列上怎样来消除分组聚合产生的排序?
答:在分组的列上建立索引,来消除分组聚合产生的排序行为
(重点!难点!)加order by null!!
新建索引列里面,包括主键列。
覆盖索引是select的数据列只用从索引中就能够取得,不必读取数据行,换句话说查询列要被所建的索引覆盖。
explain 时,最后的那列 useing index 说明使用了覆盖索引!
使用覆盖索引的情形:
select、group by、order by、having、where 的列都在索引里面,不会回表,资源消耗小。
同时满足过滤和覆盖索引:where条件中要包括索引的第一个列!(大幅提升性能!)
useing where ,usering index condition ,useing index
注意:一二同时出现,说明使用了覆盖索引,useing where不起作用!
select distinct e.c_d_id from customer e;
DISTINCT去重,访问全表,删除相同的行,隐含着排序,消耗资源。
解释5.6在多列索引中为什么不害怕中间有空列,5.5版本中害怕有空列。
5.5和5.6多列索引中,5.6不害怕中间有空列,5.5不仅仅害怕空列,还害怕非=号出现在前面的列中。
select * from customer where c_w_id<2 and c_id<5 and c_first like 'ab%';//只能将第一个列压入
select * from customer where c_w_id=1 and c_id<5 and c_first like 'ab%';//可以压入两个列
5.5中多列索引使用的限制很多,5.6解决了这个问题(icp)
5.5中最好将选择性最强的列放在最前面,但是要注意这个列必须被经常使用到!
explain select * from customer where upper(c_first)='ABC'; #全表扫描
explain select * from customer where c_w_id-1<2;
#全表扫描
1、select/delete/update,没有where条件只能全表扫描
往往是误写
delete from customer;
update customer set c_first='abc';
2、where条件上没有索引
3、多列索引没有被正确使用,因为where条件中没有写前导列(前缀列、引导列)
explain select * from customer where upper(c_first)='ABC'; #全表扫描
explain select * from customer where c_w_id-1<2;
#全表扫描
explain select * from t2 ignore index (i_t2_name) where name like 'xkj';
8、统计信息
mysql针对应用发送过来的SQL需要进行解析(parse)
解析步骤
===RBO:
select ...
from customer c,orders o,order_line ol
where ...
and c like c.c_first = 'abc'
and o.status like 'valid';
①表的连接顺序,按照书写顺序;
②对于有严格限制条件的表,顺序提到最前面;
③对于有多个限制条件的表,第一个表的限制条件为准;
④限制条件优先考虑=,like放在=号的后面
表的连接顺序:o c ol
===对于CBO:
①先根据规则库,过滤掉肯定不行的路径;
②对于可能行的执行路径,计算每一个执行路径的成本;
③选择一个最小的成本的路径作为执行计划。
===cbo解析的时候会用到:
①数据字典
②统计信息:表的行数、索引列的选择性(card)
③(不好的地方)mysql没有记录列值的倾斜程度(列值分布 直方图)(平均每个值占多少行)
=====手工修改rows和card统计信息,引导mysql走索引
①手工收集
analyze table tpcc1000.customer;
②没有手工收集则会自动收集
③自动存储:
innodb_stats_persistent=on
④变化量大的情况下,自动收集
innodb_stats_auto_recalc=on #则是开启了自动收集
innodb_stats_persistent_sample_pages
innodb_stats_sample_pages
⑤手工修改统计信息的rows和card(不建议)
计算一个列上的唯一值的数量以及数值
mysql5.6版本面对数据倾斜:
图中,发生了严重的数据倾斜。会宕机。
如上图, 对于pt_c 表,stat_name里的size=1代表只占一个数据页;
对于customer表,stat_name 里面的size=11440个数据页,1440*16K =表的大小;
n_leaf_pages是叶子节点,size-n_leaf_pages=树高。
这一点应当引起重视,也是开发中经常会犯的错误。
由于表的字段tu_mdn定义为varchar2(20),但在查询时把该字段作为number类型以where条件传给Oracle,这样会导致索引失效。
错误的例子:select * from test where tu_mdn=13333333333;
正确的例子:select * from test where tu_mdn=’13333333333’;数字列,不害怕隐式类型转换
字符列,非常害怕隐式类型转换
explain select * from customer where c_first = 123;
易踩的坑:
①存储数字的列被定义成了字符串列
②存储日期时间的列被定义成了字符串列
explain select * from t1 where s_data <> 'yes';
explain select * from customer where c_id <> 10;
② 单独的>,<,(有时会用到,有时不会)
explain select * from customer where c_id > 10;
explain select * from customer where c_id > 10000;
explain select * from t1 where s_data like 'no%';
not like效果不好,不管是%放在后面还是前面
explain select * from customer where c_id not in (1,2,3,4);
explain select * from customer where c_id in (1,2,3,4);
explain select * from t1 where s_data not in ('yes');
explain select * from t1 where s_data not in ('no');
explain select * from orders where o_entry_d = cast('2011-01-01 12:30:30' as time);
explain select * from orders where o_entry_d = cast('2011-01-01 12:30:30' as date);
explain select * from stock where s_dist_01 is null;
explain select * from stock where s_dist_01 is not null;