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)