MySQL性能优化

性能优化


常见

  • Where子句中:
    where表之间的连接必须写在其他Where条件之前,那些可以过滤掉最大数量记录的条件必须写在Where子句的末尾.HAVING最后。

  • 用EXISTS替代IN、用NOT EXISTS替代NOT IN。

  • 避免在索引列上使用计算。

  • 避免在索引列上使用IS NULL和IS NOT NULL

  • 对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。

  • 应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描。

  • 应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。


为表中字段选择合适的数据类型

字段类型优先级: 整形>date,time>enum,char>varchar>blob,text

优先考虑数字类型,其次是日期或者二进制类型,最后是字符串类型,同级别得数据类型,应该优先选择占用空间小的数据类型。

CHAR和VARCHAR的区别

1.CHAR和VARCHAR类型在存储和检索方面有所不同。

2.CHAR列长度固定为创建表时声明的长度,长度值范围是1到255。

当CHAR值被存储时,它们被用空格填充到特定长度,检索CHAR值时需删除尾随空格。


字段类型和编码

MySQL返回字符串长度

CHARACTER_LENGTH(同CHAR_LENGTH)方法返回的是字符数,LENGTH函数返回的是字节数,一个汉字三个字节。

varvhar等字段建立索引长度计算语句

select count(distinct left(test,5))/count(*) from table;越趋近1越好

MySQL的utf8

MySQL的utf8最大是3个字节不支持emoji表情符号,必须只用utf8mb4。需要在MySQL配置文件中配置客户端字符集为utf8mb4。

JDBC的连接串不支持配置characterEncoding=utf8mb4,最好的办法是在连接池中指定初始化SQL,例如:hikari连接池,其他连接池类似spring . datasource . hikari . connection - init - sql =set names utf8mb4。否则需要每次执行SQL前都先执行set names utf8mb4。

MySQL排序规则(一般使用_bin和_genera_ci)

utf8_genera_ci不区分大小写,ci为case insensitive的缩写,即大小写不敏感;
utf8_general_cs区分大小写,cs为case sensitive的缩写,即大小写敏感,但是目前MySQL版本中已经不支持类似于***_genera_cs的排序规则,直接使用utf8_bin替代;
utf8_bin将字符串中的每一个字符用二进制数据存储,区分大小写。
那么,同样是区分大小写,utf8_general_cs和utf8_bin有什么区别?

cs为case sensitive的缩写,即大小写敏感;bin的意思是二进制,也就是二进制编码比较;
utf8_general_cs排序规则下,即便是区分了大小写,但是某些西欧的字符和拉丁字符是不区分的,比如ä=a,但是有时并不需要ä=a,所以才有utf8_bin;
utf8_bin的特点在于使用字符的二进制的编码进行运算,任何不同的二进制编码都是不同的,因此在utf8_bin排序规则下:ä<>a。


EXPLIAN

  • desc或者explain加上你的SQL。
  • extended explain加上你的SQL,然后通过show warnings可以查看实际执行的语句,这一点也是非常有用的,很多时候不同的写法经SQL分析后,实际执行的代码是一样的。

优化

  1. 设计良好的数据库结构,允许部分数据冗余,尽量避免join查询,提高效率。

  2. 选择合适的表字段数据类型和存储引擎,适当的添加索引。

  3. mysql库主从读写分离。

  4. 找规律分表,减少单表中的数据量提高查询速度。

5.添加缓存机制。
比如memcached,apc等。

  1. 不经常改动的页面,生成静态页面。

  2. 书写高效率的SQL。
    比如 SELECT * FROM TABEL 改为 SELECT field_1, field_2, field_3 FROM TABLE.


锁的优化策略

  1. 读写分离

  2. 分段加锁

  3. 减少锁持有的时间

  4. 多个线程尽量以相同的顺序去获取资源

不能将锁的粒度过于细化,不然可能会出现线程的加锁和释放次数过多,反而效率不如一次加一把大锁。


千万大表在线修改

MySQL在表数据量很大的时候,如果修改表结构会导致锁表,业务请求被阻塞。MySQL在5.6之后引入了在线更新,但是在某些情况下还是会锁表,所以一般都采用pt工具( Percona Toolkit)。

如对表添加索引:

pt-online-schema-change --user=‘root’ --host=‘localhost’ --ask-pass --alter “add index idx_user_id(room_id,create_time)”

