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不是索引,对表进行加锁
相关推荐
余衫马41 分钟前
CentOS7 离线安装 Postgresql 指南
数据库·postgresql
E___V___E1 小时前
MySQL数据库入门到大蛇尚硅谷宋红康老师笔记 高级篇 part 2
数据库·笔记·mysql
m0_748254882 小时前
mysql之如何获知版本
数据库·mysql
小金的学习笔记2 小时前
如何在本地和服务器新建mysql用户和密码
运维·服务器·mysql
mikey棒棒棒2 小时前
Redis——优惠券秒杀问题(分布式id、一人多单超卖、乐悲锁、CAS、分布式锁、Redisson)
数据库·redis·lua·redisson·watchdog·cas·并发锁
星星点点洲3 小时前
【操作幂等和数据一致性】保障业务在MySQL和COS对象存储的一致
java·mysql
水手胡巴3 小时前
oracle apex post接口
数据库·oracle
_院长大人_4 小时前
Docker Mysql 数据迁移
mysql·adb·docker
史迪仔01126 小时前
【SQL】SQL多表查询
数据库·sql
Quz6 小时前
MySQL:修改数据库默认存储目录与数据迁移
数据库·mysql