8.28笔记(mysql索引)

本文详细探讨了MySQL中的B+树索引构建、不同类型索引(如普通、唯一、主键、组合及前缀索引)及其在查询优化中的作用。涵盖了聚簇索引、辅助索引的创建与管理,以及索引失效、自优化和避免回表的策略。通过实例分析和性能测试,提供了选择和维护索引的实用建议。
摘要由CSDN通过智能技术生成
基于算法的索引类型(mysql都支持)
B树索引
Hash索引 
R树
Full text
GIS
基于功能(底层算法是b树)的索引类型:
1.普通索引
2.唯一索引
3.主键索引
4.组合索引
BTree树算法
遍历----二叉树----平衡二叉树----balance tree(多路二叉树)
b树方式:
针对底层数据生成上层的数据节点,包含下层的两个节点的起始数据
然后最后生成根节点包含上层节点的所有起始数据。
btree种类
B-tree
B+Tree 在范围查询方面提供了更好的性能(> < >= <= like)
范围类查找做了双向指针,底层数据直接跳转。
B*Tree b+tree的增强版
枝节点增加双向指针。减少io查询。



mysql B+树索引构建过程:
结构:

聚簇索引(innodb独有)
区===簇====64个pages 页===1m
聚簇索引(innodb独有)
区===簇====64个pages 页===1m
前提:
1。建表时指定了主键列。mysql innodb 将会把主键作为聚簇索引,
比如,ID,not null,primary key
2。没有指定主键,自动选择唯一键(unique)的列作为聚簇索引。
3。以上都没有,生成隐藏聚簇索引。
作用:
有了聚簇索引后,将来插入数据行,在同一区内,都会按照ID值顺序,有序在磁盘存储。
mysql innodb聚簇索引组织存储数据表。


辅助索引
在普通列上建立索引。需要人为创建
作用:
弥补聚簇索引欠缺的地方。非聚簇索引查询条件之外的优化。
alter table t1 add index idx(name);
辅助索引会单独查询一个列然后再回查聚簇索引。
辅助索引的类型:
单列:alter table t1 add index idx(name);
联合索引:alter table t1 add index idx(name,addr);
多个列作为索引条件,生成索引树,理论上设计的好的,可以减少大量的回表
查询
建立索引结构的时候存储索引最左列的值生成枝节点
查询条件必须有最左列,不写最左列是不走索引
建立联合索引时一定要选重复值少的列作为最左列。
例如索引 index(a,b,c) ---a, ab,abc,
select * from t1 where a= ...会走索引。
in也是可以走索引
部分覆盖
a= and b=
a=
a= and c=
a= and b >< >= <= like and c=
a= order by b
不覆盖
bc
c
b
CREATE TABLE department10 (
		dept_id INT,
		dept_name VARCHAR(30) ,
		comment VARCHAR(50),
		INDEX indep(dept_name)
		);
总结:选择联合索引。、
1,最左原则,一定选择查询频繁的列。查询语句中一定有该列。
2,选择重复行少的列作为最左联合索引。
3,查询语句中,如果不包含最左联合索引,会引起,联合索引失效。
前缀索引:
字符串过长,占用过多的索引空间。(索引树过高)
所以可以选择大字段前面部分字符作为索引生成条件。
导致索引应用的时候io次数过多。
MySQL中建议,索引树高度3-4层
alter table city add index idx_di(district(5));
B+Tree索引树高度影响因素:
1.索引字段过长:前缀索引。
2.数据行过多:分区表,归档表。(pt-archive),分布式架构(大企业)
3.数据类型:选择合适的数据类型,减少索引树的高度。
索引的管理命令:
什么时候该创建索引?
按照业务需求创建合适的索引。并不是把所有列创建索引。不是索引越多越好
将索引建立在经常where group by order by join on ...等查询条件上
为什么不能乱建索引?
冗余索引过多,表数据变化的时候会引起索引更新,会阻塞正常业务更新请求。
索引过多,会引起优化器出现偏差。
索引不能完全覆盖的情况下会出现回表,如果回表过多会产生大量I/O 导致iops增大。
随机io增大。
怎么避免或者减少回表?
1.将查询尽可能的id的主键列查询。
2.设置合理的辅助索引或者联合索引。(完全覆盖)(精确的查询条件)
3.
索引维护:
查询索引:desc 表名 key:pri聚簇索引, mul辅助索引 uni唯一索引
show index from 表


insert update delete 语句会引起聚簇索引立即更新
辅助索引不是实时更新。
在innodb的内存结构中,加入了insert buffer(会话),现在版本叫change buffer


建索引:
分析业务语句。
假设:select * ftom city where name="wuhan";
查询较多,用name字段做索引:
alter table city add index inx_na(name);
删除索引:
alter table city drop index inx_na;
修改一般删除重新创建
压力测试:
导入t100w.sql
命令:
mysqlslap --defaults-file=/etc/my.cnf  \
--concurrency=100(人数) --iterations=1 --create-schema='test'  \
--query="select * from test.t100w where k2='780p'" engine=innodb  \
--number-of-queries=200(查询200次) -uroot -p123 -verbose

