关系数据库性能问题

一、任务描述

工作中的一个数据批量任务,涉及到4张基本表和4张业务数据表。
基本表 (Basic Table) 数据量不大,每个表最多几百条记录;业务表 (Transaction Table) 数据量较大,每个表有几十万条记录。

以前的版本使用OO(O/R ?)方式,
(1) SQL查询数据库选出一个业务表的数据,每条记录映射为一个Object。
(2) 循环每个Object,根据属性 查询数据库,取出关联的表数据,映射为Object;依此类推,一步步取出相关数据Object。然后计算并生成结果数据。

这种方案,第一次取出的数据集很大,循环步数很多,而且每一步里面都要涉及到多次数据库查询,速度很慢,处理一个月的上万条数据,都需要几个小时。类似于
[code:1]
Select * from Transact_A where ….
While(rs.next()){
a.populate(rs);

If(a…) {
Select * from Transact_B where ….
If( … ) select * from Basic_B where ….
}else{
Select * from Basic_A where ……
}
….
}
[/code:1]

后续版本,我决定采用 Join Table的方式来处理,试图用一个复杂的Big Join Query / View 一次把所有的相关数据记录全都取出来。整个处理过程中,只需要一次数据库查询。

[code:1]
Select * from (Transact_A left join Basic_B on ….) inner join Transact_B on .., C, D, E… Where … < …. > … not exists… a lot of conditions.

While(rs.next()){
a.populate(rs);
b.populate(rs);

if(a… b.. ) …

}
[/code:1]

一部分 Business Logic (条件比较等)移动到SQL里面,代码的可读性变差,运行速度提高。
由于业务逻辑的复杂性,一条SQL很难恰好选出需要处理的数据,为了最大限度的减少不必要的数据,有些比较大的复合View还被多次引用。
几千条数据,这条SQL工作还好,几秒钟、十几秒钟就返回结果集;几万条数据,这条SQL需要几分钟才能够返回;几十万条数据,运气好,10多分钟、几十分钟返回,运气不好,就干脆不返回了。

二 SQL Optimization
Oracle网站有Performance Tuning的PDF文档,详细介绍了语句置换,hint, index, explain plan, SQL Trace等方法。
这里有一篇比较全的翻译的SQL调优文章,
http://www.chinaunix.net/jh/19/214182.html

其中的第15条明确说明,SQL中的 = 操作符 支持 Tuple元组操作,这个 = 可以用来给一个元组Tuple赋值,也可以比较两个元组Tuple。
[code:1]
15. 减少对表的查询
在含有子查询的SQL语句中,要特别注意减少对表的查询.
例如:
Slow:
SELECT TAB_NAME
FROM TABLES
WHERE TAB_NAME = ( SELECT TAB_NAME
FROM TAB_COLUMNS
WHERE VERSION = 604)
AND DB_VER= ( SELECT DB_VER
FROM TAB_COLUMNS
WHERE VERSION = 604)

Fast:
SELECT TAB_NAME
FROM TABLES
WHERE (TAB_NAME,DB_VER)
= ( SELECT TAB_NAME,DB_VER)
FROM TAB_COLUMNS
WHERE VERSION = 604)

Update 多个Column 例子:
Slow
UPDATE EMP
SET EMP_CAT = (SELECT MAX(CATEGORY) FROM EMP_CATEGORIES),
SAL_RANGE = (SELECT MAX(SAL_RANGE) FROM EMP_CATEGORIES)
WHERE EMP_DEPT = 0020;

Fast
UPDATE EMP
SET (EMP_CAT, SAL_RANGE)
= (SELECT MAX(CATEGORY) , MAX(SAL_RANGE)
FROM EMP_CATEGORIES)
WHERE EMP_DEPT = 0020;
[/code:1]

其中的第8条,我恰好用得上。
[code:1]
8. 使用DECODE函数来减少处理时间
使用DECODE函数可以避免重复扫描相同记录或重复连接相同的表.
例如:
SELECT COUNT(*),SUM(SAL)
FROM EMP
WHERE DEPT_NO = 0020
AND ENAME LIKE ‘SMITH%’;
SELECT COUNT(*),SUM(SAL)
FROM EMP
WHERE DEPT_NO = 0030
AND ENAME LIKE ‘SMITH%’;
你可以用DECODE函数高效地得到相同结果
SELECT COUNT(DECODE(DEPT_NO,0020,’X’,NULL)) D0020_COUNT,
COUNT(DECODE(DEPT_NO,0030,’X’,NULL)) D0030_COUNT,
SUM(DECODE(DEPT_NO,0020,SAL,NULL)) D0020_SAL,
SUM(DECODE(DEPT_NO,0030,SAL,NULL)) D0030_SAL
FROM EMP WHERE ENAME LIKE ‘SMITH%’;
类似的,DECODE函数也可以运用于GROUP BY 和ORDER BY子句中.
[/code:1]

