目录
因为随着公司系统(环保型项目)规模的不断增加,导致数据量和并发量逐渐增大,因此不得不对数据库进行优化,从而达到减少数据冗余,节约数据储存空间,提高查询效率的目的。通过在日常项目中优化数据库的经验及网上查询到的资料,总结几点数据库优化的思路,若有不足之处,还望批评、指正、完善。
一、数据库硬件优化
最最最最最最最最最最最最最最最最最最最最最最最最最最最最最最最最最最最简单、粗暴的方法,就是烧钱咯。:)
二、数据库结构优化
(1)范式优化: 比如消除冗余(减少表中重复的字段,节省表空间);
(2)反范式优化:比如适当加冗余等(增加表中重复字段,减少join连接);
(3)劲量避免数据维护中出现更新,插入和删除异常:
- 插入异常:如果表中的某个实体随着另一个实体而存在(单独插入某个字段失败,因为有的字段不能为空);
- 更新异常:如果更改表中的某个实体的单独属性时,需要对多行进行更新;
- 删除异常:如果删除表中的某一个实体则会导致其他实体的消失。
(4)拆分表: 垂直拆分和水平拆分;
分区将数据在物理上分隔开,不同分区的数据可以制定保存在处于不同磁盘上的数据文件里。这样,当对这个表进行查询时,只需要在表分区中进行扫描,而不必进行全表扫描,明显缩短了查询时间,另外处于不同磁盘的分区也将对这个表的数据传输分散在不同的磁盘I/O,一个精心设置的分区可以将数据传输对磁盘I/O竞争均匀地分散开。对数据量大的时时表可采取此方法。可按月自动建表分区。
案例:
简单购物系统暂设涉及如下表:
1.产品表(数据量10w,稳定)
2.订单表(数据量200w,且有增长趋势)
3.用户表 (数据量100w,且有增长趋势)
以mysql为例讲述下水平拆分和垂直拆分,mysql能容忍的数量级在百万静态数据可以到千万
垂直拆分:
解决问题:表与表之间的io竞争
不解决问题:单表中数据量增长出现的压力
方案:
把产品表和用户表放到一个server上
订单表单独放到一个server上
水平拆分:
解决问题:单表中数据量增长出现的压力
不解决问题:表与表之间的io争夺
方案:
用户表通过性别拆分为男用户表和女用户表
订单表通过已完成和完成中拆分为已完成订单和未完成订单
产品表 未完成订单放一个server上
已完成订单表盒男用户表放一个server上
女用户表放一个server上(女的爱购物 哈哈)
三、SQL语句优化
在我们书写SQL语句的时候,其实书写的顺序、策略会影响到SQL的性能,虽然实现的功能是一样的,但是它们的性能会有些许差别。下面整理了几条优化的思路:
(1)选择最有效率的表名顺序
数据库的解析器按照从右到左的顺序处理FROM子句中的表名,FROM子句中写在最后的表将被最先处理
在FROM子句中包含多个表的情况下:
- 如果三个表是完全无关系的话,将记录和列名最少的表,写在最后,然后依次类推
- 也就是说:选择记录条数最少的表放在最后
如果有3个以上的表连接查询:
- 如果三个表是有关系的话,将引用最多的表,放在最后,然后依次类推。
- 也就是说:被其他表所引用的表放在最后
例如:查询员工的编号,姓名,工资,工资等级,部门名
emp表被引用得最多,记录数也是最多,因此放在form字句的最后面
select emp.empno,emp.ename,emp.sal,salgrade.grade,dept.dname
from salgrade,dept,emp
where (emp.deptno = dept.deptno) and (emp.sal between salgrade.losal and salgrade.hisal)
(2)WHERE子句中的连接顺序
数据库采用自右而左的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之左,那些可以过滤掉最大数量记录的条件必须写在WHERE子句的之右。
emp.sal可以过滤多条记录,写在WHERE字句的最右边
select emp.empno,emp.ename,emp.sal,dept.dname
from dept,emp
where (emp.deptno = dept.deptno) and (emp.sal > 1500)
(3)SELECT子句中避免使用*号
我们当时学习的时候,“*”号是可以获取表中全部的字段数据的。
- 但是它要通过查询数据字典完成的,这意味着将耗费更多的时间
- 使用*号写出来的SQL语句也不够直观。
(4)用TRUNCATE替代DELETE
这里仅仅是:删除表的全部记录,除了表结构才这样做。
DELETE是一条一条记录的删除,而Truncate是将整个表删除,保留表结构,这样比DELETE快
(5)多使用内部函数提高SQL效率
例如使用mysql的concat()函数会比使用||来进行拼接快,因为concat()函数已经被mysql优化过了。
(6)使用表或列的别名
如果表或列的名称太长了,使用一些简短的别名也能稍微提高一些SQL的性能。毕竟要扫描的字符长度就变少了。。。
(7)多使用commit
comiit会释放回滚点...
(8)善用索引
索引就是为了提高我们的查询数据的,当表的记录量非常大的时候,我们就可以使用索引了。
(9)SQL写大写
我们在编写SQL 的时候,官方推荐的是使用大写来写关键字,因为Oracle服务器总是先将小写字母转成大写后,才执行
(10)避免在索引列上使用NOT
因为Oracle服务器遇到NOT后,他就会停止目前的工作,转而执行全表扫描
(11)避免在索引列上使用计算
WHERE子句中,如果索引列是函数的一部分,优化器将不使用索引而使用全表扫描,这样会变得变慢
(12)用 >=
替代 >
低效:
SELECT * FROM EMP WHERE DEPTNO > 3
首先定位到DEPTNO=3的记录并且扫描到第一个DEPT大于3的记录
高效:
SELECT * FROM EMP WHERE DEPTNO >= 4
直接跳到第一个DEPT等于4的记录
(13)用IN替代OR
select * from emp where sal = 1500 or sal = 3000 or sal = 800;
select * from emp where sal in (1500,3000,800);
(14)总是使用索引的第一个列
如果索引是建立在多个列上,只有在它的第一个列被WHERE子句引用时,优化器才会选择使用该索引。 当只引用索引的第二个列时,不引用索引的第一个列时,优化器使用了全表扫描而忽略了索引
create index emp_sal_job_idex
on emp(sal,job);
----------------------------------
select *
from emp
where job != 'SALES';
上边就不使用索引了。