数据库优化涉及到方方面面的知识,每种数据库的架构,优化方式也都有着很大的差别,若是想作好数据库优化要了解数据库的技术架构、存储结构、存储方式、缓存结构、SQL语句执行过程等有很深入的了解。本文只是针对开发人员平常用到的通用的优化方法进行介绍,至于数据库参数调整等数据库相关内容也再也不本文的讨论范围以内。目的是让开发人员对经常使用数据库优化有所了解。mysql
二八法则认为,在任何一组东西中,最重要的只占其中一小部分,约20%,其他80%尽管是多数,倒是次要的,如:20%的富人拥有80%的财富。20%的权贵消耗了80%的资源,等等。数据库的优化也符合二八法则:sql
因此咱们若是可以了解常见的数据库优化方法,能够解决开发中遇到的80%问题。数据库
在小品里面问大象放冰箱分几步?把冰箱门打开,把大象放进去,把冰箱门关上。缓存
数据库优化分几步?找到具体影响数据库性能的缘由,把问题解决了。bash
误区: 个人SQL语句执行的很快,问题确定不会出在我这里。服务器
正解:SQL语句单次执行的快慢并不必定表明语句的好坏。有的语句虽然单次执行效率还比较高,可是随着数据量的增长,并发量的增长极可能成为性能瓶颈。架构
该怎么作?SQL写好了以后看一下这条SQL的执行计划,用事实说话。并发
那么什么是执行计划呢?oracle
SQL是一种傻瓜式语言,每个条件就是一个需求,访问的顺序不一样就造成了不一样的执行计划。数据库必须作出选择,一次只能有一种访问路径。执行计划是一条查询语句在数据库中的执行过程或访问路径的描述。函数
每种数据库都有工具能够查看SQL的执行计划。只有执行计划没问题才表明SQL没问题。若是你能看懂了执行计划。那么恭喜你,优化问题你已经解决了80%。由于发现问题每每比解决问题更困难。
上面说到了如何发现问题,其实除了看执行计划还有其它手段,如查看单个SQL执行次数等等,可是,执行计划是开发人员最经常使用最直接的方式,可以帮助咱们解决大部分问题。那么问题发现了以后如何解决呢?
简单的说优化有两个方向:能少作事尽可能少作事,若是不能少作事尽可能利用起服务器的性能。
如何提升服务器性能,能够利用并发的方式让服务器的资源尽可能发挥到极致。有些数据库自己就提供了一些并发机制,如oracle能够经过增长hint来设置SQL的并发数,也能够经过应用程序的并发来尽可能压榨出服务器的性能。固然还能够经过增长服务器,对数据库进行分库分表来提高性能。
另一种方式是尽可能少作事(敲黑板,划重点,这很关键)。
如何让数据库少作事呢?方式也有不少,最多见的是经过索引。曾经有一位在ORACLE从业20余年的专家说了这样一句话,“其实我只懂一点IT知识,IT知识里我只懂一点ORACLE,而ORACLE我也只懂一点数据库,数据库里面只懂点SQL,SQL里面只懂点索引”。可见索引对于数据库优化有多么重要。
前面也提到索引对于数据库的优化特别重要,接下来聊聊建立和使用索引时的一些注意事项。索引建立的前缀性和可选择性,索引建立的几条建议,常见的索引被抑制状况。
先看如下例子假设在员工表(emp)的(ENAME, JOB, MGR)三个字段上建了一个索引,例如索引名叫IDX_1。三个字段分别为员工姓名、工做和所属经理号。而后,写以下一个查询语句,并不断进行查询条件和次序的排列组合,例如:
Select * from emp where ENAME=’a’ and JOB=’b’ and MGR=3;
Select * from emp where JOB=’b’ and MGR=3 and ENAME=’a’;
Select * from emp where JOB=’b’ and ENAME=’a’ and MGR=3;
Select * from emp where JOB=’b’ and MGR=3;
Select * from emp where ENAME=’a’ and MGR=3;
Select * from emp where ENAME=’a’;
Select * from emp where JOB=’b’;
Select * from emp where MGR=3;
复制代码
在各类条件组合状况下,刚才建的索引(IDX_1)是用仍是不用?也就是说对emp表的访问是全表扫描和仍是按索引(IDX_1)访问?
答案是只要有ENAME=’a’条件,就能用上索引(IDX_1),而不是全表扫描。建立复合索引时必定要考虑到索引的前缀性不然会因为没有前缀列在检索条件中致使的全表扫描。
索引的前缀性指的是必须用到索引的第一个字段。
索引的可选择性,指的是不重复的索引值(基数)和表记录数的比值。可选择性是索引筛选能力的一个指标。当可选择性越大,索引价值也就越大。
如一张订单表order记录为10万条,表中user_id列的不重复值为10000,order_date列不重复值为1000,则建立在user_id上建立索引的查询效率要比在order_date上建立索引的查询效率高。这是由于,字段值越多,可选性越强,按照索引查询后须要定位的记录越少,查询效率越高。
数据库最经常使用的索引为B树索引(不一样的数据库实现稍有不一样,例如:oralce建立的是B*树,mysql是B+树),不一样数据库可能还有本身特有的索引如:oracle的位图索引,mysql的hash索引等等。这里咱们只讨论经常使用的B树索引。下面给出几条建立B树索引时设计单字段索引和复合索引的建议:
在了解了索引建立的规则后根据业务须要建立好了索引,可是经过看语句的执行计划发现仍然走的全表扫描,建立的索引没有被使用。那么索引不被使用都有可能由于什么缘由致使的呢。
1.索引列上有表达式或者函数操做。则索引是失效的。
若有以下语句:
select user_name from user where age -30 = 0
select user_name from user where age = 30
复制代码
虽然age列上建立了索引,可是第一条语句依然是会按照全表扫描来执行的
2.存在隐式数据类型转换
若有以下语句:
select user_name from user where age = ‘30’
复制代码
可是user表定义的age列为number类型,在执行查询时发生了隐式类型转换。则索引被抑制。这种状况在开发过程当中没有上一种状况明显。很容易被你们忽视。
3.数据可选性不高
例如user表有10万条数据,可是性别列只有男女两种,这种状况下,即便建立了索引,执行语句时也不会走索引。缘由是根据索引查找出的结果集依然很大,查询效率还不如全表扫描的效率高,这是数据库就会执行全表扫描。
4.忽略的索引的前缀性
如上文所述,执行语句时,忽略了索引的前置性,则执行语句时是不会走索引的。
使用is null或者is not null,null值并无被定义,因此索引会被抑制。
前面也提到SQL的优化很大程度上要依赖执行计划,那么执行计划是如何生成出来的呢。简单的说,数据库会按期的收集数据库中每一个表的数据量等基础信息。当一条SQL发送到数据库要执行以前,在完成合法性检查以后数据库会根据每张表的数据量,索引等信息经过计算给出一个SQL执行的最优计划。
上文提到SQL的执行计划须要经过各类信息计算获得,那么若是我把SQL的执行计划缓存起来。那么每次当同一个SQL执行屡次的时候不就免去了每次计算的代价吗。实际上数据库也正是这样作的。数据库会根据每条SQL的hash值将执行计划缓存到内存。
为了提升缓存SQL的命中率,咱们写SQL的时候更多的使用占位符。而不是直接传值。
若有以下SQL:
select user_name from user where user_id = 1
select user_name from user where user_id = 2
复制代码
这时数据库会当作两条SQL来处理。由于缓存并无命中。作以下修改:
select user_name from user where user_id = ?
复制代码
每次经过绑定不一样的参数则会命中缓存,减小了SQL的解析代价
那么是否是全部的变量都要以绑定变量的方式来传入呢?固然也不是。看下面的例子
若有订单表t_order(user_id, order_date,amount),其中user_id为用户ID,order_date为订单日期
分别建立索引 :
idx_order_1(user_id)
idx_order_2(order_date)
复制代码
若是我但愿查询的SQL为:
select user_id, order_date,amount
from t_order
where user_id >= :num1
and user_id <= :num2
and order_date >= :date1
and order_date <= :date2
复制代码
这时当传入的参数num1和num2之间范围较小极限状况下num1=num2,也就是说我要查询某一我的一段时间的订单,则使用idx_order_2的效率会更高。而当传入的参数num1和num2之间范围较大但date1和date2的范围较小时。如查询某一天全部人的订单,则使用idx_order_1的效率会更高。这时若是咱们使用绑定变量的方式。因为数据库分析执行计划时并不能知道参数的范围是什么。也就不可以给出最优的计划,这时就不建议使用绑定变量方式来传值。
本文也只是对数据库常见的优化方式 进行了讨论。要作好数据库的优化还要在平常开发中多多尝试。
李光明,民生科技有限公司,用户体验技术部Firefly移动金融开发平台Java开发工程师。