正好用来更新 parent 纪录中关于 child的统计字段。以前我这么写,
[code:1]
update parent p
set
balance_1 = (select sum(amount) from child c where c.parent_id = p.id and type in (1, 2))
balance_2 = (select sum(amount) from child c where c.parent_id = p.id and type in (3, 4))
where …
[/code:1]

现在我这么写,
[code:1]
update parent p
set
(balance_1, balance_2) = (
select
sum(decode(type, 1, amount, 2, amount, null)) as balance_1,
sum(decode(type, 3, amount, 4, amount, null)) as balance_2,
from child c where c.parent_id = p.id)
where …
[/code:1]

三 临时表
优化了半天,瓶颈在于对其中一个Big View的多次使用上。只好用空间换时间,把这个多次使用的View先放到临时表里面。
业务逻辑有这样的匹配逻辑,选择最符合条件的数据,其他的数据不用。
[code:1]
Select * from
(Select * from temp
where …
order by column_1, column_2, column_3) t
where t.rownum = 1 -- 只选择排在最前面的一条
[/code:1]

如果直接用上面这段SQL来过滤temp数据,比较麻烦。我干脆又在temp里面多加了一个额外字段match_level, 专门用来存放匹配级别。
创建临时表数据的时候,直接生成这个match_level。
[code:1]
Insert into temp
select
big_view.*,
decode(column_1, … ) || decode(column_2, …) || decode(column_3, …) as match_level
from big_view where …
[/code:1]

这里把多个column排序结果综合到一个字段里面,便于后面的比较。
于是,我就可以用一条相对比较简单的SQL删除掉 不是最佳匹配的纪录。
[code:1]
delete temp
where exists(
select * from
(select id, max(match_level) from temp t group by id having count(*) > 1) t
where temp.id = t.id and temp.match_level < t.match_level)
[/code:1]

处理一个月的数据,上万条记录的情况下,上面这两条insert和 delete执行时间加起来在10 -- 20秒左右。后面的一个Big Query就可以用这个temp table直接把恰好要使用的数据取出来,10秒左右就可以返回结果集(几万条)。处理这个结果集的时间,几分钟左右。
处理24个月的几十万条数据,需要几十分钟。速度提高了几十倍。

既然为了速度,用了这么多vendor native SQL feature, Big Join, temp table, 已经没有移植性可言,为什么不干脆用PL/SQL?
我对PL/SQL语法不熟悉,只是尽量把条件判断过滤放到SQL里面,而重要的计算公式部分(还是比较复杂的)在Java里面处理。我觉得,SQL即使带一点Native feature,也应该比Stored Procedure容易移植。

四 对象数据库

关系数据库号称 更快,支持更复杂的数据类型和关系,数据间的关联查询更快。

不算那些Java, C++等Object的存储工具,只算那些称得上DB的,开源的Object Database有db4object, OZONE, GigaBase(Object Relational Database)等。
(Berkeley DB勉强算得上Object Database?)
存储空间和性能还无法和商业数据库比。不像关系数据库,有了MaxDB, Ingres, PostgreSQL等比较成熟的开源关系数据库。
商业对象数据库挺多,这里就不罗列了。我看的资料比较的多的是Objectivity/DB和Intersystems Cache’。
http://www.objectivity.com
http://www.intersystems.cn
http://www.intersystems.com

两者都号称大数据量、高并发、高性能。通过看过的文档和资料,我更看好Cache’一些。似乎Cache’ 更快,更便宜,支持关系模型更好。Cache’号称 后关系数据库 Post Relational Database。当然,Objectivity是purer Object Database,面向对象特性也许更多一些。

关于对象数据库性能,有两种相反的说法。

1. 对象数据库 比 关系数据库 快
引用

原文
http://www.cnblogs.com/xgchang/archive/2004/12/05/26474.html
⑷性能的比较
ODBMS和RDBMS产品数据存取性能的差别已按通用测试标准验证过了。SUN公司的Riok Cattell等人著的“对象数据库评估”(1991年对象世界会议文集)已对四个ODBMS产品:Objectivity,Objectstore,Ontos,Versant以及两个RDBMS产品:Sybase和Ingress进行了测试。一般说来,对于“冷”数据存取(对磁盘数据库存取)ODBMS比RDBMS平均快5倍;对于“热”数据存取(在内存中的数据库存取)要快30倍,对于“热导航”(在内存中对某一给定的对象访问与之相联系的所有对象)ODBMS任何一个产品都比RDBMS的每一个产品性能要高出三个数量级。


