mysql优化

MySQL性能分析

1.sql执行频率

show [session/global] status 可以提供服务器状态信息

通过如下指令可以查看当前数据库的insert,update,delete,select

sql 复制代码
show global status like 'Com_______' #一个下划线一个字符,共7个
2.慢查询日志
sql 复制代码
vi /etc/my.cnf

按下回车,G 按下字母i,进入编辑模式

到文本末尾添加

sql 复制代码
#开启慢日志查询
slow_query_log=1

#设置慢日志时间为2秒,sql语句执行时间查过2秒,就会视为慢查询,记录慢查询日志

复制代码
long_query_time=2
复制代码
然后按下esc,:wq保存退出

执行命令,重启mysql

复制代码
systemctl restart mysqld
复制代码
慢查询日志文件

此时里面会记录一些数据库的基本信息

执行一个命令,查看该文件的实时输出内容

复制代码
tail -f 192-slow.log  可能为localhost-slow.log

当sql语句执行时间过长时,会被文件记录下来

3.profile操作

sql语句耗时1.9秒时,不会被上面日志记录,

show profiles可以了解到时间耗费到哪,通过have_profiling,查看mysql是否支持profile操作

复制代码
select @@have_profiling;

默认profiling是关闭的,可以通过set语句在session/global级别开启profiling:

#查看每一条sql耗时情况

复制代码
show profiles;

#查看指定query_id的sql语句各个阶段的耗时情况

sql 复制代码
show profile for query query_id;

#查看指定query_id的sql语句cpu的使用情况

4.explain/desc

如图所示三张表

explqin执行计划各个字段含义:

id :select查询的序列号,表示查询中执行了select子句或者操作表的顺序(id相同,执行顺序从上到下;id 不同,值越大,越先执行)

因为 student与course没有直接关系,所以会先执行student_course

查询选修了mysql课程的学生,先执行course,然后student_course,第二个子查询的表,最后student

select_type:表示select的类型,常见取值有simpel(简单表,即不用表连接或者子查询),

primary(主查询,即外层的查询),union(union中的第二个或者后面的查询语句),subquery(select/where之后包含子查询)等

type:表示连接类型,性能由好到差的连接类型为null,system,const,eq_ref,ref,index,all

当访问时不查询任何表,才为null

system:访问系统表

const:根据主键唯一索引访问

ref:非唯一性索引

all:全表扫描

index:用了索引,会对索引扫描,遍历整个索引树

possible_key:显示可能应用在这张表上的索引,一个或多个

key:实际使用的索引,如果为null,则没有使用索引

key_len:表示索引中使用的字节数,该值为索引字段最大可能长度,并非实际使用长度,在不损失精确性的情况下,长度越短越好

rows:mysql默认必须要执行查询的行数,在innodb引擎的表中,是一个估计值,可能并不总是准确

filtered:表示返回的结果行数占需读取行数的百分比,filtered的值越大越好

extra:执行查询的过程中,前几个字段没有展示出来的值,会在这里显示

注:主要重点关注type,possible_key,key,key_len,extra

索引的使用

原则:
最左前缀使用法则:

若索引了多列(联合索引),要遵循最左前缀法则,指的是查询从索引的最左列开始,并且不跳过索引中的列,若跳过某一列,索引将部分失效(后面的字段索引失效)

如下跳过第一个字段,没有根据索引查找

如下跳过age,后面字段索引会失效

如下情况,依然会使用索引,只是要包含,与顺序无关

范围查询:

联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效

规避情况,可以使用>=

索引列运算:

不要再索引列上进行运算操作,索引将失效

字符串不加引号:

字符串类型字段使用时,不加引号,索引将失效

联合索引不加引号也会失效

模糊查询:

若尾部模糊匹配不会失效,头部模糊匹配还会失效

Or连接条件:

用or分割开的条件,如果or前的连接条件有索引,而后面列中没有索引,那么涉及的索引都不会生效

只有两侧都有索引才会生效

数据分布影响:

如果mysql评估使用索引比全表更慢,则不使用索引

Is null和is not null走不走索引还是根据数据分布情况

SQL提示:

是优化数据库的一个重要手段,就是在sql语句中加入一些人为的提示达到优化操作的目的

如下,由两个索引,使用的都是联合索引

Use index:(建议)

sql 复制代码
explain select * from tb_user use index(idx_user_pro) where profession = '软件工程';

Ignore index:(忽略)

sql 复制代码
explain select * from tb_user ignore index(idx_user_pro) where profession = '软件工程';

Force index :(强制)

sql 复制代码
explain select * from tb_user force index(idx_user_pro) where profession = '软件工程';
覆盖索引:

尽量使用覆盖索引(查询使用了索引,并且需要返回的列,在该索引中已经全部能够找到),减少使用select *(很容易出现回表查询)

前面一条sql语句执行效率高,user表中创建了联合索引,属于二级索引,下面sql中的name根据二级索引找不到

Using index condition:查找使用了索引,但是需要回表查询数据

Using where;using index:查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询数据

如下图就没有进行回表查询,是覆盖索引

前缀索引:

当前字段类型为字符串(varchar,text等),有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘IO,影响查询效率。此时可以只将字符串的一部分前缀建立索引,这样可以大大节约索引空间,从而提高索引效率

语法:

sql 复制代码
create index idx_xxx on table_name(cloumn(n));

前缀长度:

可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值,索引选择性越高则查询效率越高,唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的

可以根据以下计算:

sql 复制代码
select count(distinct email) / count(*)from tb_user; 
select count(distinct substring(email,1,10)) / count(*) from tb_user;

前缀索引查询流程:

先查辅助,然后聚集,查到之后并不是直接返回,而是拿到email的值与传递的一致不,如果是,拿到返回,然后辅助再查下一个链表,不是直接返回,若是,再去聚集查,最终组装数据返回

