• 数据库相关优化方案


    1 背景概述

    在大多数的开发项目中,尤其是集成项目,都会有涉及到数据分析部分的工作,数据分析多数是各种图表的展现和交互(所谓数据可视化),数据分析的展现速度直接影响着用户的体验,而且绝大多数管理系统(MES、PDM/PLM、ERP、SCM、OA、HR等等)的数据都存储在数据库中,数据库相关的性能优化可以较容易提高程序系统的整体性能、提升用户的体验,保障项目的顺利验收。对应用程序进行整体的性能优化需要全局考虑,比如:硬件选型、软件架构、部署架构、程序开发等方面。本文主要侧重介绍程序开发部分的数据库层面的相关优化手段,希望能对大家有所帮助。

    2 预期读者

    1. 数通畅联内部员工
    2. 广大IT相关从业者

    3 优化途径

    无论是开发项目还是集成项目最终的目的是项目的验收,促进项目的回款,保障公司的资金流进一步的运作。但是如果功能程序的性能不过关,响应速度慢进而影响客户的体验,则直接影响着项目的验收,从而阻碍了公司的正常运作。典型的优化途径有:硬件选选型、系统软件、应用程序三个途径。

    3.1 硬件选型

    选择什么样的服务器都会遇到一个相同的问题,那就是选择什么硬件配置的服务器。在日常的项目工作中会将服务器区分为:应用服务器、数据库服务器、文件服务器以及其他服务器。

    • 应用服务器:一般用于业务系统功能的部署以及应用系统的部署服务器,对CPU、内存、要求都比较高。(推荐配置:CPU 3.0G Hz 及以上、4核及以上 内存 32G 硬盘 500G(RAID10)) 。
    • 数据库服务器:数据服务器对CPU、内存、磁盘的要求都很高,在实际应用中如果某个硬件是短板都会带来性能问题。(推荐配置:CPU 3.0G Hz 及以上、4核及以上,内存 16G 及以上、固态硬盘 1T(RAID10))。
    • 文件服务器:文件服务器主要是对IO、硬盘大小要求较高,内存较低。(推荐配置:CPU 3.0G Hz 及以上、内存 4G及以上、硬盘 2TB(RAID5))。
    • 其他服务器:至于其它服务器就看各位的具体需要具体分析了。

    通常情况下硬件配置越高,性能越好,但是综合考虑(money!)硬件配置一般能满足展望未来3-5年性能要求即可。注意:云服务器现在也是可以考虑的选择。

    3.2 系统软件

    在操作系统的选择上最常见的就是Linux以及Windows,考虑到服务器的性能、安全性通常我们选择Linux操作系统。虽然Server版本操作系统本身的性能已经相对稳定,但是我们可以优化对应操作系统的配置来进一步匹配对应项目的性能需求,而Linux系列的操作系统相对来说有更多的优化策略和空间,更重要的是运维尤其远程运维很方便。

    3.3 应用程序

    衡量一个程序的标准首当其冲的是程序的安全性,然后则是程序的性能,也就是程序的响应速度。对于程序的保密性要求并不是所有行业均是严格要求的那么对于程序的性能则是不区分行业均是更改的性能带来更好的体验。

    应用程序的优化必杀技通常来说就是程序(软件)本身支持水平扩展,很多书籍都有介绍,百度关键字:大型系统架构,可以了解很多相关知识,水平扩展是另外一个话题,这个话题也会涉及到很多方面,在本文中就不一一赘述。

    系统程序的基础环境调优对应用程序的优化也较为明显,比如:Java程序的JVM设置、PHP程序的子进程数配置、.NET程序的认证机制、运行库设置等等。基础环境调优也不是本文阐述重点。在下面我们主要对软件数据库相关的优化方案中进行详细介绍。

    4 优化方案

    虽然NoSQL也开始流行,但是更多场景下它只是数据库的补充,数据库自从诞生开始起就牢牢占据着管理软件后台存储的主场,而且从未离开。数据库层面的性能优化属于短平快调整就能见效、或者在开发中稍微注意就可以大幅度提升性能的常规系统调优手段。

    4.1 整体策略

    我们通常需要从整体策略发的角度出发,将数据库调优从汇总查询、视图方式、数据缓存等三个方面来进行。

    4.1.1 汇总查询

    在日常工作中如果所涉及的查询语句较为复杂,或者需要访问第三方的数据库,而在访问第三方数据库时常常会因为不同的数据库中不同数据表的读取频率不同,进而影响性能。面对这种情况我们通常将需要查询的内容汇总到中间表,然后直接从中间表进行数据查询。

    4.1.2 视图方式

    一般情况下创建视图是不会直接提高性能的,但是如果需要查询的内容涉及到多个数据表之间的关联且关联关系较为复查,查询出的结果集被高频的访问。这时如果没有创建视图那么每要查询这个结果集就需要重新创建SQL,但是如果创建统一的视图并且在创建视图是已经进行SQL调优,方便大家的统一调用从而来提升数据库的性能。

    4.1.3 缓存方式

    当前查询结果的结果集是为展现内容提供数据展现,不是交互性数据操作,不经常被改变是我们可以将数据的查询结果集放入缓存中,这样在读取时在缓存中进行获取,减少了对数据库的访问操作进一步的提升程序的响应速率。在程序应用中常见的缓存处理手段如下:

    静态缓存

    静态缓存通常为创建一个静态的HashMap 变量,在数据获取是判断Map中是否含有,如果有在Map变量中获取,如果没有则在数据库中查询然后放入缓存的Map变量中。

    分布式缓存

    分布式缓存通常是应用于集群部署的场景,通常应用部署于不同的业务服务器,通过Redis 或者Mncached来进行分布式缓存的管理。

    4.2 常规优化

    在数据库优化的方案中最常见也是性能优化的最关键的部分就是数据库的SQL优化,本篇文章分别在查询优化、更新优化、其他说明三个方面来进行说明常见的SQL优化。

    4.2.1 查询优化

    避免在客户端返回大数据量

    尽量避免在客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。如果一定要返回大数据量,考虑使用数据库分页来处理。

    查询避免使用*

    SELECT子句中避免使用*号数据库在解析的过程中,会将*依次转换成所有的列名,这个工作是通过查询数据字典完成的,这意味着将耗费更多的时间。如:

    Select * from emp

    应该为:

    Select id,name,code from emp

    慎用DISTINCT

    用EXISTS替换DISTINCT: 当提交一个包含一对多表信息(比如部门表和雇员表)的查询时,避免在SELECT子句中使用DISTINCT. 一般可以考虑用EXIST替换, EXISTS 使查询更为迅速,因为RDBMS核心模块将在子查询的条件一旦满足后,立刻返回结果. 例子:

    (低效):

    SELECT DISTINCT DEPT_NO,DEPT_NAME FROM DEPT D , EMP E WHERE D.DEPT_NO = E.DEPT_NO

    (高效):

    SELECT DEPT_NO,DEPT_NAME FROM DEPT D WHERE EXISTS ( SELECT ‘X' FROM EMP E WHERE E.DEPT_NO = D.DEPT_NO);

    UNION和UNION-ALL

    用UNION-ALL 替换UNION ( 如果有可能的话): 当SQL 语句需要UNION两个查询结果集合时,这两个结果集合会以UNION-ALL的方式被合并, 然后在输出最终结果前进行排序. 如果用UNION ALL替代UNION, 这样排序就不是必要了. 效率就会因此得到提高. 需要注意的是,UNION ALL 将重复输出两个结果集合中相同记录. 因此还是要从业务需求分析考虑使用UNION ALL的可行性。

    条件子句的注意事项

    创建索引

    对where中的条件列创建索引,可以加快查询速度。对于表中的主键、外键、有对像或身份标识意义的字段视情况添加索引。

    避免null判断

    应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:

    select name from system_users where id is null

    最好不要给数据库留NULL,尽可能的使用 NOT NULL填充数据库。备注、描述、评论之类的可以设置为 NULL,其他的,最好不要使用NULL。不要以为 NULL 不需要空间,比如:char(100) 型,在字段建立时,空间就固定了, 不管是否插入值(NULL也包含在内),都是占用 100个字符的空间的,如果是varchar这样的变长字段, null 不占用空间。

    可以在id上设置默认值0,确保表中id列没有null值,然后这样查询:

    select name from system_users where id = 0

    避免不等于操作
    尽量避免在 where 子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描。

    避免in或not in

    in 和 not in 也要慎用,否则会导致全表扫描,如:

    select id from t where num in(1,2,3)

    对于连续的数值,能用 between 就不要用 in 了:

    select id from t where num between 1 and 3

    很多时候用 exists 代替 in 是一个好的选择:

    select num from a where num in(select num from b)

    用下面的语句替换:

    select num from a where exists(select 1 from b where num=a.num)

    避免对字段进行函数操作

    尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如下:

    select id from t where substring(name,1,3) = ’abc’

    查询所有以abc开头的名字的id

    应改为:

    select id from t where name like 'abc%'

    4.2.2 更新优化

    更新批量使用bach处理

    在程序中尽量避免大量的insert或者delete同时处理,如果遇到这种情况需要使用bach进行批量统一处理。

    避免大批量的insert和delete

    因为这两个操作是会锁表的,表一锁住了,别的操作都进不来了。所以,如果有一个大的处理,一定把其拆分,使用 LIMIT oracle(rownum),sqlserver(top)条件。

    Update注意

    如果只更改1、2个字段,不要Update全部字段,否则频繁调用会引起明显的性能消耗,同时带来大量日志。

    杜绝count(*)

    select count(*) from table;

    这样不带任何条件的count会引起全表扫描,并且没有任何业务意义,是一定要杜绝的。

    4.2.3 事务处理

    在数据库使用中尽量减少长事务

    在数据库中如果涉及到主表、从表、附属从表,这时如果同时操作三个数据表同时成功以及同时失败,如果当前数据表的数据量较大,为了降低数据库的性能压力,我们可以采用批处理方式分别批处理三个数据表来进行数据库性能的提升。

    减少分布式事务的使用

    一般的数据库均是支持分布式事务,当涉及到跨数据库的不同数据表的操作时我们可以使用分布式事务。但为了提高性能损耗,尽量减少这种强一致性需求,更多情况下转化为最终一致性方式来满足业务需求,通常来说引入消息中间件是这种场景下的常规解决手段。

    4.2.4 其他说明

    多用varchar和nvarchar

    尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。

    减少大字段的使用

    在数据库中定义类型是尽量避免使用大字段类型如:BLOB、TEXT、LONG以及Object等大对象的类型

    不要在数据库中存储文件

    在程序设计以及数据库存储是不要将图片文件、其他日志文件的文件类型存储于数据库中,而是在数据库中存储文件索引的URL将文件存储于文件服务器中。

    4.3 配置优化

    在进行数据库连接操作时,我们可以通过选择合适的驱动、释放连接池中的资源、选择符合应用场景的接口,构造只读结果集来进一步的优化JDBC的配置。下面我们通过连接处理、匹配接口以及返回结果三个方面进行详细的说明。

    4.3.1 连接处理

    对于Java程序而言, Connention的优化通常使用数据连接池(dbcp、proxool、c3p0)来进行Connention对象的管理,这样程序的灵活性强,便于移植。但要注意的是对象池里中是没有回收机制,并且对象池里有容量限制,对于对象池里的闲置对象尽早的释放资源

    下面来简单说明不用的连接池的对比:

    Dbcp(DataBase connection pool):是apache上的一个 java连接池项目。

    优点:配置方便,可以设置最大和最小连接,连接等待时间等,持续运行的稳定性,速度快。

    缺点:没有自动的去回收空闲连接的功能,大并发量的压力下稳定性不高,不能够进行连接池监控。

    ProxoolProxool是一种Java数据库连接池技术。是sourceforge下的一个开源项目。

    优点:可以设置最大和最小连接,具备监控功能。

    缺点:明显的性能问题持,续运行的稳定性不高。

    C3p0是在Hibernate和Spring中默认支持该数据库连接池,实现了数据源和jndi绑定,支持jdbc3规范和jdbc2的标准扩展。

    优点:支持高并发,异步操作,有自动回收空闲连接功能。

    缺点:没有Dbcp的速度快。

    4.3.2 匹配接口

    对于Statement对象的优化,我们需要根据不同的应用场合选择合适的Statement接口。如:

    Statement不带参数,例如:查询时,不需要到任何参数。

    PreparedStatement PreparedStatement可以写参数化查询,比Statement能获得更好的性能,可以阻止常见的SQL注入式攻击,提高安全性。

    CallableStatement专门针对存储过程,使用它能享受到所有存储过程带来的优势,但也包括存储过程带来的劣势如Java程序可移植性查,依赖数据库等。

    4.3.3 返回结果

    优化结果集(ResultSet)查询时候,返回的结果集有不同的类型。结果集分两种类型:只读和可更改。返回的结果集默认就是只读的。而在Oracle中我们可以设置手工加锁语句(Select XXX forUpdate)。

    明确指定主键,并且有此数据则锁定若无则不锁定

    SELECT * FROM products WHERE id='3' FOR UPDATE;

    无主键或者主键不明确则进行表锁定

    SELECT * FROM products WHERE name='Mouse' FOR UPDATE;

     

    5 个人总结

    应用程序优化是一个系统工程,需要综合考虑,更多时候要提前考虑,在系统架构层面来保障系统具有更多优化的能力。系统运维有一种消极的说法,系统能用就行,不要轻易去改变;但对于系统开发而言,每一次代码重构都是一次系统调优以及增强调优能力的机会。Devops也慢慢开始盛行了,开发和运营越来越密切,甚至是一套班子两种角色,你(们)如何选择?我个人而言,倾向主动调优、拥抱变化,即便可能带来一些风险。

    无论是对公司的产品进行开发还是在项目开发的过程中,要在全局的角度出发整体考虑、制定规范、落实到每一项的工作中,从制度上保障系统性能调优的能力。笔者作为数通畅联公司的一名技术员工,今天将自己所学所用的常见的数据库优化相关处理总结出来与大家分享。如果对本文档相关的描述信息存在疑问欢迎加入数通畅联官方技术群(299719834)进行讨论。

  • 相关阅读:
    Json字串转换成Java复杂对象
    [Code Snipper]图片轮换
    将CSDN600W用户及密码帐号存入本地MySql数据库
    【转】一个隐形的java int溢出
    【转】展望未来,总结过去10年的程序员生涯,给程序员小弟弟小妹妹们的一些总结性忠告
    如何在Android 4.0 ICS中禁用StatusBar | SystemBar | 状态栏 【完美版】
    【转】提问的智慧(How To Ask Questions the Smart)
    商业开发实战之VB篇精彩视频
    我的设计原语
    RAPIDXML 中文手册,根据官方文档完整翻译!
  • 原文地址:https://www.cnblogs.com/agileai/p/7000064.html
Copyright © 2020-2023  润新知