sql 索引优化

1.  mysql的执行流程:

客户端: 发送连接请求,然后发送增删改查sql语句进行执行

服务端: 

(1)  连接层: 在tcp协议下,提供和客户端连接的要求, 提供对线程并发的技术, 让多个用户登录到mysql中(show processlist; 可查看所有登录到数据库的用户进程)

(2) 服务器: 提供各种接口(增删改查) , 分析器组件会解析各种sql命令, 先查询缓存, 如果缓存里面有数据, 则直接返回, 如果没有再执行, 如果发现命令的执行效率很低, 则会提交给优化器(mysql query optimizer)组件进行优化, 然后再执行

(3) 存储引擎: 存取或者提取数据. Innodb支持事务处理, 行锁, 高并发.

(4) 日志文件: 产生binlog日志(二进制文件)

2. sql卡顿原因:

(1) 硬盘读写数据,io延迟高,sql语句性能低,导致sql执行的时间漫长

(2) 表中的数据没有任何索引, 并且数据量较大, 造成sql查询速度慢

查询命令编写顺序:

select...from...join on...where...group by...having...order by...limit

sql解析顺序:

from...join on...where...group by...having...select...order by...limit

3. 索引: 

索引概念: 是一种数状的数据结构(B树结构,分支节点>2) , 相当于字典的目录, 加快查询速度  

常用的树: B树, 二叉树, 红黑树, hash树

树节点: 根节点(最顶级节点), 分支节点(父节点, 子节点), 叶子节点(最后一层存储数据的节点), 树的高度(树的层级, 理想情况下是三层, 任何数据最多查询三次可得到结果, 支持百万级别的数据查询, 追求树的矮胖结构)

B树索引

(1) B+树模型: 在相邻的叶子节点上, 加入双向链表(指针), 当前叶子节点不仅存贮着数据, 还保存着上下两个节点的地址(小范围数据中, 加快查询速度)

(2) B* 树模型: 在相连的分支节点上, 加入双向链表(指针), 当前叶子节点不仅存储着数据, 还保存着上下两个节点的地址(大范围数据中, 加快查询速度)

B* 树索引

磁盘块(block) 也叫数据页, 存储数据最多为16K, Innodb 和MyISAM都是B+树结构

4.   Innodb 和MyISAM 的索引结构:

(1) 聚集索引: Innodb存储引擎的特点, MySIAM不支持

如果有主键, 自动以主键创建聚集索引的数据结构(数状结构), 如果没有主键, 选择唯一键, 如果都没有, 则自动生成隐藏的聚集索引, 构建一个占6个字节的长整形字段.

叶子节点上面直接存储真实数据(索引和数据捆绑在一起), 分支节点存储的是索引的最小值, 用来划分范围.

在数据量变大的时候, 尽量在树层级高度不变的情况下, 横向发展, 从而提升查询的效率, 减少io阻塞.

(2) 非聚集索引: (辅助索引, 二级索引, 普通索引)

先对创建索引的这个字段划分区间进行排序, 把索引值分布在叶子节点上.

存储的是该字段的值以及映射出的主键ID, 没有存真实数据, 通过ID再去其他文件中找对应的数据. 

(3) 两者区别: 

myisam 和 innodb 使用的索引结构都是b+树,但是叶子节点存储的数据不同

innodb文件结构中只有frm和ibd 直接把数据存在叶子节点上

myisam文件结构中有frm,myi,myd,叶子节点上存储的索引值,通过索引找id,通过id找数据.

(4) 性能优化:

利用索引查询时,可以增快查询速度,但是增删改速度变慢,会改变树状结构

追求尽量让叶子节点存储的数据类型小一点,让高度变矮,让数据页变少.

5. 索引
(1) 常用索引:
单个字段索引
-主键索引 primary key : 非空且唯一
-唯一索引 unique : 唯一
-普通索引 index : 单纯加个索引,为了提升查询效率
联合索引
primary key(字段1,字段2..) :联合主键索引
unique(字段1,字段2..) :联合唯一索引
index(字段1,字段2..) :联合普通索引

(2) 应用场景
编号:int
姓名:varchar(255)
身份证号:char(18)
电话char(11)
地址varchar(255)
备注:text
姓: varchar(10)
名: varchar(10)

编号: 主键
姓名: 普通索引(注意在区分度高的字段上加)
身份证:unique
电话:unique
备注:全文索引 , 借助第三方软件sphinx来运行
姓和名:联合索引 , 联合在一起查,加快速度

(3) 不同的存储引擎支持的数据结构
innodb : 支持b-tree fulltext 不支持hash类型索引结构
myisam : 支持b-tree fulltext 不支持hash类型索引结构
memory : 支持b-tree hash类型 不支持fulltext索引

