1.索引优化
-
在经常用于过滤(WHERE子句)和排序(ORDER BY子句)的列上创建索引。(使用索引)
-
避免在WHERE子句中使用非等值比较(如!=, <>),因为这可能使数据库无法使用索引。
-
使用覆盖索引(Covering Indexes),即索引中包含了查询所需的所有列,这样数据库就不需要回表查询数据。
2.查询结构优化
-
避免使用SELECT *,而是明确指定所需的列,减少数据传输量。
#反例
SELECT * FROM user
#正例
SELECT id,username,tel FROM user#理由
#1.节省资源、减少网络开销。
#2.可能用到覆盖索引,减少回表,提高查询效率。
-
优化JOIN操作,确保连接条件正确,并且连接的列上有索引。(使用JOIN联合查询代替子查询)
-
使用EXISTS或IN代替LEFT JOIN和IS NULL检查,根据具体情况选择更高效的写法。
#正例
SELECT name FROM users WHERE EXISTS (SELECT 1 FROM orders WHERE orders.user_id = users.id AND orders.total > 100
);
-
提高group by语句的效率
#反例:先分组,再过滤 select job, avg(salary) from employee group by job having job ='develop' or job = 'test'; #2.正例:先过滤,后分组 select job,avg(salary) from employee where job ='develop' or job = 'test' group by job; #3.理由 #可以在执行到该语句前,把不需要的记录过滤掉
3.数据类型和存储优化
-
尽量使用数值替代字符串类型。
#正例 主键(id):primary key优先使用数值类型int,tinyint 性别(sex):0代表女,1代表男;数据库没有布尔类型,mysql推荐使用tinyint #理由 #1.因为引擎在处理查询和连接时会逐个比较字符串中每一个字符; #2.而对于数字型而言只需要比较一次就够了; #3.字符会降低查询和连接的性能,并会增加存储开销
-
对于频繁查询的列,考虑使用更紧凑的数据类型来减少磁盘I/O和内存使用。
4. 避免全表扫描
-
尽量避免在WHERE子句中使用NULL值判断,可以使用IS NULL或IS NOT NULL替代=或!=。
-
使用LIMIT限制结果集大小,尤其是在处理大数据量时。(避免冗余和重复的表扫描)
#正例子 SELECT id, name FROM users WHERE email = 2890850899@qq.com' LIMIT 1;
5.使用统计信息和分析工具
-
定期更新数据库的统计信息,帮助优化器做出更好的执行计划。
-
使用数据库的执行计划工具(如EXPLAIN)来分析查询的执行过程,找出瓶颈。
6.避免使用OR操作
-
如果可能,使用UNION ALL代替OR,因为后者可能导致索引失效。
#反例 SELECT id FROM user WHERE id=1 OR salary=5000 #正例:使用union all SELECT id FROM user WHERE id=1 UNION ALL SELECT id FROM user WHERE salary=5000 #理由 #1.使用or可能会使索引失效,从而全表扫描; #2.对于or没有索引的salary这种情况,假设它走了id的索引,但是走到salary查询条件时,它还得全表扫描; #3.也就是说整个过程需要三步:全表扫描+索引扫描+合并。如果它一开始就走全表扫描,直接一遍扫描就搞定; #4.虽然mysql是有优化器的,出于效率与成本考虑,遇到or条件,索引还是可能失效的;
7.函数调用优化
-
避免在WHERE子句中使用复杂的函数调用,因为这可能阻止数据库使用索引。
#正例 SELECT name FROM users WHERE created_at >= '2023-01-01';
8.批处理和分区
-
对大表进行分区,将数据分散到不同的物理位置,减少单个查询需要扫描的数据量。
-
批量插入或更新数据,减少事务处理次数,减少数据库交互次数。
9.缓存和读取优化
-
利用数据库缓存机制,如查询缓存(Query Cache),虽然现代数据库系统可能已经弃用了全局查询缓存,但可以利用缓存策略优化读取。
-
使用只读副本或只读视图来处理读密集型查询。
10.并发控制和锁定策略
-
调整事务隔离级别,平衡并发性和一致性需求。
-
减少锁定范围和持续时间,避免长时间持有锁。