mysqlslap --defaults-file=/etc/my.cnf  \
--concurrency=100 --iterations=1 --create-schema='test'  \
--query="select * from test.t100w where k2='780p'" engine=innodb  \
--number-of-queries=200 -uroot -p123 -verbose

建立索引:
alter table t100w add index idx_k2(k2);
再次运行进行对比。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++
什么是执行计划?
先对语句进行解析。然后生成执行方案。
分析的是优化器按照内置的cost(资源消耗)计算算法,最终选择后的执行计划。
cost?代价和成本,主要是cpu和io
io等级高于cpu,内存
查看执行计划:
执行语句前面增加desc或者explain

执行计划的显示结果分析:
table: city          ---->查询操作的表    
possible_keys: CountryCode,idx_co_po    ---->可能会走的索引 
key: CountryCode   ---->真正走的索引名字/    
key_len: 索引覆盖长度           
type: ref   ---->查询类型    全表扫描或者索引扫描
rows:  此次数据扫描的行数
Extra: Using index condition    ---->额外信息        
表:
查询多表时,精确到问题表。


type:
全表扫描:不用任何索引,ALL
从左到右性能依次变好.
all < index <range < ref < eq_ref < system,const
ALL  :  
全表扫描,不走索引
例子:
1. 查询条件列,没有索引
SELECT * FROM t_100w WHERE k2='780P';  
2. 查询条件出现以下语句(辅助索引列)
USE world 
DESC city;
DESC SELECT * FROM city WHERE countrycode <> 'CHN';
DESC SELECT * FROM city WHERE countrycode NOT IN ('CHN','USA');
DESC SELECT * FROM city WHERE countrycode LIKE '%CH%';
注意:对于聚集索引列,使用以上语句,依然会走索引
DESC SELECT * FROM city WHERE id <> 10;

INDEX  :
全索引扫描
1. 查询需要获取整个索引树种的值时:
DESC  SELECT countrycode  FROM city;

2. 联合索引中,任何一个非最左列作为查询条件时:
idx_a_b_c(a,b,c)  ---> a  ab  abc

SELECT * FROM t1 WHERE b 
SELECT * FROM t1 WHERE c  
可能会产生的问题:
(1) 每个索引都需要占用磁盘空间,索引越多,需要的磁盘空间就越大。
(2) 修改表时,对索引的重构和更新很麻烦。越多的索引,会使更新表变得很浪费时间。
(3) 优化器的负担会很重,有可能会影响到优化器的选择.
percona-toolkit中有个工具,专门分析索引是否有用
删除不再使用或者很少使用的索引(percona toolkit)
pt-duplicate-key-checker

表中的数据被大量更新,或者数据的使用方式被改变后,原有的一些索引可能不再需要。数据库管理
员应当定期找出这些索引,将它们删除,从而减少索引对更新操作的影响。

大表加索引,要在业务不繁忙期间操作

尽量少在经常更新值的列上建索引
建索引原则
(1) 必须要有主键,如果没有可以做为主键条件的列,创建无关列
(2) 经常做为where条件列  order by  group by  join on, distinct 的条件(业务:产品功能+用户行为)
(3) 最好使用唯一值多的列作为索引,如果索引列重复值较多,可以考虑使用联合索引
(4) 列值长度较长的索引列,我们建议使用前缀索引.
(5) 降低索引条目,一方面不要创建没用索引,不常使用的索引清理,percona toolkit(xxxxx)
(6) 索引维护要避开业务繁忙期
++++++++++++++++++++++++++++++++++++++++++++++++++
不走索引的情况:
1.没有查询条件,或者查询条件没有建立索引
select * from tab;       全表扫描。
select  * from tab where 1=1;
在业务数据库中,特别是数据量比较大的表。
是没有全表扫描这种需求。
1、对用户查看是非常痛苦的。
2、对服务器来讲毁灭性的。
(1)
select * from tab;
SQL改写成以下语句:
select  * from  tab  order by  price  limit 10 ;    需要在price列上建立索引
(2)
select  * from  tab where name='zhangsan'          name列没有索引
改:
1、换成有索引的列作为查询条件
2、将name列建立索引

2.查询结果集是原表中的大部分数据,应该是25%以上
查询的结果集,超过了总数行数25%,优化器觉得就没有必要走索引了。
与数据的预读能力有关。
假如:tab表 id,name    id:1-100w  ,id列有(辅助)索引
select * from tab  where id>500000;
如果业务允许,可以使用limit控制。
怎么改写 ?
结合业务判断,有没有更好的方式。如果没有更好的改写方案
尽量不要在mysql存放这个数据了。放到redis里面。

3.索引本身失效,统计数据不真实
索引有自我维护的能力。
对于表内容变化比较频繁的情况下,有可能会出现索引失效。
一般是删除重建

现象:
有一条select语句平常查询时很快,突然有一天很慢,会是什么原因
select?  --->索引失效,,统计数据不真实
DML ?   --->锁冲突