Objectivity/DB自己的Bench Mark文章这样写,在小数据量下,RDBMS(Oracle)更快,数据量越大,Objectivity就会比RDBMS快的越多。

2. 对象数据库 比 关系数据库 慢
引用

原文
http://www.fic.se/MScThesis.Frost.Beta.pdf

Multi-dimensional and relational data warehouses have problems.
expressing complex data types. Object orientation on the other hand deal with complex
data types but lacks in performance on the database technology side.

object-oriented database management systems (OODBMS) the poor performance on ad- hoc queries.

object-oriented data warehouse still face the problem of ad-hoc query performance on large datasets, a problem which multi-dimensional, relational and hybrid techniques overcomes.





原文
http://www.intersystems.co.jp/forum2004/pdf/cache.pdf

从这张图可以看出,Objectivity比关系数据库慢,Cache’比关系数据库快。
  • B76c85de-9c8f-4b70-8b64-da325c89860c-thumb
  • 描述:
  • 大小: 59.2 KB
评论
bluemeteor 2006-04-24
Tao 写道
"九层之台,起于累土",你所解决是问题,本不该到现在才来解决!
真正的性能解决之道,不在于SQL Optimization,也不在于数据库的优劣,更不在于一些所谓的"偏方".

"为之于未有,治之于未乱",一个精心设计的系统,不会存在复杂的SQL,也不会存在大规模集中式的计算,但它却可以用惊人的速度为你展示结果.

这是个什么样的系统呢?其实说起来非常简单(只是说起来),因为它会为它将要解决的问题而作好充分准备,利用它的业余时间,默默无闻地准备着,你甚至感觉不到它正在做这件事.当你向它提问时,它会利用它平时思考的结果或稍加修饰地呈现给你,到这时才来翻课本不是它的一贯作风.

对于SQL Optimization,我认为它对性能优化的作用是很有限的,你今天做了几个SQL Optimization,就意味作明天要作更多的SQL Optimization,这就好比人生病了要吃药,但是药就有三分毒,好了这里,坏了那里,有时还得不偿失,因此最好的办法就是平时多锻炼,多保养,不生病就是了.


楼上诸位 大哉斯言 十分受教

以前非常崇拜甚至痴迷于SQL 尤其是在mysql4.x下 希望能够通过一些自认为是艺术作品的sql语句来解决mysql不能支持的问题 可惜现实会告诉你,在商业开发里,真正的艺术品就是最简单的select * from ta。而且sql语句只是众多优化方法之一,不值得花费太多的精力和寄托,我自己便是个反面教材:)

TAO和楼主的观点是没有冲突,一个系统的优化是讲究的‘时时’和‘处处’ 不放过任何一个微小的细节 不浪费任何的CPU时间(我的server最繁忙的时间就是凌晨4点^_^) 把握好从磁盘I/O到DBsql每一个我们能用到的技术手段 这才是一个完美的优化方案
lixigua 2006-04-15
看了搂住提到的SQL优化,感觉很有作用。
不过搂住提到十几万条记录操作就是十几分钟是我不敢想象的事情,不知道搂住的机器性能如何?
如果像我们这样的大数据量操作怎么办?
歆渊 2006-04-14
呵呵,楼主还来么? 有没有兴趣试试TOB?

http://tob.ableverse.org
licy 2005-04-09
Tao 写道
"九层之台,起于累土",你所解决是问题,本不该到现在才来解决!
真正的性能解决之道,不在于SQL Optimization,也不在于数据库的优劣,更不在于一些所谓的"偏方".

"为之于未有,治之于未乱",一个精心设计的系统,不会存在复杂的SQL,也不会存在大规模集中式的计算,但它却可以用惊人的速度为你展示结果.

这是个什么样的系统呢?其实说起来非常简单(只是说起来),因为它会为它将要解决的问题而作好充分准备,利用它的业余时间,默默无闻地准备着,你甚至感觉不到它正在做这件事.当你向它提问时,它会利用它平时思考的结果或稍加修饰地呈现给你,到这时才来翻课本不是它的一贯作风.

对于SQL Optimization,我认为它对性能优化的作用是很有限的,你今天做了几个SQL Optimization,就意味作明天要作更多的SQL Optimization,这就好比人生病了要吃药,但是药就有三分毒,好了这里,坏了那里,有时还得不偿失,因此最好的办法就是平时多锻炼,多保养,不生病就是了.