D=fission_show_room_v2,t=room_favorite_info --execute


慢查询日志

有时候如果线上请求超时,应该去关注下慢查询日志,慢查询的分析很简单,先找到慢查询日志文件的位置,然后利用mysqldumpslow去分析。查询慢查询日志信息可以直接通过执行SQL命令查看相关变量,常用的SQL如下:

–查看慢查询配置

–slow_query_1og 慢查询日志是否开启

–slow_query_1og_file 的值是记录的慢查询日志到文件中

–long_query_time 指定了慢查询的阈值

–1og_queries_not_using_indexes 是否记录所有没有利用索引的查询
SHOW VARIABLES LIKE ‘%quer%’;

–查看慢查询是日志还是表的形式
SHOW VARIABLES LIKE ‘1og_output’;

–查看慢查询的数量
SHOW GLOBAL STATUS LIKE ‘slow_queries’;

mysqldumpslow的工具十分简单,主要用到的是参数如下:

  • -t:限制输出的行数,我一般取前十条就够了;
  • -s:根据什么来排序默认是平均查询时间at,我还经常用到c查询次数,因为查询次数很频繁但是时间不高也是有必要优化的,还有t查询时间,查看那个语句特别卡;
  • -v:输出详细信息。
    例子:mysqldumpslow -v -s t -t 10 mysql_slow.log.2018-11-20-0500

查看SQL进程和杀死进程

如果执行了一个SQL的操作,但是迟迟没有返回,可以通过查询进程列表看看它的实际执行状况,如果该SQL十分耗时,为了避免影响线上可以用kill命令杀死进程,通过查看进程列表也能直观的看下当前SQL的执行状态;如果当前数据库负载很高,在进程列表可能会出现,大量的进程夯住,执行时间很长。

命令如下:

1
2
3
4
5
--查看进程列表  
SHOW PROCESSLIST;

--杀死某个进程
kill 183665

一些数据库性能的思考

在对公司慢查询日志做优化的时候,很多时候可能是忘了建索引,像这种问题很容易解决,加个索引就行了。但是有几种情况就不是简单加索引能解决了:

1、业务代码循环读数据库

考虑这样一个场景,获取用户粉丝列表信息,加入分页是十个,其实像这样的SQL是十分简单的,通过连表查询性能也很高。但是有时候,很多开发采用了取出一串ID,然后循环读每个ID的信息,这样如果ID很多对数据库的压力是很大的,而且性能也很低。

2、统计SQL

很多时候,业务上都会有排行榜这种,发现公司有很多地方直接采用数据库做计算,在对一些大表的做聚合运算的时候,经常超过五秒,这些SQL一般很长而且很难优化。像这种场景,如果业务允许(比如一致性要求不高或者是隔一段时间才统计的),可以专门在从库里面做统计。另外我建议还是采用Redis缓存来处理这种业务。

3、超大分页

在慢查询日志中发现了一些超大分页的慢查询如limit 40000,1000,因为MySQL的分页是在server层做的,可以采用延迟关联在减少回表。但是看了相关的业务代码正常的业务逻辑是不会出现这样的请求的,所以很有可能是有恶意用户在刷接口,最好在开发的时候也对接口加上校验拦截这些恶意请求。


  • 如果有自增列,truncate语句会把自增列的基数重置为0,有些场景用自增列作为业务上的ID需要十分重视;
  • 聚合函数会自动滤空,比如a列的类型是int且全部是null,则SUM(a)返回的是null而不是0;
  • MySQL判断相等不能用“a=”,这个结果永远为UnKnown,where和having中,UnKnown永远被视为false,check约束中,UnKnown就会视为true来处理。所以要用“a is ”处理。

SQL注入与防止

SQL注入产生的原因
程序开发过程中不注意规范书写sql语句和对特殊字符进行过滤,导致客户端可以通过全局变量POST和GET提交一些sql语句正常执行。

防止SQL注入的方式

开启配置文件中的magic_quotes_gpc 和 magic_quotes_runtime设置

执行sql语句时使用addslashes进行sql语句转换

Sql语句书写尽量不要省略双引号和单引号。

过滤掉sql语句中的一些关键词:update、insert、delete、select、 * 。

提高数据库表和字段的命名技巧,对一些重要的字段根据程序的特点命名,取不易被猜到的。