MySQL索引及SQL优化

先对索引做个大概回顾,然后我们详细探讨SQL优化

索引

索引的分类

主键索引

设定为主键后数据库会自动建立索引,innodb为聚簇索引

单值索引

即一个索引只包含单个列,一个表可以有多个单列索引【建议不要超过3】

唯一索引

索引列的值必须唯一,但允许有空值

复合索引

又称之为 组合索引、联合索引即一个索引包含多个列

最左原则(此原则只针对:复合索引)

(工号、名称、入职日期) 作为一个组合索引,将会生成的索引目录结构。由接口可以看出,工号是最先需要判断的字段,所以工号这个查询条件必须存在工号判断完,才会判断名称名称判断完才会判断入职日期

也就是说,组合索引查询条件必须得带有最左边的列:对于我们的索引:

  • 条件为: (工号,名称,入职日期) 这几种情况都是生效的
  • 条件为: (名称)不生效 (名称,入职日期)不生效
  • 条件为 (工号) (工号,名称)(工号,入职日期)部分生效
  • 条件为 (工号,名称,入职日期)全部生效

索引优缺点

索引的优点

  • 建立索引的列可以保证行的唯一性,生成唯一的rowId
  • 建立索引可以有效缩短数据的检索时间==【合理的建立索引】合理:user_code 不合理:user_sex ,全部字段都添加索引,数据量过小==
  • 建立索引可以加快表与表之间的连接查询 select * from oeder o left join user u on u.id = o.user_id where o.id="1010011"
  • 为用来排序或者是分组的字段添加索引可以加快分组和排序速度

索引的缺点

  • 创建索引和维护索引需要时间成本,这个成本随着数据量的增加而加大
  • 创建索引和维护索引需要空间成本,每一条索引都要占据数据库的物理存储空间,数据量越大,占用空间也越大(数据表占据的是数据库的数据空间)
  • 会降低表的增删改的效率,因为每次增删改索引需要进行动态维护,导致时间变长

索引的选择

那些情况下需要创建索引

  • 主键自动建立唯一索引,【自增、UUID、雪花算法】
  • 频繁作为查询条件内容差异化大的字段应该创建索引【where 后面的语句】
  • 查询中与其它表关联的字段,外键关系建立索引【on条件的字段】
  • 单键/组合索引的选择问题,who?【在高并发下倾向创建组合索引
  • 查询中分组、排序的字段,排序字段若通过索引去访问将大大提高排序速度【order by 字段 group by 字段】
  • 查询中统计字段 sum(字段)

哪些情况不要创建索引

  • 记录比较少,本身数据就少即使加上索引也不会有太大的提升,增删改时还需要对索引维护进行维护,反而增加了工作量
  • where条件里用不到的字段不建立索引,选择索引的时候不是越多越好【单表少于3个索引】
  • 经常增删改的表,索引提高了查询的速度,同时却会降低更新表的速度,因为建立索引后, 如果对表进行INSERT,UPDATE 和DELETE, MYSQL不仅要保存数据,还要保存一下索引文件
  • 数据重复的表字段如果某个数据列包含了许多重复的内容,为它建立索引 就没有太大在的实际效果,比如表中的某一个字段为国籍,性别,数据的差异率不高,这种建立索引就没有太多意义。

SQL优化

为什么要SQL优化

在应用开发的过程中,由于前期数据量少,开发人员编写的SQL语句或者数据库整体解决方案都更重视在功能上的实现, 但是当应用系统正式上线后,随着生成数据量的急剧增长,很多SQL语句和数据库整体方案开始逐渐显露出了性 能问题,对生成的影响也越来越大,此时Mysql数据库的性能问题成为系统应用的瓶颈,因此需要进行Mysql数据库的性能优化。

性能下降的表现:

  1. 慢查询造成页面无法加载
  2. 阻塞造成数据无法提交

性能下降的原因:

  • 查询语句写的不好,各种连接,各种子查询导致用不上索引或者没有建立索引,胡乱建立索引
    • 【1、不常用字段 2、内容差别不大的数据字 3、单表索引字段超过3个】
  • 建立的索引失效,建立了索引,在真正执行时,没有用上建立的索引
  • select * from order 
    select id,name.age from order

  • 关联查询太多join
    select id form ordero left join user u on o.user_id = u.id left join user_Info ui on ui.user_id = u.id where u.age >18 and ui.school ='人民大学'

    wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

  • 服务器调优及配置参数导致,如果设置的不合理,比例不恰当,也会导致性能下降,sql变慢
  • 系统架构的问题

通用数据库优化

SQL及索引优化

  • 根据需求写出良好的SQL,并创建有效的索引,实现某一种需求可以多种写法,我们就要选择一种效率最高的写法,这个时候就要了解sql优化。【explain】
  • sql优化的目的之一就是==减少中间结果集==,降低物理IO

数据库表结构优化

  • 根据数据库的范式,设计表结构,表结构设计的好坏直接关系到SQL语句的复杂度。order ==>userName ==>userId==>user ===>userName
  • 适当的将表进行拆分,原本需要join的查询只需要一张单表查询就可以了。
  • 合理的分库、分表

系统配置优化

内核优化:linux 基础之-高并发内核优化_王道长的编程之路的博客-CSDN博客

  • 大多数运行在Linux机器上,如tcp连接数的限制、打开文件数的限制、安全性的限制,因此我们要对这些配置进行相应的优化

