sql数据库优化策略

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

一、避免不走索引的场景

1.尽量避免在字段开头模糊查询会导致数据库引擎放弃索引走全表扫描如下
SELECT * FROM t WHERE username LIKE '%陈%'

优化方式尽量在字段后面使用模糊查询。如下

SELECT * FROM t WHERE username LIKE '陈%'

如果需求是要在前面使用模糊查询

  • 使用MySQL内置函数INSTR(str,substr) 来匹配作用类似于java中的indexOf()查询字符串出现的角标位置

  • 使用FullText全文索引用match against 检索

  • 数据量较大的情况建议引用ElasticSearch、solr亿级数据量检索速度秒级

  • 当表数据量较少几千条儿那种别整花里胡哨的直接用like '%xx%'。

2.尽量避免使用in 和not in会导致引擎走全表扫描。如下
SELECT * FROM t WHERE id IN (2,3)

优化方式如果是连续数值可以用between代替。如下

SELECT * FROM t WHERE id BETWEEN 2 AND 3

如果是子查询可以用exists代替。

-- 不走索引
select * from A where A.id in (select id from B);
-- 走索引
select * from A where exists (select * from B where B.id = A.id);
3. 尽量避免使用 or会导致数据库引擎放弃索引进行全表扫描。如下
SELECT * FROM t WHEREid = 1ORid = 3

优化方式可以用union代替or。如下

SELECT * FROM t WHERE id = 1
   UNION
SELECT * FROM t WHERE id = 3
4. 尽量避免进行null值的判断会导致数据库引擎放弃索引进行全表扫描。如下:
SELECT * FROM t WHERE score IS NULL

优化方式可以给字段添加默认值0对0值进行判断。如下

SELECT * FROM t WHERE score = 0
5. 尽量避免在where条件中等号的左侧进行表达式、函数操作会导致数据库引擎放弃索引进行全表扫描。

可以将表达式、函数操作移动到等号右侧。如下

-- 全表扫描
SELECT * FROM T WHERE score/10 = 9
-- 走索引
SELECT * FROM T WHERE score = 10*9
6. 当数据量大时避免使用where 1=1的条件。通常为了方便拼装查询条件我们会默认使用该条件数据库引擎会放弃索引进行全表扫描。如下
SELECT username, age, sex FROM T WHERE 1=1

优化方式用代码拼装sql时进行判断没 where 条件就去掉 where有where条件就加 and

7. 查询条件不能用 < > 或者 !=

使用索引列作为条件进行查询时需要避免使用<>或者!=等判断条件。如确实业务需要使用到不等于符号需要在重新评估索引建立避免在此字段上建立索引改由查询条件中其他索引字段代替。

8. where条件仅包含复合索引非前置列

如下复合联合索引包含key_part1key_part2key_part3三列但SQL语句没有包含索引前置列"key_part1"按照MySQL联合索引的最左匹配原则不会走联合索引。

select col1 from table where key_part2=1 and key_part3=2
9. 隐式类型转换造成不使用索引

如下SQL语句由于索引对列类型为varchar但给定的值为数值涉及隐式类型转换造成不能正确走索引。

select col1 from table where col_varchar=123; 
10. order by 条件要与where中条件一致否则order by不会利用索引进行排序
-- 不走age索引
SELECT * FROM t order by age;

-- 走age索引
SELECT * FROM t where age > 0 order by age;

对于上面的语句数据库的处理顺序是

  • 第一步根据where条件和统计信息生成执行计划得到数据。

  • 第二步将得到的数据排序。当执行处理数据order by时数据库会先查看第一步的执行计划看order by 的字段是否在执行计划中利用了索引。如果是则可以利用索引顺序而直接取得已经排好序的数据。如果不是则重新进行排序操作。

  • 第三步返回排序后的数据。

当order by 中的字段出现在where条件中时才会利用索引而不再二次排序更准确的说order by 中的字段在执行计划中利用了索引时不用排序操作。

这个结论不仅对order by有效对其他需要排序的操作也有效。比如group by 、union 、distinct等。

二、SELECT语句其他优化

1. 避免出现select *

使用select * 取出全部列会让优化器无法完成索引覆盖扫描这类优化会影响优化器对执行计划的选择也会增加网络带宽消耗更会带来额外的I/O,内存和CPU消耗。

2. 避免出现不确定结果的函数

特定针对主从复制这类业务场景。由于原理上从库复制的是主库执行的语句使用如now()、rand()、sysdate()、current_user()等不确定结果的函数很容易导致主库与从库相应的数据不一致。另外不确定值的函数,产生的SQL语句无法利用query cache。

3.多表关联查询时小表在前大表在后。

在MySQL中执行 from 后的表关联查询是从左往右执行的Oracle相反第一张表会涉及到全表扫描所以将小表放在前面先扫小表扫描快效率较高在扫描后面的大表或许只扫描大表的前100行就符合返回条件并return了。

例如表1有50条数据表2有30亿条数据如果全表扫描表2你品那就先去吃个饭再说吧是吧。

4. 使用表的别名

当在SQL语句中连接多个表时请使用表的别名并把别名前缀于每个列名上。这样就可以减少解析的时间并减少哪些友列名歧义引起的语法错误。

