MYSQL专题-绝对实用的MYSQL优化总结

相信大家不管是在面试中,或者是在实际的开发过程中,都有接触过SQL优化相关的事情。之前有看到过类似知识的小伙伴可能能说出点东西来,对于没有涉及过相关知识的伙伴也不用着急,看了这篇,相信也足够你去应对面试或者在实际开发工程中使用了。关注收藏,随时拿出来看看,相信对你会有很大的帮助。当然以下SQL优化策略适用于数据量较大的场景下,如果数据量较小,没必要以此为准,以免画蛇添足。

这里不仅仅针对SQL优化进行归纳和总结,我们从最初的建表,包括建表需要注意的事项以及规约,到最后的SQL语句,包括SQL语句书写需要注意的事项以及对于某些SQL语句的优化这一整个过程做一个系统的总结,当然整个过程都涉及到索引的建立与使用,这个是SQL优化的关键,所以这里我会一共分为三个部分进行总结:建表规范及优化、SQL规范及优化、索引规范及优化。当然有些是建议不作为SQL指定规范,大家适当甄别。

建表规范及优化

  • (1) 库名、表名、字段名均小写,下划线风格,不超过32个字符,必须见名知意,禁止拼音英文混用。
  • (2)数据库表、表字段必须加入中文注释。
  • (3)尽量统一使用UTF8字符集,在Mysql中的UTF8并非“真正的UTF-8”,而utf8mb4”才是真正的“UTF-8”,如果需要存储表情,那么选择utf8mb4来进行存储,注意它与utf-8编码的区别。
  • (4)存储引擎必须使用InnoDB,因为InnoDB支持事物、行级锁、并发性能更好,CPU及内存缓存页优化使得资源利用率更高。
  • (5)每张表必须设置一个主键ID,create_time,update_time。且这个主键ID使用自增主键(在满足需要的情况下尽量短),除非在分库分表环境下。
    • InnoDB组织数据的方式决定了需要有一个主键,而且若是这个主键ID是单调递增的可以有效提高插入的性能,避免过多的页分裂、减少表碎片提高空间的使用率。
    • 在分库分表环境下,需要统一来分配各个表中的主键值,从而避免整个逻辑表中主键重复。
  • (6)单表列数目必须小于30,若超过则应该考虑将表拆分。单表列数太多使得Mysql服务器处理InnoDB返回数据之间的映射成本太高。
  • (7)禁止使用外键,如果有外键完整性约束,需要应用程序控制,因为外键会导致表与表之间耦合,UPDATE与DELETE操作都会涉及相关联的表,十分影响SQL的性能,甚至会造成死锁。
  • (8)必须把字段定义为NOT NULL并且提供默认值
    • NULL的列使索引/索引统计/值比较都更加复杂,对MySQL来说更难优化 ;
    • NULL这种类型Msql内部需要进行特殊处理,增加数据库处理记录的复杂性;
    • 同等条件下,表中有较多空字段的时候,数据库的处理性能会降低很多;
    • NULL值需要更多的存储空,无论是表还是索引中每行中的NULL的列都需要额外的空间来标识。不要以为 NULL 不需要空间,比如char(100) 型,在字段建立时,空间就固定了, 不管是否插入值(NULL也包含在内),都是占用 100个字符的空间的,如果是varchar这样的变长字段, null 不占用空间。
  • (9)禁用保留字,如DESC、RANGE、MARCH等,请参考Mysql官方保留字
  • (10)在一些场景下,考虑使用TIMESTAMP代替DATETIME
    • 这两种类型的都能表达"yyyy-MM-dd HH:mm:ss"格式的时间
    • TIMESTAMP只需要占用4个字节的长度,可以存储的范围为(1970-2038)年,在各个时区,所展示的时间是不一样的;
    • DATETIME类型占用8个字节,对时区不敏感,可以存储的范围为(1001-9999)年。
  • (11)尽可能的使用 varchar/nvarchar 代替 char/nchar
    • 因为首先变长字段存储空间小,可以节省存储空间;
    • 对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
  • (12)索引命名要规范
    • 主键索引名为 pk_ 字段名,pk_即primary key;
    • 唯一索引名为 uk _字段名 ,uk_即unique key;
    • 普通索引名则为 idx _字段名,idx_即index 。

SQL规范及优化

SQL防范优化

  • (1)写完SQL先explain查看执行计划,分析一下,尤其注意走不走索引,然后针对性的进行调整优化;
explain SELECT 
   id,name
