摘要 结合DB的使用经验从数据库设计查询优化并发控制客户/服务器模式四个方面来讨论数据库应用系统性能优化的一些原则方法等
关键词 DB 性能优化 数据库设计 查询优化 并发控制 C/S模式
引言
DB是一种高性能的大型关系数据库管理系统广泛的应用在客户/服务器体系结构中评价系统性能优化的标准有吞吐量响应时间并行能力等本文从数据库的设计查询的优化并发控制以及客户/服务器模式这四个角度来讨论优化系统性能
设计数据库
熟悉业务系统
对业务系统的熟悉程度对整个数据库系统的性能有很大影响一个对业务不熟悉的设计人员尽管有丰富的数据库知识也很难设计出性能最佳的数据库应用系统
规范化与非规范化
数据库被规范化后减少了数据冗余数据量变小数据行变窄这样DB的每一页可以包括更多行那么每一区里的数据量更多从而加速表的扫描改进了单个表的查询性能但是当查询涉及多个表的时候需要用很多连接操作把信息从各个表中组合在一起导致更高的CPU和I/O花销那么有很多时候需要在规范化和非规范化之间保持平衡用适当的冗余信息来减少系统开销用空间代价来换取时间代价有订单信息表OrderDetail它里面记录了投递员信息收款员信息物品信息价格策略客户信息…这些信息分别在投递员信息表收款员信息表物品信息表价格策略表客户信息表中存放如果按照规范化的要求OrderDetail查询时就必须要与这么多个表进行连接或者嵌套查询如果OrderDetail表中的数据量是在百万级的那么一次查询所需要的时间可能会达到好几个小时事实上只要在设计时保证数据的逻辑有效性很多信息都可以直接冗余在OrderDetail表中这些冗余的数据能够极大的提高查询的效率从而减少CPU和I/O操作
数据条带化
如果一个表的记录条数超过一定的规模那么最基本的查询操作也会受到影响需要将该表根据日期水平划分把最近最经常用的数据和历史的不经常用的数据划分开来或是根据地理位置部门等等进行划分还有一种划分方式――垂直划分即把一个属性列很多的表分割成好几个小表比如把经常用到的属性放在一个表里不经常用到的属性放在另一个表里这样可以加快表的扫描提高效率
选择数据类型
对每一属性选择什么样的数据类型很大程度上依据表的要求但是在不违背表要求的前提下选择适当的数据类型可以提高系统性能比如有text列存放一本书的信息用BLOB而不是character()BLOB存放的是指针或者文件参照变量真正的文本信息可以放在数据库之外从而减少数据库存储空间使得程序运行的速度提高DB提供了UDT(User Defined Datatypes)功能用户可以根据自己的需要定义自己的数据类型
选择索引
索引是数据库中重要的数据结构它的根本目的就是为了提高查询效率现在大多数的数据库产品都采用IBM最先提出的ISAM索引结构使用索引可以快速直接有序的存取数据索引的建立虽然加快了查询另一方面却将低了数据更新的速度因为新数据不仅要增加到表中也要增加到索引中另外索引还需要额外的磁盘空间和维护开销因此要合理使用索引
●在经常进行连接但是没有指定为外键的属性列上建立索引
●在频繁进行排序或分组(即进行group by或order by操作)的列上建立索引按索引来排序或分组可以提高效率
●在条件表达式中经常用到的不同值较多的列上建立检索在不同值少的列上不要建立索引
●如果待排序的列有多个可以在这些列上建立复合索引(compound index)即索引由多个字段复合而成
查询优化
现在的数据库产品在系统查询优化方面已经做得越来越好但由于用户提交的SQL语句是系统优化的基础很难设想一个原本糟糕的查询计划经过系统的优化之后会变得高效因此用户所写语句的优劣至关重要下面重点说明改善用户查询计划的解决方案
. 排序
在很多时候应当简化或避免对大型表进行重复的排序当能够利用索引自动以适当的次序产生输出时可以避免排序的步骤当以下的情况发生时排序就不能省略
●索引中不包括一个或几个待排序的列
●group by或order by子句中列的次序与索引的次序不一样
●排序的列来自不同的表
为了避免不必要的排序就要正确地增建索引合理地合并数据库表尽管有时可能影响表的规范化但相对于效率的提高是值得的如果排序不可避免那么应当试图简化它如缩小排序列的范围等
. 主键
主键用整型会极大的提高查询效率而字符型的比较开销要比整型的比较开销大很多用字符型数据作主键会使数据插入更新与查询的效率降低数据量小的时候这点降低可能不会被注意可是当数据量大的时候小的改进也能够提高系统的响应速度
. 嵌套查询
在SQL语言中一个查询块可以作为另一个查询块中谓词的一个操作数因此SQL查询可以层层嵌套例如在一个大型分布式数据库系统中有订单表Order订单信息表OrderDetail如果需要两表关联查询
SELECT CreateUser
FROM Order
WHERE OrderNo IN
(SELECT OrderNo
FROM OrderDetail
WHERE Price=)
在这个查询中找出报纸单价为元的收订员名单下层查询返回一组值给上层查询然后由上层查询块再根据下层块提供的值继续查询在这种嵌套查询中对上层查询的每一个值OrderNo下层查询都要对表OrderDetail进行全部扫描执行效率显然不会高在该查询中有层嵌套如果每层都查询行那么这个查询就要查询万行数据在系统开销中对表Order的扫描占%对表OrderDetail的搜索占%如果我们用连接来代替即
SELECT CreateUser
FROM OrderOrderDetail
WHERE OrderOrderNo=OrderDetailOrderNo AND Praice=
那么对表Order的扫描占%对表OrderDetail的搜索占%
而且一个列的标签同时在主查询和where子句中的查询中出现那么很可能当主查询中的列值改变之后子查询必须重新查询一次查询嵌套层次越多效率越低因此应当尽量避免子查询如果子查询不可避免那么要在子查询中过滤掉尽可能多的行
. 通配符
在SQL语句中LIKE关键字支持通配符匹配但这种匹配特别耗费时间例如SELECT * FROM Order WHERE CreateUser LIKE M_ _ _ 即使在CreateUser字段上建立了索引在这种情况下也还是采用顺序扫描的方式Order表中有条记录就需要比较次如果把语句改为SELECT * FROM Order WHERE CreateUser >M AND CreateUser <N在执行查询时就会利用索引来查询显然会大大提高速度
. distinct
使用distinct是为了保证在结果集中不出现重复值但是distinct会产生一张工作表并进行排序来删除重复记录这会大大增加查询和I/O的操作次数因此应当避免使用distinct关键字
. 负逻辑
负逻辑如!=<>not in等都会导致DB用表扫描来完成查询当表较大时会严重影响系统性能可以用别的操作来代替
. 临时表
使用临时表时数据库会在磁盘中建立相应的数据结构因为内存的访问速度远远大于外部存储器的访问速度在复杂查询中使用临时表时中间结果会被导入到临时表中这种磁盘操作会大大降低查询效率另外在分布式系统中临时表的使用还会带来多个查询进程之间的同步问题所以在进行复杂查询时最好不要使用临时表
. 存储过程
DB中的Stored Procedure Builder可以产生存储过程运行并测试存储过程存储过程可以包含巨大而复杂的查询或SQL操作经过编译后存储在DB数据库中用户在多次使用同样的SQL操作时可以先把这些SQL操作做成存储过程在需要用到的地方直接引用其名字进行调用存储过程在第一次执行时建立优化的查询方案DB将查询方案保存在高速缓存里以后调用运行时可以直接从高速缓存执行省去了优化和编译的阶段节省了执行时间从而提高效率和系统利用率
最优的查询方案按照某些标准选择往往不可行要根据实际的要求和具体情况通过比较进行选择DB提供的Query Patroller可以对不同的查询方案的查询代价进行比较通过追蹤查询语句返回查询不同阶段的系统开销从而作出最佳选择DB提供的Performance Monitor也对整个数据库系统的性能进行监控包括I/O时间查询次数排序时间同步读写时间等等
数据库系统的并发控制也能影响系统性能多个用户的同时操作可能导致数据的不一致性DB为了防止同时修改造成数据丢失和访问未被提交的数据以及数据的保护读采用Lock机制来实现控制
DB中可以对表空间表表列和索引加锁锁的粒度越大锁越简单开销小并发度低粒度小锁机制复杂开销大并发度高大型系统在并发处理中如果遇到所要分配的资源处于锁定状态系统会把进程挂起等待如果一个很耗时的查询操作工作于一个经常使用的表上此时使用表一级锁意味着整个系统都要等待你的查询结束以后才能够继续运行所以在复杂查询中尽量避免使用表一级锁如果有这一类的需要该怎么办呢?可以利用视图来解决这一类问题视图避免了对表的直接操作同时有能够保证数据库的高效运转