MySQL优化
在应用的的开发过程中,由于初期数据量小,开发人员写 SQL 语句时更重视功能上的实现,但是当应用系统正式上线后,随着生产数据量的急剧增长,很多 SQL 语句开始逐渐显露出性能问题,对生产的影响也越来越大,此时这些有问题的 SQL 语句就成为整个系统性能的瓶颈,因此我们必须要对它们进行优化。
SQL优化
优化SQL的步骤:当面对一个有 SQL 性能问题的数据库时,我们应该从何处入手来进行系统的分析,使得能够尽快定位问题 SQL 并尽快解决问题?
- 查看SQL执行频率:作为SQL优化的借鉴指标,一般80%都是查询
show status like ‘Com_______’;
,show status like 'Innodb_rows_%';
- 定位低效率执行SQL
- 慢查询日志:
- 慢查询日志默认是关闭的 ,需要配置进行开启,并且设置文件名和时间限制。
- 直接通过 cat 指令查询该日志文件即可
show processlist
:可以使用show processlist命令查看当前MySQL在进行的线程,包括线程的状态、是否锁表等,可以实时地查看 SQL 的执行情况,同时对一些锁表操作进行优化。
- 慢查询日志:
- explain 分析执行计划:找到查询效率低的 SQL 语句后,通过 explain 获取执行 Select 语句的信息,包括 Select 语句执行过程中标如何连接和连接的顺序。
- id 表示操作表的顺序:Id相同顺序从上到下,id不同越大越先被执行
- select_type 表示 SELECT 的类型,常见的取值有 SIMPLE(简单表,即不使用表连接或者子查询)、PRIMARY(主查询,即外层的查询)、UNION(UNION 中的第二个或者后面的查询语句)、SUBQUERY(子查询中的第一个 SELECT)等
- table:输出结果集的表
- type:表示表的连接类型,性能由好到差的连接类型为( system —> const -----> eq_ref ------> ref -------> ref_or_null----> index_merge —> index_subquery -----> range -----> index ------> all ) 一般来说, 我们需要保证查询至少达到 range 级别, 最好达到ref 。
- NULL:MySQL不访问任何表,索引,直接返回结果
- system:表只有一行记录(等于系统表),这是const类型的特例,一般不会出现
- const:表示通过索引一次就找到了,const 用于比较primary key 或者 unique 索引。因为只匹配一行数据,所以很快。如将主键置于where列表中,MySQL 就能将该查询转换为一个常亮。const于将 “主键” 或 “唯一” 索引的所有部分与常量值进行比较
- eq_ref:类似ref,区别在于使用的是唯一索引,使用主键的关联查询,关联查询出的记录只有一条。常见于主键或唯一索引扫描
- ref:非唯一性索引扫描,返回匹配某个单独值的所有行。本质上也是一种索引访问,返回所有匹配某个单独值的所有行(多个)
- range:只检索给定返回的行,使用一个索引来选择行。 where 之后出现 between , < , > , in 等操作。
- index:index 与 ALL的区别为 index 类型只是遍历了索引树, 通常比ALL 快, ALL 是遍历数据文件。
- all:将遍历全表以找到匹配的行
- possible_keys:表示查询时,可能使用的索引
- key:表示实际使用的索引
- key_len:索引字段的长度
- rows:扫描行的数量
- extra:执行情况的说明和描述
- show profile分析SQL:show profiles 能够在做SQL优化时帮助我们了解时间都耗费到哪里去了。
- trace分析优化器执行计划
具体优化SQL的方法:主要还是尽量减少或优化数据库内部排序,比如因为InnoDB类型的表是按照主键的顺序保存的,按主键顺序操作数据效率更高,在比如尽量用索引来提高排序的效率。
- 大批量插入数据:对于 InnoDB 类型的表,提高
load
命令导入的效率。- 按主键顺序插入:因为InnoDB类型的表是按照主键的顺序保存的,所以将导入的数据按照主键的顺序排列,可以有效的提高导入数据的效率。
- 关闭唯一性校验:在导入数据前执行
SET UNIQUE_CHECKS=0
,关闭唯一性校验,在导入结束后执行SET UNIQUE_CHECKS=1
,恢复唯一性校验,可以提高导入的效率。 - 手动提交事务:如果应用使用自动提交的方式,建议在导入前执行
SET AUTOCOMMIT=0
,关闭自动提交,导入结束后再执行SET AUTOCOMMIT=1
,打开自动提交,也可以提高导入的效率。
- 优化insert语句:
insert into tb_test values(1,'Tom'),(2,'Cat'),(3,'Jerry');
- 尽量一句插入完全部数据,缩减客户端与数据库之间的连接、关闭等消耗,或者在事务中进行数据插入,也能达到同样的效果。
- 按主键顺序插入
- 优化order by语句:尽量减少额外的排序,通过索引直接返回有序数据,通过创建合适的索引,能够减少 Filesort 的出现。
- 优化group by 语句:创建索引和执行
order by null
禁止排序来提高效率。 - 优化嵌套查询:用多表联查来替代子查询,使MySQL不需要在内存中创建临时表来完成这个逻辑上需要两个步骤的查询工作。
- 优化OR条件:建议使用 union 替换 or ;对于包含OR的查询子句,如果要利用索引,则OR之间的每个条件列都必须用到索引 , 而且不能使用到复合索引; 如果没有索引,则应该考虑增加索引。
- 优化分页查询一个常见又非常头疼的问题就是 limit 2000000,10 ,此时需要MySQL排序前2000010 记录,仅仅返回2000000 - 2000010 的记录,其他记录丢弃,查询排序的代价非常大 。
- 优化思路一:在索引上完成排序分页操作,最后根据主键关联回原表查询所需要的其他列内容。
- 优化思路二:该方案适用于主键自增的表,可以把Limit 查询转换成某个位置的查询 。
- 使用SQL提示
引擎选择
在选择存储引擎时,应该根据应用系统的特点选择合适的存储引擎。对于复杂的应用系统,还可以根据实际情况选择多种存储引擎进行组合。以下是几种常用的存储引擎的使用环境。
- InnoDB : 是Mysql的默认存储引擎,用于事务处理应用程序,支持外键。如果应用对事务的完整性有比较高的要求,在并发条件下要求数据的一致性,数据操作除了插入和查询意外,还包含很多的更新、删除操作,那么InnoDB存储引擎是比较合适的选择。InnoDB存储引擎除了有效的降低由于删除和更新导致的锁定, 还可以确保事务的完整提交和回滚,对于类似于计费系统或者财务系统等对数据准确性要求比较高的系统,InnoDB是最合适的选择。
- MyISAM : 如果应用是以读操作和插入操作为主,只有很少的更新和删除操作,并且对事务的完整性、并发性要求不是很高,那么选择这个存储引擎是非常合适的。
表结构优化
- 数据类型选择:用更小更简单的类型存储
- 使用可以存下你的数据的最小的数据类型。
- 使用简单的数据类型。int要比varchar类型在mysql处理上更简单。
- 尽可能的使用 not null 定义字段。
- 尽量少用text类型,非用不可时最好考虑分表。
- 使用int来存储日志时间,利用FROM_UNIXTINE()(得到日期),UNIX_TIMESTAMP()(得到时间戳)两个函数来进行转换
- 使用bigint来存ip地址,利用INET_ATON(),INET_NTOA()两个函数来进行转换
- 范式化优化:目前说到范式化一般是指符合第三设计范式,即任何非主属性不依赖于其它非主属性,即表中不要有冗余数据, 就是说,表的信息,如果能够被推导出来,就不应该单独的设计一个字段来存放.(外键)
- 反范式化优化:没有冗余的数据库未必是最好的数据库,有时为了提高运行效率,就必须降低范式标准,适当保留冗余数据。具体做法是: 在概念数据模型设计时遵守第三范式,降低范式标准的工作放到物理数据模型设计时考虑。降低范式就是增加字段,允许冗余。 订单和订单项、相册浏览次数和照片的浏览次数。
- 表的垂直拆分:把多个列拆分为多个表,比如不常用的字段、大字段、经常一起使用的字段存放到一个表中。
- 表的水平拆分:为了结汇数据量过大的问题,可以mod为n列。
- 数据量达到千万级别才考虑分表分库。
https://m.imooc.com/learn/194
索引优化
- 如何选择合适的列建立索引?
- 在where从句,group by 从句,order by 从句, on 从句中出现的列
- 索引字段越小越好
- 离散度大的列放到联合索引的前面
- 重复及冗余索引
- 重复索引是指相同的列以相同的顺序建立的同类型的索引
- 冗余索引是指多个索引的前缀列相同,或是在联合索引中包含了主键的索引。
- 删除不使用的索引:通过慢查询日志配合 pt-index-usage 工具来进行索引使用情况的分析。
主从复制
复制是指将主数据库的DDL 和 DML 操作通过二进制日志传到从库服务器中,然后在从库上对这些日志重新执行(也叫重做),从而使得从库和主库的数据保持同步。MySQL支持一台主库同时向多台从库进行复制, 从库同时也可以作为其他从服务器的主库,实现链状复制。
复制分成三步:
- Master 主库在事务提交时,会把数据变更作为时间 Events 记录在二进制日志文件 Binlog 中。
- 主库推送二进制日志文件 Binlog 中的日志事件到从库的中继日志 Relay Log 。
- slave重做中继日志中的事件,将改变反映它自己的数据。
MySQL 复制的优点主要包含以下三个方面:
- 主库出现问题,可以快速切换到从库提供服务。
- 可以在从库上执行查询操作,从主库中更新,实现读写分离,降低主库的访问压力。
- 可以在从库中执行备份,以避免备份期间影响主库的服务。
读写分离
如果数据库的使用场景读的操作比较的时候,为了避免写的操作所造成的性能影响可以采用读写分离的架构,读
写分离,解决的是,数据库的写入,影响了查询的效率。读写分离的基本原理是让主数据库处理事务性增、改、删
操作(INSERT、UPDATE、DELETE),而从数据库处理SELECT 查询操作。数据库复制被用来把事务性操作导致
的变更同步到集群中的从数据库