hash类型索引: 数据放内存中,通过键来获取到值,单条数据查询快,一个范围内的数据慢
b-tree : 最理想的三层结构,理论上可支撑百万条数据的查询;

6. 建立索引: 三种方法

(1) 方法一: 建表的时候直接创建索引 index 索引名(索引字段)

create table t1(

id int primary key,

name char(10),

index index_id(id)

);

(2) 方法二: 建表之后, 直接创建索引 create index 索引名  on 表名(索引字段)

create table t2(

id int primary key,

name char(10)

);

create index index_id on t2(id);

(3) 方法三: 改字段变索引, alter table 表名 add index 索引名(索引字段)

create table t3(

id int primary key,

name char(10)

);

alter table t3 add index index_id(id);

(4) 删除索引  

drop index index_id on t3;

7. 正确使用索引:

(1)  在数据量大的情况下, 使用所以和不适用索引, 差别很大

未加索引的查询时间:

mysql> select * from s1 where id = 10000;
+-------+--------+--------+-----------------+------------+-------------+
| id | name | gender | email | first_name | last_name |
+-------+--------+--------+-----------------+------------+-------------+
| 10000 | rachel | women | rachel10000@liu | 刘10000 | 思敏10000 |
+-------+--------+--------+-----------------+------------+-------------+
1 row in set (1.28 sec)

alter table s1 add index index_id(id);  

添加索引之后的查询时间(加了索引之后, ibd文件会变大):

mysql> select * from s1 where id = 10000;
+-------+--------+--------+-----------------+------------+-------------+
| id | name | gender | email | first_name | last_name |
+-------+--------+--------+-----------------+------------+-------------+
| 10000 | rachel | women | rachel10000@liu | 刘10000 | 思敏10000 |
+-------+--------+--------+-----------------+------------+-------------+
1 row in set (0.01 sec)

(2) 把频繁作为搜索条件的字段作为索引,查单条数据,如果查询的是一个大范围中的数据,不能命中索引
表达范围的符号: > < >= <= != like between and .. in
select * from s1 where id > 5; # 表达一个大范围内的数据不能命中.
select * from s1 where id < 5; # 表达一个小范围内的数据可以命中.

(3) 选一个区分度较高的字段作索引
选区分度低的字段做了索引,在查询数据的时候,先走索引建好的树状结构,在把数据搜出来
因为树状结构中有大量的重复数据,会增加树的高度,反而速度不快,冗余数据过多

默认系统会把主键或者unique标识的约束,自动创建索引,因为区分度较高,没有冗余数据;

create index index_name on s1(name); # 不推荐把区分度不高的字段加索引

(4) 在搜索条件中,不能让索引字段参与计算,这样不能命中索引,会让查询时间变长
select * from s1 where id = 1000;
select * from s1 where id*3 = 3000; # id = 1000

  (5)  如果条件中含有and, sql语句会通过优化器进行优化

如果是and相连, 会找到第一个有索引的,并且树的高度最矮的字段进行优化

select count(*) from s1 where email = "rachel1000000@liu";                                1 row in set (1.28 sec)

select count(*) from s1 where email = "rachel1000000@liu" and id = 1000000;                     1 row in set (0.00 sec)

select count(*) from s1 where email = "rachel1000000@liu" and name = "rachel";                   1 row in set (13.14 sec)

select count(*) from s1 where email = "rachel1000000@liu" and name = "rachel" and id = 1000000;        1 row in set (0.01 sec)     

如果是or相连, 所有语句从左到右执行, 索引没有了作用

select count(*) from s1 where id = 1000 or name = "rachel";             1 row in set (18.45 sec)  

select count(*) from s1 where id = 1000 or email = "rachel1000@liu";       1 row in set (1.82 sec) 

(6) 联合索引: 遵循最左前缀原则, 被标记成MUL这个字段,必须存在在搜索条件中,就命中索引

drop index index_id on s1;

drop index index_name on s1;

create index union_index on s1(first_name,last_name);    

select count(*) from s1 where first_name = "刘1000" and last_name = "思敏1000";          1 row in set (0.00 sec)

select count(*) from s1 where first_name = "刘1000";                            1 row in set (0.00 sec)

 select count(*) from s1 where last_name = "思敏1000" and first_name = "刘1000";         1 row in set (0.00 sec)

select count(*) from s1 where last_name = "思敏1000";                            1 row in set (0.97 sec)   不能命中

select count(*) from s1 where first_name = "刘1000" and name = "rachel";               1 row in set (0.01 sec)

(7) 其他:

数据类型不匹配, 不能命中索引

select count(*) from s1 where first_name = 1000;              1 row in set, 65535 warnings (2.03 sec)  

使用了函数不能命中

select count(*) from s1 where reverse(first_name) = "0001刘";      1 row in set (1.02 sec)  

          

  

原文地址:https://www.cnblogs.com/fdsimin/p/13182481.html