4.查询条件使用函数在索引列上,或者对索引列进行运算,运算包括(+,-,*,/,! 等)
例子:
错误的例子:select * from test where id-1=9;
正确的例子:select * from test where id=10;
算术运算
函数运算
子查询

5.隐式转换导致索引失效.这一点应当引起重视.也是开发中经常会犯的错误.
这样会导致索引失效. 错误的例子:
mysql> alter table tab add index inx_tel(telnum);
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0
mysql>
mysql> desc tab;
+--------+-------------+------+-----+---------+-------+
| Field  | Type        | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| id    | int(11)    | YES  |    | NULL    |      |
| name  | varchar(20) | YES  |    | NULL    |      |
| telnum | varchar(20) | YES  | MUL | NULL    |      |
+--------+-------------+------+-----+---------+-------+
3 rows in set (0.01 sec)
mysql> select * from tab where telnum='1333333';
+------+------+---------+
| id  | name | telnum  |
+------+------+---------+
|    1 | a    | 1333333 |
+------+------+---------+
1 row in set (0.00 sec)
mysql> select * from tab where telnum=1333333;
+------+------+---------+
| id  | name | telnum  |
+------+------+---------+
|    1 | a    | 1333333 |
+------+------+---------+
1 row in set (0.00 sec)
mysql> explain  select * from tab where telnum='1333333';
+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys | key    | key_len | ref  | rows | Extra                |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+

|  1 | SIMPLE      | tab  | ref  | inx_tel      | inx_tel | 63      | const |    1 | Using index condition |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
1 row in set (0.00 sec)
mysql> explain  select * from tab where telnum=1333333;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra      |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | tab  | ALL  | inx_tel      | NULL | NULL    | NULL |    2 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
mysql> explain  select * from tab where telnum=1555555;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra      |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | tab  | ALL  | inx_tel      | NULL | NULL    | NULL |    2 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
mysql> explain  select * from tab where telnum='1555555';
+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys | key    | key_len | ref  | rows | Extra                |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | tab  | ref  | inx_tel      | inx_tel | 63      | const |    1 | Using index condition |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
1 row in set (0.00 sec)
mysql>

6.<> ,not in 不走索引(辅助索引)
EXPLAIN  SELECT * FROM teltab WHERE telnum  <> '110';
EXPLAIN  SELECT * FROM teltab WHERE telnum  NOT IN ('110','119');

mysql> select * from tab where telnum <> '1555555';
+------+------+---------+
| id  | name | telnum  |
+------+------+---------+
|    1 | a    | 1333333 |
+------+------+---------+
1 row in set (0.00 sec)
mysql> explain select * from tab where telnum <> '1555555';

单独的>,<,in 有可能走,也有可能不走,和结果集有关,尽量结合业务添加limit
or或in  尽量改成union
EXPLAIN  SELECT * FROM teltab WHERE telnum  IN ('110','119');
改写成:
EXPLAIN SELECT * FROM teltab WHERE telnum='110'
UNION ALL
SELECT * FROM teltab WHERE telnum='119'

7.like "%_" 百分号在最前面不走
EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '31%'  走range索引扫描
EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '%110'  不走索引
%linux%类的搜索需求,可以使用elasticsearch+mongodb 专门做搜索服务的数据库产品
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
索引的自优化
优化参数:show variable "switch%"
如何修改:
1./etc/my.cnf
2.set global optimizer_switch='xxx参数=on';
3.hints 方式,用/*里面针对某个语句开启单个方法。

mysql的innodb引擎只有btree结构(会自适应hash)会将热点数据生成索引页的索引。
AHI作用:会自评估热点内存索引,page,生成hash,加快innodb快速读取索引页。
类似索引的索引。
change buffer
在做insert update,delete 操作时,辅助索引会临时更新存储到change buffer,
之后在使用的时候,会自动进行merge(合并操作)

ICP
索引下推。
解决了联合索引部分应用的情况。
减少没必要的数据页扫面。
index (a,b,c)假设部分索引符合:
select * from t1 where a=server层已经判断 and c=到引擎层才做判断。
将优化下推到引擎层进行优化。
再sever层先做a列过滤索引优化,再将c列的过滤下推到engine层做过滤,加载数据页。
 
MRR
mmuti range read 关闭mrr_cost_base,开启mrr
辅助索引---回表---聚簇索引
引擎层缓冲区转换为
辅助索引----sort id ---回表---聚簇索引(减少回表次数)
多路读取。


SNLJ
a join b on a.xx=b.yy where ...
伪代码:
for each row in a matching range (
    for each row in b (
	  a.xxx = b.yyy send to client
    )
)	
a叫驱动表,尽量驱动表数据行少些。
以上例子可以通过left join 强制驱动表
逐行a进行循环。

BNLJ
在SNLJ基础上,变成块嵌套循环,a表内数据变成数据块放到join buffer,再进行循环
一次性将驱动表内关联数据值与非驱动表进行匹配。
主要优化了cpu的消耗。减少了io消耗



BKA
在bnlj基础上,用来优化非驱动表的关联列,关联列有辅助索引。
开启方法:
先开启mrr,在开启bka
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值