MySql相关性能优化(InnoDB)

目录

一、MySql服务器

二、表结构

三、索引

四、查询


 

一、MySql服务器

1.1 查询缓存

当数据库中的操作增删改比较多的时候,可以关闭查询缓存。 因为每次增删改的时候都会清空该表的缓存。即使要使用查询缓存,也要合理的设置缓存空间。

当数据库中的查询操作更频繁的时候可以启用查询缓存。

需要注意的是在sql上不能发生任何改变,否则不会命中缓存;当查询语句中包含一些不确定的数据时,不会被缓存,例如Now()、Current_Date()等。如果查询中包含任何用户自定义函数、存储函数、用户变量、临时表、Mysql系统表,或者说任何包含列级别权限表,都不会被缓存。

1.2  设置innodb_io_capacity值

如果服务器的IO性能很高,而innodb_io_capacity设置得很低,MySql服务器会认为你的服务器性能就这样。通过合理的设置innodb_io_capacity值可以控制刷脏页的速度,进而提升读写性能。

使用fio工具检测服务器的io能力

fio -filename=$filename -direct=1 -iodepth 1 -thread -rw=randrw -ioengine=psync -bs=16k -size=500M -numjobs=10 -runtime=10 -group_reporting -name=mytest 

1.3 配置redo log

redo log 太小的话,会导致很快就被写满,然后不得不强行刷 redo log,WAL 机制的能力就发挥不出。会导致数据库忽快忽慢的情况。

1.4 配置buffer pool 缓存池

Mysql将常用数据缓存在buffer pool中,并根据热度排列优先级。可以将buffer pool设置到可用内存的50%-80%。

二、表结构

2.1 表字段

a. 能用char()不用varchar()。当字段长度固定的时候应该使用char();例如GUID列、订单编号。

b. 尽量使用NOT NULL。

c. 尽量使用较小的类型。避免使用TEXT、BOLB。

d. 主键尽量用自增ID,不用GUID; 

2.2 范式与反范式

2.2.1 范式的优点和缺点

         a. 范式化的更新操作通常比反范式快;

         b. 重复数据更少甚至没有重复数据,所以只需要修改更少的数据;

         c. 范式化的表更小,可以更好地放在内存中,执行操作更快;

         d. 使用distinct和group by的场景更少了;

2.2.2 反范式的优点和缺点

         a. 查询的时候可以减少关联次数,提高查询效率;

         b. 会出现很多重复数据;

         c. 如果经常只查询其中少数列的话,可能还没有范式化的性能高。因为需要加载更多的数据到内存中;

三、索引

通过建立索引可以有效的提高查询性能。索引也分为很多种(聚集、非聚集、联合索引等),数据结构主要有哈希索引和B+树等,哈希索引在单个查询性能上很强大,但不适合做范围查询。以下讨论主要是建立在B+树索引上面的。

索引一般采用B+树的数据结构,B+树是一个有序的树形结构,每个父节点可以有多个子节点,子节点之间也通过指针相互连接。

建立索引需要注意的地方:

a. 尽量不要在字段类型长度太大的字段上建立索引,例如 text;

b. 不要在取值范围太小的字段建立索引;例如 性别;

c. 不要在不经常进行where查询过滤的字段建立索引;

d. 一个表不能建立过多的索引;维护索引需要消耗资源;

e. 数据量太少也不需要建索引,有时候反而性能更低。当然这点性能可以忽略不计;

那么如何选择一个有效、合适的索引呢?首先让我们先了解一下它们。

3.1 聚集索引(也叫 聚簇索引、主键索引)

一个表只能拥有一个聚集索引,而且是建立在主键上面的,主键一般采用自增ID和GUID等方式。自增ID由于是有序的,而且占用字节较少。所以在性能和空间上都比较有优势。

表中的数据保存在聚集索引的叶子节点中。在利用聚集索引做查询的时候只需要根据索引字段找到对应的节点就可以查询到相应的数据。因为索引是有序的。 所以在范围查找的效率上也是很高的。只需要找到第一个节点和最后一个节点。其次可以利用索引字段进行order by 和group by操作,效率都是很高的。

3.2 非聚集索引(也叫 非聚簇索引、二级索引)

一个表只能拥有多个非聚集索引,非聚集索引的叶子节点只保存聚集索引的索引字段值(主键列发的值)。

所以在利用非聚集索引做查询的时候通常会涉及到回表操作;这是因为在根据非聚集索引中查询只能得到主键列的值,然后需要根据主键列的值去查询聚集索引,最后才能得到我们想要的数据。

有时候也是可以不需要回表操作的, 比如:select id from table where index=1 ;  这是因为在非聚集索引中已经能获取到我们想要的数据了,这种方式被称为覆盖索引

3.3 联合索引

一个索引中可以包含多个字段,这种索引就叫做联合索引。

联合索引需要注意的就是字段顺序问题。在查询里面必须要使用联合索引中的第一个字段;

例如:索引包含三个字段(name,age,number);

select * from table where name ="小熊" ;  --走索引

select * from table where name like "小%" ;  --走索引

select * from table where age=18;  --不走索引

如果查询结果中也只包含这三列是不用做回表查询的:例如select name,age,number from table where name ="小熊" ;

利用覆盖索引可以减少树的搜索次数,提高查询性能。

