为何要做数据库性能优化
关于为何要做索引优化这个问题呢,我有以下几个观点:
- 提升用户体验:想必在互联网时代大家都对一些常用的APP、网站、网上商城等等,不管我们何时去使用它,我们都希望这些系统能够快速的响应,从而提高用户满意度,这背后必不可少的就是各种各样的性能优化了,其中当然也包含了我们今天的主题"数据库性能优化"。
- 提高系统吞吐量:吞吐量指的是在单位时间内,系统能够处理的请求数量,当我们的性能提升之后,每个请求的处理时间相对缩短,那么吞吐量自然就上来了,在一些大型的系统进行大量并发请求时就能够体现出来。
- 减少资源消耗:我们也都知道一个请求到系统上的时候呢,在这请求结束之前,整个过程中会占用很多CPU资源。试想一下如果不做优化,那么CPU资源就很可能被长时间占用,甚至可能出现内存溢出或者宕机等故障。
如何优化
到这里我们就具体来讲讲几种优化方案。
数据库层面优化
选择合适的数据库类型,根据业务场景选择合适的存储引擎。
- 数据库选择
首先可以根据业务系统搭配合理的数据库,像大型企业级的系统多数会选用Oracle,普遍中小型企业都使用MySql,还有用作大数据动态分析可以用MongoDB等等,根据自己的业务场景选择合适的数据库,避免后期更换数据库要付出更多代价。
在性能方面,Oracle具有强大的性能诊断工具,可以进行深度的性能调优。MySQL虽然在这方面相对较弱,但其开源的特性使得其在全球范围内有大量的开发者持续优化,从而提高了其性能。
不同数据库SQL语法、特性方面也有些差别,也要考虑开发人员学习成本。
- 存储引擎选择
常见的存储引擎有InnoDB、MyISAM、Memory等。以下是一些选择存储引擎的考虑因素:
- 应用类型:首先,需要确定你的应用类型。如果你的应用需要频繁进行事务处理,那么InnoDB是一个不错的选择,因为它被设计用来处理大量的并发读写操作。而如果你的应用主要是只读或少量写入,那么MyISAM可能更适合。
- 并发性能:并发性能对于一个数据库引擎来说非常重要。InnoDB使用行级锁定,可以最大程度地减少锁冲突,提高并发性能。
- 数据完整性和可靠性:数据完整性和可靠性是数据库引擎的核心特性之一。InnoDB使用日志和事务机制,确保数据的ACID属性。
- 性能和空间占用:某些存储引擎在性能和空间占用方面表现更好。如果你的应用对性能和空间有严格的要求,那么可以考虑使用内存存储引擎,如红黑树或哈希表。
优化案例
SQL
/*学生基本信息*/
DROP TABLE IF EXISTS t_student;
CREATE TABLE t_student (
id BIGINT(8) PRIMARY KEY NOT NULL AUTO_INCREMENT COMMENT '主键',
number CHAR(10) NOT NULL COMMENT '学号',
`name` VARCHAR(50) NOT NULL COMMENT '姓名',
`sex` CHAR(1) NOT NULL COMMENT '性别',
age TINYINT NOT NULL COMMENT '年龄',
address VARCHAR(256) NOT NULL COMMENT '地址',
strong_point VARCHAR(256) NOT NULL COMMENT '特长'
);
这里我们以创建学生信息作为基础数据建表,下面我们来根据该表结构分析。
表结构优化
- 根据数据属性选择合适的属性类型,尽量将字段的长度设定尽可能小:
a. 主键字段通常直接设置为长整型自增即可,根据预估数据量大小合理设置好长度即可。
b. 如上表的number
(学号)字段设置为char(10),即为固定10个字符长度的学号,通常我们学校的学号也都是统一编排的,不可能一长一短。
c. 如sex
(性别)字段设置为char(1),这种类似固定属性类型的,通常可以用特定字符替换(0:未知,1:男,2:女),后续在代码中进行格式转换即可。
d. 如age
(年龄)字段设置为tinyint,仅占用1个字节,用过存储年龄就相对合适。
- 非必要的时候尽量把字段设置为NOT NULL,这样在数据库执行查询的时候就不用去比较NULL值:
像上表我们都是把字段设置为NOT NULL,在进行数据存储的时候我们就可以存入一个空字符串代表为空即可,减少数据库对空数据的处理,或者在进行数据查询时,明确指定所要查询的字段,排除无需查询的字段。
索引优化
以MySql
为例,索引优化主要是根据MySql
提供的Explain
关键字进行分析优化的,Explain
指标解释如下:
- id:查询的标识符,相同的id表示同一个查询块。
- select_type:查询的类型,如SIMPLE(简单查询)、PRIMARY(主查询)、SUBQUERY(子查询)等。
- table:正在访问哪个表。
- type:访问类型,表示MySQL在表中查找数据的方式,如ALL(全表扫描)、range(范围扫描)、ref(索引扫描)、const(主键、唯一索引)等。 - 此列较为重要:通常要求索引性能优化至少要达到range级别,要求是ref级别,最好是const级别。
- possible_keys:可能使用的索引。
- key:实际使用的索引。
- key_len:使用的索引的长度。
- ref:显示索引的哪一列被使用了,如果可能的话,是一个常数。
- rows:MySQL认为必须检查的行数。
- filtered:数据过滤命中百分比。
- Extra:包含不适合在其他列中显示的额外信息,如Using index(使用覆盖索引)、Using filesort(使用文件排序)等。
这里我们先根据上表加几条数据针对索引优化进行详细说明:
- 首先在未添加任何索引的情况,我们使用
Explain
进行分析:
如图可以看出未加索引的情况type=ALL
,都是根据全表进行查询,这种情况很显然不是我们想要的,那么我们就可以根据需要进行适当的优化:
- 首先分析学号在一个学校里肯定都是唯一的,那么这里学号就可以作为一个唯一键进行索引设置,增加
number
列为唯一索引如下:
SQL
ALTER TABLE `t_student` ADD UNIQUE INDEX `UK_NUMBER` (`number`);
添加完索引再次查询Explain
结果如下图,可以看出此时type=const
即为唯一索引类型,优化达到此类型性能最佳。rows=1
表示MySql
预估要读取的行数,值越小性能越好。
- 若数据没办法作为唯一索引,那么我们以
name
字段为例,有的学生可能同名,这时候我们就可以将其设置为普通索引,在以name
为条件等值查询的时候也能够覆盖索引达到优化效果:
SQL
ALTER TABLE `t_student` ADD INDEX `NK_NAME` (`name`);
- 那么对于模糊查询、范围查询的又该如何优化呢? 对于
where
条件没有覆盖到索引列的type=ALL
就不用考虑了,针对索引列的模糊查询,必须遵循最左匹配原则,下面我们来看两个查询结果对比就知道了:
以上两个看似相同的查询结果,但是表现的性能可是不一样的,第一个type=ALL
即MySql
会对数据库全表进行搜索,所以就达不到优化的效果,第二个type=range
也就是我们要求最少能够达到的优化目标。
多表查询优化
使用JOIN
连接代替子查询,使用UNION
代替多条件查询,看情况优先使用UNION ALL
。
- 为什么推荐使用
JOIN
?当然这个也不是绝对的,试想一下如果是子查询的话,那么在查询主表结果之前数据库是不是肯定要先查询到子查询的结果,再把子查询的结果丢给主表作为条件进行查询,这种情况如果子查询返回的结果集非常大,那么就会造成性能大大下降,而JOIN
则没有这种烦恼。所以也就是数据量大的时候最好不要直接使用子查询,当然如果你的系统数据仅有几千几万条,还是不必担忧的。
SQL
-- 查询所有学生成继
SELECT * FROM t_grade t1
LEFT JOIN t_student t2 ON t1.student_id = t2.id;
- 使用
UNION
代替多条件查询,当一个SQL
语句有过多的where
条件,且数据量较大时,这时候一个语句有过多的查询条件查询性能会大大降低,那么这时候我们就可以考虑将语句一分为二,再将结果使用UNION
拼接起来。如果能够明确一分为二的查询结果都没有重复数据,即可直接使用UNION ALL
代替UNION
,原因是UNION
会对数据进行一个去重处理,这一步骤肯定需要消耗部分性能。
总结
性能优化路漫漫,真实上手操作看一遍每次优化后的结果,如此以来定能感悟加倍。