FROM subactivity where id=22184;
SEQ	id	select_type	table	partitions	type	possible_keys	key	key_len	ref	rows	filtered	Extra
1	1	SIMPLE	subactivity	NULL	const	PRIMARY	PRIMARY	8	const	1	100	NULL
  • (2)操作delete或者update语句,加个limit,这样可以降低写错SQL的代价
    • 加了limit 1,如果第一条就命中目标return,提高SQL效率, 没有limit的话,就会继续执行扫描表;
    • delete执行时,如果相关字段加了索引,MySQL会将所有相关的行加写锁和间隙锁,所有执行相关行会被锁住,如果删除数量大,会直接影响相关业务无法使用;
    • 删除数据量很大时,不加 limit限制一下记录数,容易把cpu打满,导致越删越慢的。
  • (3)变更SQL操作先在测试环境执行,写明详细的操作步骤以及回滚方案,并在上生产前review。
  • (4)修改或删除重要数据前,要先备份。
  • (5)修改或者删除SQL,先写WHERE查一下,确认后再补充 delete 或 update。

SQL查询优化

  • (1)避免使用select *

    • 使用select * 取出全部列,会让优化器无法完成索引覆盖扫描这类优化,会影响优化器对执行计划的选择,也会增加网络带宽消耗,更会带来额外的I/O,内存和CPU消耗。
  • (2)多表关联查询时,小表在前,大表在后,且使用表的别名

    • 执行 from 后的表关联查询是从左往右执行的(Oracle相反),第一张表会涉及到全表扫描,所以将小表放在前面,先扫小表,扫描快效率较高,在扫描后面的大表,或许只扫描大表的前100行就符合返回条件并return了。
  • (3)调整Where字句中的连接顺序

    • MySQL采用从左往右,自上而下的顺序解析where子句。根据这个原理,应将过滤数据多 的条件往前放,最快速度缩小结果集。
  • (4)用where字句替换HAVING字句。

    • HAVING只会在检索出所有记录之后才对结果集进行过滤
    • where是在聚合前刷选记录,如果能通过where字句限制记录的数目,那就能减少这方面的开销;
    • HAVING中的条件一般用于聚合函数的过滤,除此之外,应该将条件写在where字句中。
  • (5)对于复杂的查询,可以使用中间临时表 暂存数据

  • (6)建议使用union all

    • MySQL通过创建并填充临时表的方式来执行union查询。除非确实要消除重复的行,否则建议使用union all。
    • 如果没有all这个关键词,MySQL会给临时表加上distinct选项,这会导致对整个临时表的数据做唯一性校验,这样做的消耗相当高。
  • (7)拆分复杂SQL为多个小SQL,避免大事务

    • 简单的SQL容易使用到MySQL的QUERY CACHE;
    • 减少锁表时间特别是使用MyISAM存储引擎的表;
    • 可以使用多核CPU。
  • (8)查询数据量大的表 会造成查询缓慢,主要的原因是扫描行数过多。这个时候可以通过程序,分段分页进行查询,循环遍历,将结果合并处理进行展示。

SQL增删改优化

  • (1)如果同时执行大量的插入,建议使用多个值的INSERT语句,这比使用分开INSERT语句快,一般情况下批量插入效率有几倍的差别。
  • (2)命令行修改数据,使用begin + commit 事务。
  • (3)当删除全表中记录时,使用truncate代替delete。
    • 使用delete语句的操作会被记录到undo块中,删除记录也记录binlog,当确认需要删除全表时,会产生很大量的binlog并占用大量的undo数据块,此时既没有很好的效率也占用了大量的资源;
    • 使用truncate替代,不会记录可恢复的信息,数据不能被恢复。也因此使用truncate操作有其极少的资源占用与极快的时间。另外,使用truncate可以回收表的水位,使自增字段值归零。

索引规范及优化

索引建立规范

  • (1)表的主键、外键必须有索引;
  • (2)经常与其他表进行连接的表,在连接字段上应该建立索引;
  • (3)对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
  • (4)尽量使用数字型字段(如性别,男:1 女:2)
    • 若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销;
    • 引擎在处理查询和连接时会 逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。

索引失效场景

  • (1)尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
  • (2)尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描
  • (3)尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,可以改用union或union all
  • (4)尽量避免在字段开头模糊查询,会导致数据库引擎放弃索引进行全表扫描。
  • (5)尽量避免使用in 和not in,会导致引擎走全表扫描。如果是连续数值,可以用between代替,如果是子查询,可以用exists代替。
  • (6)尽量避免在where条件中等号的左侧进行表达式、函数操作,会导致数据库引擎放弃索引进行全表扫描。可以将表达式、函数操作移动到等号右侧。
  • (7)当数据量大时,避免使用where 1=1的条件。通常为了方便拼装查询条件,我们会默认使用该条件,数据库引擎会放弃索引进行全表扫描。用代码拼装sql时进行判断,没 where 条件就去掉 where,有where条件就加 and。
  • (8)where条件仅包含复合索引非前置列会导致不会走联合索引。
  • (9)尽量避免隐式类型转换,会造成不使用索引。

最后要说的是,索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值