用关系数据库管理系统。他擅长SQL系统,以及从组织级到部门级的数据建模。Peter多
次出席英国、欧洲、北美的Oracle数据库大会,在许多数据库专业杂志上发表过文章。他现任
英国Oracle用户组委员会主任,可通过peter.robson@justsql.com联系他。
查询的识别
有经验的朋友都知道,把关键系统从开发环境切换到生产环境是一场战役,一场甚嚣尘上的战
役。通常,在“攻击发起日(D-Day)”的前几周,xìng能测试会显示新系统达不到预期要求。于是,
找专家,调优SQL语句,召集数据库管理员和系统管理员不断开会讨论对策。最后,xìng能总算
与以前的系统大致相当了(尽管新系统用的是价格翻倍的硬件)。
人们常常使用战术,而忽略了战略。战略要求从大局上把握整个架构与设计。和战争一样,战
略的基本原则并不多,且经常被忽视。架构错误的代价非常高,SQL 程序员必须准备充分,明
确目标,了解如何实现目标。在本章中,我们讨论编写高效访问数据库的程序需要实现哪些关
键目标。
查询的识别
Query
Identification
数个世纪以来,将军通过辨别军装颜色和旗帜等来判断各部队的位置,以此检查激战中部队行
进情况。同样,当一些进程消耗了过多的CPU 资源时,通常也可以确定是由哪些正被执行的
SQL 语句造成的。但是,要确定是应用的哪部分提jiāo了这些SQL语句却困难得多,特别是复杂
的大型系统包含动态建立的查询的时候。尽管许多产品提供良好的监控工具,但要确定一小段
SQL语句与整个系统的关系,有时却非常困难。因此,要养成为程序和关键模块加注释的习惯,
在SQL中chā入注释有助于辨别查询在程序中的位置。例如:
/* CUSTOMER REGISTRATION */ select blah ...
这些注释在查错时非常有用。另外,注释也有助于判断单独应用对服务器造成的负载有多大;
例如我们希望本地应用承担更多工作,需要判断当前硬件是否能承受突发高负载,这时注释特
别有用。
有些产品还提供了专门的记录功能(registration facilities),将你从“为每个语句加注释”的乏味
工作中解放出来。例如Oracle 的dbms_application_info包,它支持48个字
符的模块名称(module ncom)、32 个字符的动作名称(action ncom)和64个字符的客户信
息,这些字段的内容可由我们定制。在Oracle 环境下,你可以利用这个程序包记录哪个应用
正在执行,以及它在何时正在做什么。因为应用是通过“Oracle V$ 动态视图”(能显示目前内存
中发生的情况)向程序包传递信息的,于是我们可以轻易地掌握这些信息。
总结:易识别的语句有助于定位xìng能问题。
保持数据库连接稳定
Stable
Database
Connections
建立一个新的数据库连接,既快又方便,但这其中往往掩藏着重复建立数据库连接带来的巨大
开销。所以,管理数据库连接必须非常小心。允许多重连接——可能就藏在你的应用中——的
后果可能很严重,下面即是一例。
不久前,我遇到一个应用,要处理很多小的文本文件。这些文本文件最大的也不超过一百行,
每一行包含要加载的数据及数据库等信息。此例中固然只有一个数据库实例,但即使有上百个,
这里所说明的原理也是适用的。
处理每个文件的代码如下:
上述处理工作令人满意,但当大量小文件都在极短的时间内到达时,可能应用程序来不及处理,
于是积压大量待处理文件,花费时间相当可观。
我用C 语言编了个简单的程序来模拟上述情况,以说明频繁的数据库连接和中断所造成的系
统xìng能下降问题。表2-1列出了模拟的结果。
注意
产生表2-1结果的程序使用了常规的insert语句。顺便提一下,直接加载(direct-loading)的技
术会更快。
表2-1:连接/中断xìng能测试结果
Open the file
Until the end of file is reached
Read a row
Connect to the server specified by the row
Insert the data
Disconnect
Close the file
测试结果
依次对每一行作连接/中断7.4 行/秒
连接一次,所有行逐个chā入1 681 行/秒
连接一次,以10 行为一数组chā入5 914 行/秒
连接一次,以100 行为一数组chā入9 190 行/秒
此例说明了尽量减少分别连接数据库次数的重要xìng。对比表中前后两次针对相同数据库的chā入
cāo作,明显发现xìng能有显著提升。其实还可以做进一步的优化。因为数据库实例的数量势必有
限,所以可以建立一组处理程序(handler)分别负责一个数据库连接,每个数据库只连接一次,
使xìng能进一步提高。正如表2-1 所示,仅连接数据库一次(或很少次)的简单技巧,再加上一
点额外工作,就能让效率提升200倍以上。
当然,在上述改进的基础上,再将yù更新的数据填入数组,这样就尽可能减少了程序和数据库
核心间的jiāo互次数,从而使xìng能产生了另一次飞跃。这种每次chā入几行数据的做法,可以使数
据的总处理能力又增加了5倍。表2-1 中的结果显示改进后的xìng能几乎是最初的1 200 倍。
为何有如此大的xìng能提升?
第一个原因,也是最大的原因,在于数据库连接是很“重”的cāo作,消耗资源很多。
在常见的客户/服务器模式中(现在仍广为使用),简单的连接cāo作背后潜藏着如下事实:首先,
客户端与远程服务器的jiān tīng程序(listener program)建立联系;接着,jiān tīng程序要么创建一个
进程或线程来执行数据库核心程序,要么直接或间接地把客户请求传递给已存在的服务器进程,
这取决于此服务器是否为共享服务器。
除了这些系统cāo作(创建进程或线程并开始执行)之外,数据库系统还必须为每
次session建立新环境,以跟踪它的行为。建立新session前,DBMS还要检查密码是否与保存
的加密的账户密码相符。或许,DBMS还要执行登录触发器(logon trigger),还要初始化存储
过程和程序包(如果它们是第一次被调用)。上面这些还不包括客户端进程和服务器进程之间要
完成的握手协议。正因为如此,连接池(connection pooling)等保持永久数据库连接的技术对
xìng能才如此重要。
第二个原因,你的程序(甚至包括存储过程)和数据库之间的jiāo互也有开销。
即使数据库连结已经建立且仍未中断,程序和DBMS 核心之间的上下文切换(context switch)
也有代价。因此,如果DBMS 支持数据通过数组传递,应毫不犹豫地使用它。如果该数组接
口是隐式的(API内部使用,但你不能使用),那么明智的做法是检查它的默认大小并根据具体
需要修改它。当然,任何逐行处理的方式都面临上下文切换的问题,并对xìng能产生严重影响—