MySQL索引以及SQL优化

索引:

索引是帮助MySQL高效获取数据的数据结构(有序)。在数据之外系统还维护这满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。

索引的数据结构选择

那么什么样得数据结构可以作为索引呢?这里分享一个数据结构可视化网站数据结构可视化

二叉树:

二叉树相比于链表,一般情况下可以降低链表的深度,从而提升查询效率。但是当我们顺序插入一段数据,二叉树就会退化为链表,并且当数据量大的时,树的深度也比较深,因此不选则二叉树。

红黑树:

红黑树解决了二叉顺序插入时退化为链表的弊端,但是仍然不能解决数据量大时深度较深的问题。

那么不妨通过增回一个结点存储多个元素的情况,这样数据量大就可以减少树的深度。

B树

b树解决了数据量大时树深度太深问题,但b树作为索引仍有以下弊端:

  • 非叶子结点即保存数据又保存关键字本身,占用空间比较大。
  • 各个叶子结点相互独立,各叶子节点独立存在,没有指针关联,通过关键字顺序查找和范围查找效率低。

B+树:

针对于b树的这些弊端,可以使用b+树,一个n阶的b+树具有以下特征:

  • 一颗m阶的B+树,每个结点至多有m棵子树
  • 非叶结点不保存数据,只保存用做索引的关键字,所有数据都保存在叶子结点中。
  • 各结点会存在数据冗余(出现重复关键字)
  • 各叶子节点通过指针链相连,便于通过关键字顺序查找和范围查找。

因此MySQL的数据库引擎InnoDB采用b+树作为索引。但是MySql中的B+树,对原来的B+树做了一个优化,每一个叶子结点都会指向相邻的叶子结点,提高了区间访问性能。

那么为什么不采用hash作为索引呢? 

Hash:

hash虽然在一般情况下具有比b+树更高的查询效率,但是仍有以下弊端:

  • 只能用于对等比较(=,in),不支持范围查询(between,> <)
  • 无法利用索引完成排序操作

以上说了为什么要使用b+树作为索引,下来说一说MySQL中索引的分类:

索引分类:

我们可以根据索引的特征以及存储位置将索引分为:

根据索引特征:

分类含义特点关键字
主键索引针对表中主键创建的索引默认自动创建只有一个PRIMARY
唯一索引避免表中同一列的数据值重复可以有多个UNIQUE
常规索引快速定位特定数据可以有多个
唯一索引查找文本中的关键字可以有多个FULLTEXT

根据存储位置:

分类含义特点
聚集索引索引和存储位置放在一起,叶子结点存储行数据必须有只能有一个
二级索引索引与存储位置分开,叶子结点存储与之关联的主键可以存在多个

注意聚集索引的选取规则:

  • 如果该表存在主键索引,那么就将该主键索引作为聚集索引。
  • 如果不存在主键索引,那么将选取合适的(第一个)唯一索引作为聚集索引。
  • 如果既不存在主键索引,又不存在唯一索引,那么InnoDB就自动生成rowId作为隐藏的聚集索引。

 SQL优化:

SQL优化,大部分情况下都是对于索引的优化,因此在SQL优化前,了解索引可以更好的帮助理解SQL优化。

前置知识:

索引的使用原则:

最左匹配原则:如果索引了多列(联合索引),要遵守最左前缀法则。最左前缀法则则指的是查询从索引的最左侧开始,并且不跳过索引中的列。如果跳跃某一列,索引将部分失效(后边的字段索引失效)。

注意:查询字段必读包含索引左边字段,但是与位置无关。当跳过索引其中一个字段,可能会导致索引部分失效。

范围储存:当查询条件包含 > <时候,后部分的索引将失效。

措施:将 > < 换成 >= , <=;

索引列运算:不要在索隐列进行运算,包含字符串截取。

字符串不加引号:索引会失效

模糊查询:以“%”开头的,“_”开头的索引失效。

or连接的条件:or两边都必须有索引,违反则索引失效。

数据分布影响:如果全表扫描比索引快,则索引失效。(使用索引比全表扫描还慢)

回表查询:

假设有数据库student表字段分别为(id,name,gender,birthday)其中id为主键,name建立普通索引。主键建立聚簇索引,将整条row存储到 叶子结点。name 为二级索引,将这条数据的主键放在叶子结点。

执行以下SQL语句:

select * from student where id = 2 ;
--此时走的是聚簇索引
select * from student where name = 'example';
--此时会出现回表查询,二次查询红黑树
select id,name, from student where name = 'example';
-- 只会走二级索引,不会回表查询

所谓回表查询就是指二次查询红黑树的情况,这样势必会降低查询效率,那么我们可以针对要查询的字段建立覆盖索引,针对上边的例子,针对name,和id建立二级索引。那么执行

SELECT id,name, FROM student where name = 'example';就不会回表查询。