3.4 普通索引和唯一索引

3.4.1 查询方面(唯一索引更佳,但差距非常小)

         a. 普通索引:在查找到第一条记录后还需要查找下一条记录,直到遇到不满足条件的记录为止;由于MySql读取是根据数据页来读取的(16KB),所以普通索引在查询过程中可能需要读取多个数据页;

         b. 唯一索引:由于字段值的唯一性,查找到第一个满足条件的记录后就可以停止查询;更适合做查询操作

3.4.2 更新(普通索引更佳)

         a. 普通索引:由于普通索引在更新是时候是可以利用change buffer来提高性能的,但对于更新后立刻查询的业务场景应该关闭change buffer;否则会增加chang buffer的维护代价。更适合做更新操作

         b. 唯一索引:唯一索引的更新不能使用change buffer。而且每次更新操作的时候先要判断是否已经存在相同的字段值。相对普通索引来讲增加了数据页的读取和判断操作。

四、查询

4.1 COUNT()

性能从高到低:COUNT(*) > COUNT(1) > COUNT(id) > COUNT(其它字段)

4.2 子查询与连接查询

在应用场景上这两者往往是有所区别的。但应该尽量选择使用连接查询,而不是子查询。因为子查询在查询的时候会创建临时表。

如果存在子查询,并且查询条件如果只针对子查询,那么先在子查询中进行条件判断。

4.3 其它查询优化

a. 尽量利用索引查询

b. 能用like '熊%' 不用 like '%熊%'

c. 不要在字段上计算;例如 where  a+1 =5;可以写成 where a=5-1;

d. 尽量使用limit ; 例如 limit 1 在查询的时候找到第一条数据就可以返回,无需继续查找;

e. 尽量用between and 代替 in;

f. 需要什么字段查什么字段,避免查询出不需要的字段数据;

g. group by和order by 只涉及到一个表中的列;

h. 能用union all 不用union;

i. where条件两边的字段类型保持一致;

j. 连接查询的两个表字符集编码保持一致或者修改被驱动表字符集编码:CONVERT(字段 USING utf8mb4)

k.Group by的结果如果不需要排序的话可以加上Order By NULL来避免自动排序。

l.连接查询使用小表作为驱动表;小表指的是本身数据量小的表和过滤之后数据量较少的表;被驱动表的关联字段应建立索引;可以使用straight_join指定驱动表。

m.update更新语句后面的where条件使用索引字段(行锁)来过滤,否则将锁住整个表。跟MySql的事务隔离级别和隔离锁相关,MySql的默认事务隔离级别为Repeatable read,如果改成read commited或者取消隔离锁机制。 不会有这个问题。

4.4 EXPLAIN关键字

可以在查询语句前面加上explain关键词查看执行计划;下面说明结果中列的含义:

a. id列:只是一个编号,标识select所属的行,对于一个没有连接查询和子查询的sql语句,只会有一个编号=1;

b. select_type列:显示对应行是简单的还是复杂的select;

                        SIMPLE: 简单查询

                                   SUBQUERY: 子查询

                                   DERIVED:表示包含在FROM子句的子查询中的select

                                   UNION: union关键字后面的查询

                                   UNION RESULT: 从union的匿名临时表检查结果的select

             除了这些值,SUBQUERY和UNION还可以被标记为DEPENDENT和UNCACHEABLE;DEPENDENT意味着select依赖于外层查询中得到的数据;UNCACHEABLE意味着select中的某些特性阻止结果被缓存与Item_cache中;

c. table列:显示对应行正在访问的表的名称或者别名

d. type列:显示了访问类型,即Mysql如何查找表中的行;

                      ALL: 全表扫描;

                      index: 跟全表扫描一样,只不过是按照索引的顺序而不是行;

                      range: 范围扫描就是一个有限制的索引扫描;

                      ref: 索引查找;有可能是一行或多行,通常出现在非唯一性索引或唯一性索引的非唯一性前缀时发生;

                      eq_ref: 索引查找;最多只返回一条数据;只出现在主键查询和唯一性索引查询中;

                      const,system: Mysql对查询优化并将其转换成一个常量,例如将主键放入where中来查询此行主键;

 e. possible_keys列: 显示了查询可以使用哪些索引;

 f. key列: 显示Mysql决定采用哪个索引来优化对表单访问;

 g. key_len列: 显示Mysql在索引里使用的字节数;

 h. rows列: 显示为了查找出结果扫描了多少行数据;计算所有的rows的乘积可以知道整个查询大概会检查的行数

 i. filtered列: 在使用explain extened时出现;显示针对表里符合条件(where条件或者联接条件)的记录数的百分比所做的一个悲观估算。

 j. extra列: 包含的是不适合在其它列显示的额外信息;                    

4.5 利用慢查询

利用慢查询可以记录执行超过一定时间的sql语句;然后再针对这些sql语句优化;

查看慢查询状态: SHOW VARIABLES LIKE 'slow_query%';

查看时间设置:show variables like 'long_query_time';

开启慢查询:set global slow_query_log='ON';

设置慢查询日志地址:set global slow_query_log_file='/slow.log';

设置时间:set global long_query_time=1;  超过1秒就记录,可以设置0.1代表100ms;0代表记录所有的sql

 

发布了65 篇原创文章 · 获赞 28 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_26900081/article/details/89415517