硬件优化

  • 数据库主机的IO性能是需要最优先考虑的一个因素
  • 数据库主机和普通的应用程序服务器相比,资源要相对集中很多,单台主机上所需要进行的计算量自然也就比较多,所以数据库主机的CPU处理能力也是一个重要的因素。
  • 数据库主机网络设备(一般指网卡等)的性能也可能会成为系统的瓶颈。

插入数据

如果我们需要一次性往数据库表中插入多条记录,可以从以下三个方面进行优化。

insert

优化方案一

批量插入数据

Insert into tb_test values(1,'Tom'),(2,'Cat'),(3,'Jerry');

优化方案二

手动控制事务

start transaction;
insert into tb_test values(1,'Tom'),(2,'Cat'),(3,'Jerry');
insert into tb_test values(4,'Tom'),(5,'Cat'),(6,'Jerry');
insert into tb_test values(7,'Tom'),(8,'Cat'),(9,'Jerry');
commit;

优化方案三

主键顺序插入,性能要高于乱序插入。

大批量插入数据

如果一次性需要插入大批量数据(比如: 几百万的记录),使用insert语句插入性能较低,此时可以使 用MySQL数据库提供的load指令进行插入。操作如下:

可以执行如下指令,将数据脚本文件中的数据加载到表结构中:

-- 客户端连接服务端时,加上参数 -–local-infile
mysql –-local-infile -u root -p

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

-- 执行load指令将准备好的数据,加载到表结构中
load data local infile '/root/sql1.log' into table tb_user fields terminated by ',' lines terminated by '\n' ;

主键顺序插入性能高于乱序插入

主键优化

为什么主键顺序插入的性能是要高于乱序插入的 ?

数据组织方式

在InnoDB存储引擎中,表数据都是根据主键顺序组织存放的,这种存储方式的表称为索引组织表行数据,都是存储在聚集索引的叶子节点上的。而我们之前也讲解过InnoDB的逻辑结构图:

在InnoDB引擎中,数据行是记录在逻辑结构 page 页中的,而每一个页的大小是固定的,默认16K。 那也就意味着, 一个页中所存储的行也是有限的,如果插入的数据行row在该页存储不小,将会存储 到下一个页中,页与页之间会通过指针连接。

页分裂

页可以为空,也可以填充一半,也可以填充100%。每个页包含了2-N行数据(如果一行数据过大,会行溢出),根据主键排列。

主键乱序插入可能会产生页分裂!

页合并

当页中删除的记录达到 MERGE_THRESHOLD(默认为页的50%),InnoDB会开始寻找最靠近的页(前 或后)看看是否可以将两个页合并以优化空间使用。

order by优化

MySQL的排序,有两种方式:

Using filesort : 通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sort buffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序。

Using index : 通过有序索引顺序扫描直接返回有序数据,这种情况即为 using index,不需要 额外排序,操作效率高。

对于以上的两种排序方式,Using index的性能高,而Using filesort的性能低,我们在优化排序操作时,尽量要优化为 Using index。

group by优化

在分组操作中,我们需要通过以下两点进行优化,以提升性能:

A. 在分组操作时,可以通过索引来提高效率。

B. 分组操作时,索引的使用也是满足最左前缀法则的。

limit优化

分页查询时,如果执行 limit 2000000,10 ,此时需要MySQL排序前2000010 记 录,仅仅返回 2000000 - 2000010 的记录,其他记录丢弃,查询排序的代价非常大 。

优化思路: 一般分页查询时,通过创建覆盖索引能够比较好地提高性能,可以通过覆盖索引加子查询形式进行优化。

select * from tb_sku t , (select id from tb_sku order by id
limit 2000000,10) a where t.id = a.id;

count优化

  • MyISAM 引擎把一个表的总行数存在了磁盘上,因此执行 count(*) 的时候会直接返回这个 数,效率很高; 但是如果是带条件的count,MyISAM也慢。
  • InnoDB 引擎就麻烦了,它执行 count(*) 的时候,需要把数据一行一行地从引擎里面读出来,然后累积计数。

如果说要大幅度提升InnoDB表的count效率,主要的优化思路:自己计数(可以借助于redis这样的数 据库进行,但是如果是带条件的count又比较麻烦了)。

count用法

count() 是一个聚合函数,对于返回的结果集,一行行地判断,如果 count 函数的参数不是 NULL,累计值就加 1,否则不加,最后返回累计值。

用法:count(*)、count(主键)、count(字段)、count(数字)

按照效率排序的话,count(字段) < count(主键 id) < count(1) ≈ count(*),所以尽量使用 count(*)。

update优化

我们主要需要注意一下update语句执行时的注意事项。

update course set name = 'javaEE' where id = 1 ;

当我们在执行删除的SQL语句时,会锁定id为1这一行的数据,然后事务提交之后,行锁释放。

但是当我们在执行如下SQL时。

update course set name = 'SpringBoot' where name = 'PHP' ;

当我们开启多个事务,在执行上述的SQL时,我们发现行锁升级为了表锁。 导致该update语句的性能 大大降低。

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

总结

插入数据

insert:批量插入/手动控制事务/主键顺序插入

大批量插入:load data local infile

主键优化

主键长度尽量短/顺序插入

order by优化

using index:直接通过索引返回数据,性能高

using filesort:需要将返回的结果在排序缓冲区排序

group by优化

最好走索引,多字段分组满足最左前缀法则

limit优化

覆盖索引+子查询

count优化

性能:count(字段) < count(主键 id) < count(1) ≈ count(*)

uodate优化

尽量根据主键/索引字段进行数据更新

猜你喜欢

转载自blog.csdn.net/m0_73843666/article/details/130203767