这个世界从来就不是完美的
mooniscrazy 2005-04-08
复杂的sql,性能肯定好不到哪里去。
高性能的系统,要尽可能使用简单的sql。
在一次会话中select很多次,很容易把数据库搞死。递归和循环代码中的查询,一般是要避免的。
基本表映射成一个行的集合,这个集合是Singleton的,并且可以按ID找出行。那么基本表可以不用每次都查询。在基本表集合第一次被请求时查询一次数据库,然后就保存在内存中。当基本表改变的时候,刷新基本表行的集合。
业务表映射成业务对象。业务对象中引用基本表的对象的时候,到基本表集合中去找,而不是去查数据库。这样应该可以避免join或者循环的select了。join也是比较消耗服务器资源的,特别是join的表比较多和层次比较深的情况。
大致是这样的[code:1]
class BaseObj{
int getId();
...
}
//这个Collection最好自己实现,提高性能,避免在查找中遍历集合
class BaseObjectList{
BaseObj findById(int id);
static getInstance();
}
class BusinessObject{
...
int baseObjId;
BaseObj getBaseObj(){
[b]return BaseObjectList.getInstance().findById(baseObjId);[/b]
}
}
[/code:1]
对于大表Join小表的情况,提高性能解决方法就是把小表始终放到内存里。
husthxd 2005-04-06
基本上属于数据库优化调优的范畴.
在itpub上查找相关调优的帖子把.
xiaoyu 2005-01-26
用硬件解决吧。分布式计算。
buaawhl 2005-01-26
我做的需求是这样:
当用户改变了某几个参数,submit,所有相关的大批数据都要调整,重新计算。

Tao 写道
"九层之台,起于累土",你所解决是问题,本不该到现在才来解决!
真正的性能解决之道,不在于SQL Optimization,也不在于数据库的优劣,更不在于一些所谓的"偏方".

"为之于未有,治之于未乱",一个精心设计的系统,不会存在复杂的SQL,也不会存在大规模集中式的计算,但它却可以用惊人的速度为你展示结果.

这是个什么样的系统呢?其实说起来非常简单(只是说起来),因为它会为它将要解决的问题而作好充分准备,利用它的业余时间,默默无闻地准备着,你甚至感觉不到它正在做这件事.当你向它提问时,它会利用它平时思考的结果或稍加修饰地呈现给你,到这时才来翻课本不是它的一贯作风.

对于SQL Optimization,我认为它对性能优化的作用是很有限的,你今天做了几个SQL Optimization,就意味作明天要作更多的SQL Optimization,这就好比人生病了要吃药,但是药就有三分毒,好了这里,坏了那里,有时还得不偿失,因此最好的办法就是平时多锻炼,多保养,不生病就是了.


多说说。有没有什么案例的具体相关描述?
jackyz 2005-01-26
Tao 写道
"九层之台,起于累土",你所解决是问题,本不该到现在才来解决!
真正的性能解决之道,不在于SQL Optimization,也不在于数据库的优劣,更不在于一些所谓的"偏方".

"为之于未有,治之于未乱",一个精心设计的系统,不会存在复杂的SQL,也不会存在大规模集中式的计算,但它却可以用惊人的速度为你展示结果.

这是个什么样的系统呢?其实说起来非常简单(只是说起来),因为它会为它将要解决的问题而作好充分准备,利用它的业余时间,默默无闻地准备着,你甚至感觉不到它正在做这件事.当你向它提问时,它会利用它平时思考的结果或稍加修饰地呈现给你,到这时才来翻课本不是它的一贯作风.

对于SQL Optimization,我认为它对性能优化的作用是很有限的,你今天做了几个SQL Optimization,就意味作明天要作更多的SQL Optimization,这就好比人生病了要吃药,但是药就有三分毒,好了这里,坏了那里,有时还得不偿失,因此最好的办法就是平时多锻炼,多保养,不生病就是了.


tao的意思就是先算,预先准备数据,在需要的时候,从中拎出来做展示.

这个方法固然很好,对于很多应用也很有必要,但因为不是实时查询,所有并不能适合所有的情况.

如果这个复杂的SQL是系统的基本查询呢?难不成也让用户几十分钟之后才能看到自己刚刚提交的结果?