前缀索引:

当我们查找条件是varchar或者text类型,此时交建立索引就会导致索引太大,从而造成空间浪费。

可以值针对文本的前部分建立索引,而不是针对整个文本。

选修长度时可以综合长度和选择性选取。

选择性 = 当前索引不重复字段总数/中行数,数值越大选择性越高。

语法:CREATE INDEX 索引名 ON 标名(字段名(截取长度))

CREATE INDEX idx_user_email_5 ON USER(email(5)):针对email前5个建立前缀索引。

单例索引和联合索引选择:

当我们的查询条件为多条件查询时,可以考虑使用联合索引。

联合索引选取得当可以避免回表查询,从而提升效率。

同时还需要注意联合索引中字段的顺序

索引设计原则:

  • 针对数据量较大,且查询比较频繁的表建立索引。
  • 针对常作为查询条件(where),排序(order by),分组(group by)操作的字段建立索引。
  • 尽量选取区分度高的作为索引,尽量建立唯一索引,区分度越高,效率越高。
  • 如果索引字段为长字符串类型,可以针对字符串的类型,建立前缀锁索引。
  • 尽量使用联合索引,减少单列索引,查询时,联合索很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率
  • 要控制表中索引的数量,索引并不是越多越好,维护索引的代价很大,影响增删改性能。
  • 如果索引能存储NULL值,在创建索引时使用NOT NULL约束。当优化器知道每列是否包含NULL值时,可以更好的确定哪个索引最有效地用于查询。

SQL优化:

INSERT优化:

  • 当一次添加多条数据时,可以使用批量插入。
  • 手动提交事务。(默认插入一条数据,提交一次事务)
  • 主键顺序插入。
  • 当需要插入大量数据(千万级条),可以使用load将本地文件导入到数据库。

连接数据库- mysql --local-infile -u root -p

设置全局参数local-infile为1,开启从本地加载文件导入数据的开关 set global local_infile = 1;

执行load指令:

load data local infile '/路径' into table ‘表名’fields terminated by ',' lines terminated by '\n'

主键优化:

数据组织方式:InnoDB中数据时根据主键顺序组织的,这种存储方式的表称为索引组织表。

InnoDB的逻辑组织形式:表,段,区,页,行。

页分裂:主键乱序插入可能出现的一种情况。

页合并: 当删除一条数据时,页的物理内存并没有被删除,只是被标记无用。当发现当前页可以被合并时会被合并。

设计原则:

  • 主键长度应该短
  • 主键应该采用自增插入。
  • 尽量不要使用UUID或者其他自然属性如身份证号。
  • 尽量不要对主键修改。

order by优化:

using filesort:表示索引排序时,是将数据通过全表扫描或者索引找到放入到缓存区中,然后在缓存区中进行排序,再返回。

using index:表示直接利用索引排序然后返回。

using index 排序效率高。

注意:在创建联合索引时,指定字段的排序(默认是ASC);

设计原则:

  • 根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀法则。
  • 尽量使用覆盖索引
  • 多字段排序时,一个升序,一个降序,此时需要注意联合索引在创建时的规则。
  • 如果不可避免的出现filesort,大数据量排序时,可以适当增大缓冲区大小sort_buffer_zize(默认为256K)

group by优化:

  • 在分组操作时,可以通过索引来提升效率。
  • 分组操作时,索引使用依然满足最左前缀法则。

limit优化:

当查询的页数太大时,可以使用覆盖索引+子查询优化。

例如:当我们有张表student(id,name,birthday,place;该表中有1000000多条记录。当我们查询

select * from student limit 99980,10;
--此时的查询效率很低,可以优化为
select s.* from student,(select id from student limit 99980,10) a where s.id = a.id;
--将a表独立看成一张表,采用联合查询

count优化:

  • count(主键):InnoDB引擎会遍历整张表,把每一行的主键id值取出来,返回给服务层。服务层拿到主键后,直接按行进行累加(主键不可能为null)。
  • count(字段):没有not null约束:InnoDB引擎会遍历整张表把每一行的字段都取出来,返回给服务层,服务层判断是否为null,不为null,计数累加。没有not null约束:InnDB引擎会遍历整张表的每一个字段值都取出来,返回给服务层,直接按行进行累加。
  • count(1):InnDB引擎遍历整张表,但不会取值。服务层对于返回的每一行,放一个数字“1”进去,直接按行进行累加。
  • count(*):InnoDB引擎并不会把全部字段取出来,而是专门做了优化,不取值,服务层直接按行进行累加。
  • 按照效率排序:count(字段)《 count(主键)《 count(1)≈ count(*)

update优化

InnoDB的行锁是针对索引加的锁,不是针对记录加的锁,并且该索引不能失效,否则会将行锁升级为表锁,降低并发性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值