优化数据库性能:关键步骤与实用技巧
优化数据库性能:关键步骤与实用技巧
数据库性能优化是每个数据库开发者和运维人员都需要掌握的重要技能。本文将从SQL语句调优和索引调优两个方面,深入探讨如何优化数据库性能,帮助读者掌握关键步骤和实用技巧。
1. SQL 语句调优
在生产中,应尽量避免一些慢 SQL 语句,学会如何去分析并定位到根本原因,总结一些常用的 SQL 调优方法,以备不时之需。
1.1 慢 SQL 的原因
无索引或索引失效
在一张大表中,如果使用一个没有索引的列作为查询条件,大部分情况下查询会非常耗时。
虽然建立了索引,在某些场景下索引会失效,也会导致慢查询。
锁等待
InnoDB 存储引擎支持行锁和表锁,MyISAM 存储引擎仅支持表锁。
如果数据库操作是基于表锁实现的,当对某个表进行更新时,需要锁住整个表,那么其他的数据库操作都将被阻塞住,这严重影响到系统的并发性能。
行锁更适合高并发场景,但要注意在某些场景下,行锁会升级为表锁。
执行批量更新操作时,因为对一张表使用大量行锁,会导致事务执行效率下降,从而可能造成其它事务长时间锁等待和更多的锁冲突问题发生,致使性能严重下降,所以行锁会升级为表锁。
行锁是基于索引实现的,执行更新操作时,条件索引失效,行锁也会升级为表锁。
所以,基于表锁的数据库操作,会导致 SQL 阻塞等待,从而影响执行速度。
而行锁相对于表锁,锁粒度更细,并发能力更强,但要注意死锁问题。
不恰当的 SQL 语句
比如使用 <
select *
、<
select count(*)
,在大表中使用<
limit m,n
分页查询,对非索引字段排序等。
1.2 优化 SQL 语句的步骤
分析执行计划
通过
EXPLAIN
命令,可以得到在执行一条 SQL 语句时,查询了哪些表,是否使用了索引,遍历了多少行数据等等。
重点看下 type 列,它表示从表中查询到行所执行的方式,是 SQL 优化中一个很重要的指标。
结果值从好到差依次是:
system > const > eq_ref > ref > range > index > ALL
。
system:表中只有一条记录且该表使用的存储引擎的统计数据是精确的,比如MyISAM、Memory
const:根据主键或者唯一二级索引列与常数进行等值匹配时。
eq_ref:使用唯一索引扫描,常见于多表连接中使用主键和唯一索引作为关联条件。
ref:通过普通的二级索引列与常量进行等值匹配时。
range:索引范围扫描,比如
<,>,between
等操作。
index:索引全表扫描,此时遍历整个索引树。
ALL:全表扫描,需要遍历全表来找到对应的行。
1.3 常用的 SQL 优化
优化分页查询
可以参考这篇文章:高效分页策略:掌握 LIMIT 语句的正确使用方法与最佳实践
优化 select count(*)
如果有一个页面经常要显示交易系统的操作记录总数,到底应该怎么办呢?如果直接使用
select count(*) from table
,随着数据越来越多,这条语句执行的会越来越慢。
如果有这样的需求,我们需要自己保存操作记录的总数,比如用
redis保存
或
数据库保存
。
优化 select *
如果我们只用到表中的几个字段,就没必要
select *
,减少回表的性能消耗。
2. 索引调优
2.1 覆盖索引优化
覆盖索引可以减少树的搜索次数,显著提升查询性能。
2.2 自增字段做主键优化
使用自增主键,每次插入的新数据就会按顺序添加到当前索引节点的位置,不需要移动已有的数据,当页面写满,就会自动开辟一个新页面。
因为不需要重新移动数据,因此这种插入数据的方法效率非常高。
使用非自增主键,会导致数据移动,从而导致页分裂,产生大量内存碎片,导致索引结构不紧凑,从而影响查询效率。
2.3 防止索引失效
如果索引进行了表达式计算,则会失效
如果对索引字段进行了表达式计算,索引就失效了。这是因为我们需要把索引字段的值都取出来,然后依次进行表达式的计算来进行条件判断,因此采用的就是全表扫描的方式。
为了避免索引失效,对 SQL 改写,执行效率显著的提高。
执行时间从 43ms 缩短到了 10ms
对索引使用函数,也会造成失效
比如我们想筛选 username 前五位为 User3 的记录。
可以看到对索引字段进行函数操作,造成了索引失效,这时可以进行查询重写。
select * from user where username like 'User3%';
在 WHERE 子句中,如果在 OR 前的条件列进行了索引,而在 OR 后的条件列没有进行索引,那么索引会失效。
比如,下面的语句中,username 列上有索引,phone列上没有建索引,因为 OR
的含义就是两个只要满足一个即可,因此只有一个条件列进行了索引是没有意义的,只要有条件列没有进行索引,就会进行全表扫描,因此有索引的条件列也会失效:
如果对 phone 列创建了索引呢
这里使用到了 index merge,就是对 username 和 phone 分别进行了扫描,然后将这两个结果集进行了合并。这样做的好处就是避免了全表扫描。
使用 LIKE 进行模糊查询,前面是 %,索引失效
使用联合索引的时候要注意最左原则
当我们在使用复合索引时,需要使用索引中的最左边的列进行查询,才能使用到复合索引。
例如我们在 ods_deposit 表中建立一个复合索引
idx_serialnumber_status_createtime(
serial_number
,
status
,
create_date_time
),
如果我们使用
serial_number
、
serial_number+status
、
serial_number+status+create_date_time
以及
serial_number+create_date_time
组合查询,则能利用到索引;
而如果我们用
status
、
status+create_date_time
查询,将无法使用到索引。