5. 用where字句替换HAVING字句

避免使用HAVING字句因为HAVING只会在检索出所有记录之后才对结果集进行过滤而where则是在聚合前刷选记录如果能通过where字句限制记录的数目那就能减少这方面的开销。HAVING中的条件一般用于聚合函数的过滤除此之外应该将条件写在where字句中。

where和having的区别where后面不能使用组函数

6.调整Where字句中的连接顺序

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

三、增删改 DML 语句优化

1. 大批量插入数据

如果同时执行大量的插入建议使用多个值的INSERT语句(方法二)。这比使用分开INSERT语句快方法一一般情况下批量插入效率有几倍的差别。

方法一
insert into T values(1,2); 
insert into T values(1,3); 
insert into T values(1,4);
方法二
Insert into T values(1,2),(1,3),(1,4); 

选择后一种方法的原因有三。

  • 减少SQL语句解析的操作MySQL没有类似Oracle的share pool采用方法二只需要解析一次就能进行数据的插入操作

  • 在特定场景可以减少对DB连接次数

  • SQL语句较短可以减少网络传输的IO。

2. 适当使用commit

适当使用commit可以释放事务占用的资源而减少消耗commit后能释放的资源如下

  • 事务占用的undo数据块

  • 事务在redo log中记录的数据块

  • 释放事务施加的减少锁争用影响性能。特别是在需要使用delete删除大量数据的时候必须分解删除量并定期commit。

四、查询条件优化

1. 对于复杂的查询可以使用中间临时表 暂存数据
2. 优化group by语句

默认情况下MySQL 会对GROUP BY分组的所有值进行排序如 “GROUP BY col1col2....;” 查询的方法如同在查询中指定 “ORDER BY col1col2...;” 如果显式包括一个包含相同的列的 ORDER BY子句MySQL 可以毫不减速地对它进行优化尽管仍然进行排序。

因此如果查询包括 GROUP BY 但你并不想对分组的值进行排序你可以指定 ORDER BY NULL禁止排序。例如

SELECT col1, col2, COUNT(*) FROM table GROUP BY col1, col2 ORDER BY NULL ;
3. 优化join语句

MySQL中可以通过子查询来使用 SELECT 语句来创建一个单列的查询结果然后把这个结果作为过滤条件用在另一个查询中。使用子查询可以一次性的完成很多逻辑上需要多个步骤才能完成的 SQL 操作同时也可以避免事务或者表锁死并且写起来也很容易。但是有些情况下子查询可以被更有效率的连接(JOIN)..替代。

连接(JOIN).. 之所以更有效率一些是因为 MySQL 不需要在内存中创建临时表来完成这个逻辑上的需要两个步骤的查询工作。

4. 优化union查询

MySQL通过创建并填充临时表的方式来执行union查询。除非确实要消除重复的行否则建议使用union all。原因在于如果没有all这个关键词MySQL会给临时表加上distinct选项这会导致对整个临时表的数据做唯一性校验这样做的消耗相当高。

高效
SELECT COL1, COL2, COL3 FROM TABLE WHERE COL1 = 10 
UNION ALL 
SELECT COL1, COL2, COL3 FROM TABLE WHERE COL3= 'TEST'; 
5.拆分复杂SQL为多个小SQL避免大事务
  • 简单的SQL容易使用到MySQL的QUERY CACHE

  • 减少锁表时间特别是使用MyISAM存储引擎的表

  • 可以使用多核CPU。

6. 使用truncate代替delete

当删除全表中记录时使用delete语句的操作会被记录到undo块中删除记录也记录binlog当确认需要删除全表时会产生很大量的binlog并占用大量的undo数据块此时既没有很好的效率也占用了大量的资源。

使用truncate替代不会记录可恢复的信息数据不能被恢复。也因此使用truncate操作有其极少的资源占用与极快的时间。另外使用truncate可以回收表的水位使自增字段值归零。

7. 使用合理的分页方式以提高分页效率

五、建表优化

1. 在表中建立索引优先考虑where、order by使用到的字段。
2. 尽量使用数字型字段如性别男1 女2

若只含数值信息的字段尽量不要设计为字符型这会降低查询和连接的性能并会增加存储开销。

这是因为引擎在处理查询和连接时会 逐个比较字符串中每一个字符而对于数字型而言只需要比较一次就够了。

3. 查询数据量大的表 会造成查询缓慢。主要的原因是扫描行数过多。这个时候可以通过程序分段分页进行查询循环遍历将结果合并处理进行展示。

要查询100000到100050的数据如下

SELECT * FROM (SELECT ROW_NUMBER() OVER(ORDER BY ID ASC) AS rowid,* 
FROM infoTab)t WHERE t.rowid > 100000 AND t.rowid <= 100050
4. 用varchar/nvarchar 代替 char/nchar

尽可能的使用 varchar/nvarchar 代替 char/nchar 因为首先变长字段存储空间小可以节省存储空间其次对于查询来说在一个相对较小的字段内搜索效率显然要高些。

不要以为 NULL 不需要空间比如char(100) 型在字段建立时空间就固定了 不管是否插入值NULL也包含在内都是占用 100个字符的空间的如果是varchar这样的变长字段 null 不占用空间

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: 数据库