单列索引与联合索引:

在业务场景中,若存在多个查询条件,考虑针对与查询字段建立索引时,建议建立联合索引,而非单列索引

多条件联合查询时,sysql优化器会评估哪个字段的索引效率更高,会选择该索引完成本次查询

索引设计原则:
  1. 针对于数据量较大,且查询比较频繁的表建立索引

  2. 针对于常作为查询条件(where),排序(order by),分组(group by)操作的字段建立索引

  3. 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高

  4. 如果是字符串类型的字段,字段的长度较长,可以针对字段的特点,建立前缀索引

  5. 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率

  6. 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率

  7. 如果索引列不能存储null值,请在创建表时使用not null约束他,当优化器知道每列是否包含null值时,他可以更好的确定哪个索引最有效的用于查询

sql优化:

Insert:

批量插入:一次性建议500~1000个数据;上万条,可以多条插入500~1000

手动提交事务:mysql默认开启事务,频繁开关耗能

主键顺序插入

大批量插入数据:

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

#客户端连接服务端时,加上参数 --local-infile

sql 复制代码
mysql --local-infile -u root -p

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

sql 复制代码
set global local-infile = 1;

#执行load指令将准备好的数据,加载到表结构中

sql 复制代码
load data local infile '/root/mysql1.log' into table 'tb_user' fields terminated by ',' lines terminated by '\n';
load data local infile '/root/1110student.sql' into table student fields terminated by ',' lines terminated by '\n';

查看是否关闭

上传文件

如图所示共有1110条数据

查看文件中的格式

查看当前文件的路径

执行命令,添加数据

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

主键优化:

数据组织方式:

在InnoDB存储引擎中,表数据是根据主键顺序组织排放的,这种存储方式称为索引组织表(index organized table IOT)

页分裂:

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

乱序插入50,此时会创建一个新页,在第一个页的50%处,将超出50%的数据移入新页,将50插入到新页

然后重新设置链表指针。这种现象称为页分裂

页合并:

当删除一行记录时,实际上记录并没有被物理删除,只是记录被标记(flaged)为删除并且它的空间变得允许被其他记录声明使用

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

merge_threshold:合并页的阈值,可以自己设置,在创建表或创建索引时指定

主键设计原则:

  1. 满足业务需求情况下,尽量降低主键长度(二级索引中,叶子节点存放主键,如果主键过长,会占用空间,在搜索时,会耗磁盘IO)

  2. 插入顺序时,尽量选择顺序插入,选择使用auto_increment自增主键,减少页的分裂

  3. 尽量不要使用UUID做主键或者其他自然主键,如身份证号(UUID无序,长度长)

  4. 业务操作时,避免对主键修改

Order by优化:

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

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

  1. 根据排序字段建立合适的索引,多字段排序,也遵循最左前缀法则

  2. 尽量使用覆盖索引

  3. 多字段排序,一个升序一个降序,此时需要注意联合索引在创建时的规则(ASC/DESC)

  4. 如果不可避免的出现filesort,大数据量排序时,可以适当增大排序缓冲区大小sort_buffer_size(默认256k)

Backward index scan反向扫描

违背最左前缀法则会出现filesort

因为创建索引时是默认升序的,age相同,才按照phone升序排列,会出现filesort

解决方案,可以在创建索引时指定顺序

collation中A表示升序排列

Group by 优化:

可以使用索引,需满足最左前缀法则

Limit 优化:

覆盖索引+子查询

count 优化

MyISAM引擎把一个表的总行数存在了磁盘上,因此执行count(*)的时候会直接返回个数,效率很高,前提是没有where条件

InnoDB需要把数据一行一行的从引擎读出来,然后累计计数

优化思路:自己计数。用redis,插入时,把某个数加1,删除时减1

count( 主键 ):

InnoDB会遍历整张表,把每一行的主键id值都取出来,返回给服务层,服务层拿到主键后,直接按行进行累加(主键不可能为null)

count(字段):

无not null约束:遍历整张表,把每一行的字段值都取出来,返回给服务层,服务层判断是否为null,不为null,计数累加

有not null约束:遍历整张表,把每一行的字段值都取出来,返回给服务层,直接按行进行累加

count(1):(任何不为 null 的数字都可)

遍历整张表,但不取值,服务层对于返回的每一行,放一个数字1进去,直接按行进行累加

count(*):

并不会把全部字段取出来,而是专门做了优化,不取值,服务层直接按行进行累加

效率:count(字段)<count(主键)<count(1)=count(*)

update 优化

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

sql 复制代码
update student set name='ds' where id = 1 #id为主键,对行加锁

update student set name='ds' where name = 'sd' #name不是索引,对表进行加锁
相关推荐
小吴编程之路5 小时前
MySQL 索引核心特性深度解析:从底层原理到实操应用
数据库·mysql
~莫子5 小时前
MySQL集群技术
数据库·mysql
凤山老林5 小时前
SpringBoot 使用 H2 文本数据库构建轻量级应用
java·数据库·spring boot·后端
就不掉头发6 小时前
Linux与数据库进阶
数据库
与衫6 小时前
Gudu SQL Omni 技术深度解析
数据库·sql
咖啡の猫6 小时前
Redis桌面客户端
数据库·redis·缓存
oradh6 小时前
Oracle 11g数据库软件和数据库静默安装
数据库·oracle
what丶k6 小时前
如何保证 Redis 与 MySQL 数据一致性?后端必备实践指南
数据库·redis·mysql
_半夏曲6 小时前
PostgreSQL 13、14、15 区别
数据库·postgresql
把你毕设抢过来6 小时前
基于Spring Boot的社区智慧养老监护管理平台(源码+文档)
数据库·spring boot·后端