zoukankan      html  css  js  c++  java
  • 数据库访问性能优化(三)


    4、减少数据库服务器CPU运算

    4.1、使用绑定变量

    绑定变量是指SQL中对变化的值采用变量参数的形式提交,而不是在SQL中直接拼写对应的值。

    非绑定变量写法:Select * from employee where id=1234567

    绑定变量写法:

    Select * from employee where id=?

    Preparestatement.setInt(1,1234567)

     

    JavaPreparestatement就是为处理绑定变量提供的对像,绑定变量有以下优点:

    1、防止SQL注入

    2、提高SQL可读性

    3、提高SQL解析性能,不使用绑定变更我们一般称为硬解析,使用绑定变量我们称为软解析。

    1和第2点很好理解,做编码的人应该都清楚,这里不详细说明。关于第3点,到底能提高多少性能呢,下面举一个例子说明:

     

    假设有这个这样的一个数据库主机:

    24CPU 

    100块磁盘,每个磁盘支持IOPS160

    业务应用的SQL如下:

    select * from table where pk=?

    这个SQL平均4IO3个索引IO+1个数据IO

    IO缓存命中率75%(索引全在内存中,数据需要访问磁盘)

    SQL硬解析CPU消耗:1ms  (常用经验值)

    SQL软解析CPU消耗:0.02ms(常用经验值)

     

    假设CPU每核性能是线性增长,访问内存Cache中的IO时间忽略,要求计算系统对如上应用采用硬解析与采用软解析支持的每秒最大并发数:

     

     

    是否使用绑定变量

    CPU支持最大并发数

    磁盘IO支持最大并发数

    不使用

    2*4*1000=8000

    100*160=16000

    使用

    2*4*1000/0.02=400000

    100*160=16000

     

     

    从以上计算可以看出,不使用绑定变量的系统当并发达到8000时会在CPU上产生瓶颈,当使用绑定变量的系统当并行达到16000时会在磁盘IO上产生瓶颈。所以如果你的系统CPU有瓶颈时请先检查是否存在大量的硬解析操作。

     

    使用绑定变量为何会提高SQL解析性能,这个需要从数据库SQL执行原理说明,一条SQLOracle数据库中的执行过程如下图所示:

     

     

    数据库访问性能优化(三) 

    当一条SQL发送给数据库服务器后,系统首先会将SQL字符串进行hash运算,得到hash值后再从服务器内存里的SQL缓存区中进行检索,如果有相同的SQL字符,并且确认是同一逻辑的SQL语句,则从共享池缓存中取出SQL对应的执行计划,根据执行计划读取数据并返回结果给客户端。

    如果在共享池中未发现相同的SQL则根据SQL逻辑生成一条新的执行计划并保存在SQL缓存区中,然后根据执行计划读取数据并返回结果给客户端。

    为了更快的检索SQL是否在缓存区中,首先进行的是SQL字符串hash值对比,如果未找到则认为没有缓存,如果存在再进行下一步的准确对比,所以要命中SQL缓存区应保证SQL字符是完全一致,中间有大小写或空格都会认为是不同的SQL

    如果我们不采用绑定变量,采用字符串拼接的模式生成SQL,那么每条SQL都会产生执行计划,这样会导致共享池耗尽,缓存命中率也很低。

     

    一些不使用绑定变量的场景:

    a、数据仓库应用,这种应用一般并发不高,但是每个SQL执行时间很长,SQL解析的时间相比SQL执行时间比较小,绑定变量对性能提高不明显。数据仓库一般都是内部分析应用,所以也不太会发生SQL注入的安全问题。

    b、数据分布不均匀的特殊逻辑,如产品表,记录有1亿,有一产品状态字段,上面建有索引,有审核中,审核通过,审核未通过3种状态,其中审核通过9500万,审核中1万,审核不通过499万。

    要做这样一个查询:

    select count(*) from product where status=?

    采用绑定变量的话,那么只会有一个执行计划,如果走索引访问,那么对于审核中查询很快,对审核通过和审核不通过会很慢;如果不走索引,那么对于审核中与审核通过和审核不通过时间基本一样;

    对于这种情况应该不使用绑定变量,而直接采用字符拼接的方式生成SQL,这样可以为每个SQL生成不同的执行计划,如下所示。

    select count(*) from product where status='approved'; //不使用索引

    select count(*) from product where status='tbd'; //不使用索引

    select count(*) from product where status='auditing';//使用索引

     

    4.2、合理使用排序

    Oracle的排序算法一直在优化,但是总体时间复杂度约等于nLog(n)。普通OLTP系统排序操作一般都是在内存里进行的,对于数据库来说是一种CPU的消耗,曾在PC机做过测试,单核普通CPU1秒钟可以完成100万条记录的全内存排序操作,所以说由于现在CPU的性能增强,对于普通的几十条或上百条记录排序对系统的影响也不会很大。但是当你的记录集增加到上万条以上时,你需要注意是否一定要这么做了,大记录集排序不仅增加了CPU开销,而且可能会由于内存不足发生硬盘排序的现象,当发生硬盘排序时性能会急剧下降,这种需求需要与DBA沟通再决定,取决于你的需求和数据,所以只有你自己最清楚,而不要被别人说排序很慢就吓倒。

    以下列出了可能会发生排序操作的SQL语法:

    Order by

    Group by

    Distinct

    Exists子查询

    Not Exists子查询

    In子查询

    Not In子查询

    Union(并集),Union All也是一种并集操作,但是不会发生排序,如果你确认两个数据集不需要执行去除重复数据操作,那请使用Union All 代替Union

    Minus(差集)

    Intersect(交集)

    Create Index

    Merge Join,这是一种两个表连接的内部算法,执行时会把两个表先排序好再连接,应用于两个大表连接的操作。如果你的两个表连接的条件都是等值运算,那可以采用Hash Join来提高性能,因为Hash Join使用Hash 运算来代替排序的操作。具体原理及设置参考SQL执行计划优化专题。

     

    4.3、减少比较操作

    我们SQL的业务逻辑经常会包含一些比较操作,如a=ba之类的操作,对于这些比较操作数据库都体现得很好,但是如果有以下操作,我们需要保持警惕:

    Like模糊查询,如下所示:

    a like ‘�c%’

     

    Like模糊查询对于数据库来说不是很擅长,特别是你需要模糊检查的记录有上万条以上时,性能比较糟糕,这种情况一般可以采用专用Search或者采用全文索引方案来提高性能。

    不能使用索引定位的大量In List,如下所示:

    a in (:1,:2,:3,…,:n)   ----n>20

    如果这里的a字段不能通过索引比较,那数据库会将字段与in里面的每个值都进行比较运算,如果记录数有上万以上,会明显感觉到SQLCPU开销加大,这个情况有两种解决方式:

    a、  in列表里面的数据放入一张中间小表,采用两个表Hash Join关联的方式处理;

    b、  采用str2varList方法将字段串列表转换一个临时表处理,关于str2varList方法可以在网上直接查询,这里不详细介绍。

     

    以上两种解决方案都需要与中间表Hash Join的方式才能提高性能,如果采用了Nested Loop的连接方式性能会更差。

    如果发现我们的系统IO没问题但是CPU负载很高,就有可能是上面的原因,这种情况不太常见,如果遇到了最好能和DBA沟通并确认准确的原因。

     

    4.4、大量复杂运算在客户端处理

    什么是复杂运算,一般我认为是一秒钟CPU只能做10万次以内的运算。如含小数的对数及指数运算、三角函数、3DESBASE64数据加密算法等等。

    如果有大量这类函数运算,尽量放在客户端处理,一般CPU每秒中也只能处理1-10万次这样的函数运算,放在数据库内不利于高并发处理。

     

    5、利用更多的资源

    5.1、客户端多进程并行访问

    多进程并行访问是指在客户端创建多个进程(线程),每个进程建立一个与数据库的连接,然后同时向数据库提交访问请求。当数据库主机资源有空闲时,我们可以采用客户端多进程并行访问的方法来提高性能。如果数据库主机已经很忙时,采用多进程并行访问性能不会提高,反而可能会更慢。所以使用这种方式最好与DBA或系统管理员进行沟通后再决定是否采用。

     

    例如:

    我们有10000个产品ID,现在需要根据ID取出产品的详细信息,如果单线程访问,按每个IO5ms计算,忽略主机CPU运算及网络传输时间,我们需要50s才能完成任务。如果采用5个并行访问,每个进程访问2000ID,那么10s就有可能完成任务。

    那是不是并行数越多越好呢,开1000个并行是否只要50ms就搞定,答案肯定是否定的,当并行数超过服务器主机资源的上限时性能就不会再提高,如果再增加反而会增加主机的进程间调度成本和进程冲突机率。

     

    以下是一些如何设置并行数的基本建议:

    如果瓶颈在服务器主机,但是主机还有空闲资源,那么最大并行数取主机CPU核数和主机提供数据服务的磁盘数两个参数中的最小值,同时要保证主机有资源做其它任务。

    如果瓶颈在客户端处理,但是客户端还有空闲资源,那建议不要增加SQL的并行,而是用一个进程取回数据后在客户端起多个进程处理即可,进程数根据客户端CPU核数计算。

    如果瓶颈在客户端网络,那建议做数据压缩或者增加多个客户端,采用map reduce的架构处理。

    如果瓶颈在服务器网络,那需要增加服务器的网络带宽或者在服务端将数据压缩后再处理了。

     

    5.2、数据库并行处理

    数据库并行处理是指客户端一条SQL的请求,数据库内部自动分解成多个进程并行处理,如下图所示:

     

    数据库访问性能优化(三) 

    并不是所有的SQL都可以使用并行处理,一般只有对表或索引进行全部访问时才可以使用并行。数据库表默认是不打开并行访问,所以需要指定SQL并行的提示,如下所示:

    select  * from employee;

     

    并行的优点:

    使用多进程处理,充分利用数据库主机资源(CPU,IO),提高性能。

    并行的缺点:

    1、单个会话占用大量资源,影响其它会话,所以只适合在主机负载低时期使用;

    2、只能采用直接IO访问,不能利用缓存数据,所以执行前会触发将脏缓存数据写入磁盘操作。

     

    注:

    1、并行处理在OLTP类系统中慎用,使用不当会导致一个会话把主机资源全部占用,而正常事务得不到及时响应,所以一般只是用于数据仓库平台。

    2、一般对于百万级记录以下的小表采用并行访问性能并不能提高,反而可能会让性能更差。


    http://www.cnblogs.com/easypass/archive/2010/12/08/1900127.html


  • 相关阅读:
    Hive 窗口函数LEAD LAG FIRST_VALUE LAST_VALUE
    xargs命令
    left semi join VS left join
    静态类静态属性静态方法
    DATASET()用法
    更改数据,ExecuteNonQuery()
    SqlDataReader
    数据适配器
    数据库连接-引用配置文件
    sql获取当前时间
  • 原文地址:https://www.cnblogs.com/liuzhuqing/p/7480083.html
Copyright © 2011-2022 走看看