优化自然有它的必要.
zzeric 2005-01-25
楼上说得轻巧。等一个系统的AI发展到这个水平,估计CPU也发展到无须为SQL Optimization伤脑筋的程度了
Tao 2005-01-25
"九层之台,起于累土",你所解决是问题,本不该到现在才来解决!
真正的性能解决之道,不在于SQL Optimization,也不在于数据库的优劣,更不在于一些所谓的"偏方".

"为之于未有,治之于未乱",一个精心设计的系统,不会存在复杂的SQL,也不会存在大规模集中式的计算,但它却可以用惊人的速度为你展示结果.

这是个什么样的系统呢?其实说起来非常简单(只是说起来),因为它会为它将要解决的问题而作好充分准备,利用它的业余时间,默默无闻地准备着,你甚至感觉不到它正在做这件事.当你向它提问时,它会利用它平时思考的结果或稍加修饰地呈现给你,到这时才来翻课本不是它的一贯作风.

对于SQL Optimization,我认为它对性能优化的作用是很有限的,你今天做了几个SQL Optimization,就意味作明天要作更多的SQL Optimization,这就好比人生病了要吃药,但是药就有三分毒,好了这里,坏了那里,有时还得不偿失,因此最好的办法就是平时多锻炼,多保养,不生病就是了.
buaawhl 2005-01-22
五 Object Relational Database 和O/R Mapping

主流关系数据库厂商都在产品中加入Object特性,支持用户自定义类型,嵌套表等,称为Object Relational Database。
看到的文章和文档里面说,Object Relational Database只是在Relational Database之上增加了一些更多的复杂类型,而基础的Infrastructure没有太大变化。
Relational Database由于 二维表结构的限制,都是Mainframe方式,无法实现 Database Cluster,支持的并发连接访问量有限,如果要提高容量和性能,只能购买更好的主机硬件。Transaction中Lock的粒度也只能是Table或者Row,不是过大就是过小。不知道是不是这样?
Object Database能够实现Database Cluster,分布式对象缓存也支持得不错。数据量增大,并发连接访问量增大的时候,只要在Database Cluster中增加多台一般的计算机。Transaction中Lock的粒度能够控制的很好,只锁住恰好需要处理的Object和Associated Objects。
Objectivity的文档里面这样说,用户可以事先把需要处理的所有关联的Objects放到一个Container里面,然后锁住这个Container。

对象数据库的Java API一般都号称 遵守ODMG3.0 and/or JDO1.0。开源的Object Database实现和 ODMG 实现,大致看了一眼 Ozone 的相关源码,觉得不错。
ODMG和JDO的目的,是提供一个 对象数据库 和 (关系数据库 + O/R) 无差别的API层。关系数据库的O/R Mapping遵守ODMG和JDO,对象数据库的API也遵守ODMG和JDO。这样,不用管下面用的是什么是数据库,用户都通过ODMG和JDO API,当作 对象数据库来使用。

关系数据库都支持标准SQL,但都提供了自己的native enhanced feature。对象数据库更是如此,API中除了包括ODMG或JDO,额外还提供了很多Native Feature的声明调用。我感觉,使用对象数据库,也许比使用 关系数据库更难移植。不知道是不是这样?
O/R Mapping天生就是用来把 关系数据库 包装成 对象数据库的使用方法的,自然就掩盖了各关系数据库之间的不同。

这里有个wiki page,估计是Hibernate Group的人总结的。列出了几乎所有的O/R Mapping tools的各方面特性的比较。有兴趣的朋友,可以在这个wiki page里面加入自己的作品。
引用

原文
http://c2.com/cgi/wiki?ObjectRelationalToolComparison

This page has been created to compare Java ObjectRelationalMapping layers. This should help potential users to make an educated choice of O/R technology and to better understand the existing products.


六 相关资料

引用

Service-Architecture在线文章列表
http://www.service-architecture.com/articles/index.html

其中,
These articles provide a basic background on concepts and standards for database management systems (DBMS). Many of these concepts apply to all forms of database management systems: relational (RDBMS), object (ODBMS), XML (XDBMS), and others as well as object-relational mapping and XML-mapping products


引用

http://www.unixspace.com/context/databases.html

这个很牛。列出来一堆的Database Model.
Hierarchical Model,
Network Model,
Relational Model,
Object/Relational Model,
Object-Oriented Model,
Associative Model,
Entity-Attribute-Value (EAV) data model,

最后提出了自己的作品的Model.
Context Model.
包括了上述所有的Model。



-- ps,
从主题来看,这个帖子的内容更接近《数据库版》。我也许发错了版。
版主能否帮助把这个帖子,移动到《数据库版》? thanks.
buaawhl
搜索本博客
其他分类
存档
最新评论