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优化器会评估哪个字段的索引效率更高,会选择该索引完成本次查询
索引设计原则:
-
针对于数据量较大,且查询比较频繁的表建立索引
-
针对于常作为查询条件(where),排序(order by),分组(group by)操作的字段建立索引
-
尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高
-
如果是字符串类型的字段,字段的长度较长,可以针对字段的特点,建立前缀索引
-
尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率
-
要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率
-
如果索引列不能存储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:合并页的阈值,可以自己设置,在创建表或创建索引时指定
主键设计原则:
-
满足业务需求情况下,尽量降低主键长度(二级索引中,叶子节点存放主键,如果主键过长,会占用空间,在搜索时,会耗磁盘IO)
-
插入顺序时,尽量选择顺序插入,选择使用auto_increment自增主键,减少页的分裂
-
尽量不要使用UUID做主键或者其他自然主键,如身份证号(UUID无序,长度长)
-
业务操作时,避免对主键修改
Order by优化:
using filesort:通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sort buffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫FileSort排序
using index:通过有序索引顺序扫描直接返回有序数据,这种情况即为using index,不需要额外排序,操作效率高
-
根据排序字段建立合适的索引,多字段排序,也遵循最左前缀法则
-
尽量使用覆盖索引
-
多字段排序,一个升序一个降序,此时需要注意联合索引在创建时的规则(ASC/DESC)
-
如果不可避免的出现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不是索引,对表进行加锁