本部分描述当您在多用户环境中工作时需要注意的几个编程问题。
如果您的数据库包含在单个用户工作站中,且不访问来自另一计算机的数据。则您的程序 可任意修改数据。在所有其他情况下,您必须考虑一种可能性,即,在您的程序正在修改 数据时,另一程序正在读取或修改同一数据。将这种情况描述为并发:同一时刻对相同数 据的两处或多处独立的使用。本部分讨论并发、锁定和隔离级别。
本部分还描述语句高速缓存特性,它可减少每一会话的内存分配,并加速查询处理。语句 高速缓存存储那些稍后在使用相同的SQL语句的不同的用户会话之中共享的数据。
11.1并发和性能
在多编程系统中,要有好的性能,并发至关重要。为了保证某一时刻仅一个程序可使用数 据,当访问序列化的数据时,处理会显著减慢。
11.2锁定和完整性
除非对数据的使用作出控制,否则,并发可导致许多负面效果。程序可读取过时的数据, 或可丢失所做的修改,即使表面上已经完成了它们。
要防止此类错误,数据库服务器强加一个锁定系统。锁定是程序可在数据块上放置的声明 或保留。只要锁定数据,数据库服务器保证没有其他程序可修改它。当另一程序请求该数 据时,数据库服务器或者让该程序等待,或者让其返回并报错。
11.3锁定和性能
由于锁定序列化对一块数据的访问,因此,它减少并发;任何想要访问该数据的其他程序 都必须等待。数据库服务器可在单个行、磁盘页、整个表或整个数据库上放置锁。(磁盘 页可能保存多行,且一行可能需要多个磁盘页。)它越是放置锁,它锁定的对象越大,并 发降得越低。锁越少,锁定的对象越小,并发和性能越高。
下列部分讨论您可如何使您的程序实现下列目标:
•放置所有必要的锁以确保数据完整性。
•锁定与前面的目标可能相一致的最少、最小的数据块。
11.4并发问题
要理解并发的危险性,您必须从多个程序的方面考虑,每一程序以它自己的速度执行。假 设您的程序通过下列游标正在访存行:
EXEC SQL DECLARE sto_curse CURSOR FOR
SELECT * FROM stock
WHERE manu_code = 'ANZ';
将每一行从数据库服务器传送到程序都花费时间。在传送期间和传送之间,其他程序可执 行其他数据块操作。大约在您的程序访存由那个查询产生的行的同一时刻,另一用户的程 序可能执行下列更新:
EXEC SQL UPDATE stock
SET unit_price = 1.15 * unit_price
WHERE manu_code = 'ANZ';
换句话说,两个程序都在通读同一个表,一个正在访存某些行,而另一个正在更改相同的 行。可能出现下列情况:
您的程序仅向您显示更新了的行。
您的程序仅向您显示原始的行。
您的程序可能返回的既有原始的行,也有更新了的行。
您的程序可向您显示的既有原始的行,也有在数据库中不再存在的更新了的行。
前两种可能性是无害的。在第1种可能的情况中,在您的查询开始之前完成了更新。更新 是在一毫秒之前结束的,还是一周前结束的,并无差异。
在第2种可能的情况中,实际上,您的查询在更新开始之前完成。其他程序可能紧跟在您 的程序之后只处理了一行,或它可能直到明晚才开始;这没有关系。
然而,后两种可能的情况对于某些应用程序的设计可至关重要。在第3种可能的情况下, 查询返回的既有更新了的数据,也有原始数据。在某些应用程序中,那种结果可能是有害 的。在其他程序中,诸如计算所有价格的平均值,根本无关紧要。
由于取消了它们的事务,如果程序返回一些在表中不可再找到的数据行,则第4种可能的 情况是灾难性的。
当您的程序使用游标来更新或删除最后访存的数据时,需要引起另外的关注。下列的事件 序列产生错误的结果:
•您的程序访存该行。
•另一程序更新或删除该行。
• 您的程序更新或删除 WHERE CURRENT OF cursor_name。
要控制诸如此类的事件,请使用数据库服务器的锁定和隔离级别特性。
11.5锁定如何工作
GBase 8s数据库服务器支持复杂的、灵活的锁定特性的集合,本部分中描述这些主题。
11. 5. 1锁的种类
下表展示GBase 8s数据库服务器对不同情况支持的锁的类型。
锁类型 |
用途 |
共享的 |
共享锁为了只读保留它的对象。它防止在该锁保留期间更改该对象。多个程序可 在同一对象上放置共享锁。在以共享的模式锁定记录时,多个对象可读取该记录。 |
排他的 |
排他锁为了单个程序的使用保留它的对象。当程序打算更改该对象时,使用此锁。 您不可在存在任何其他种类锁的地方放置排他锁。在您放置排他锁之后,您不可 在同一对象上放置另一锁。 |
可提升 的(或 更新) |
可提升的(或更新)锁有更新的意图。您仅可在不存在其他可提升锁或排他锁的 地方放置它。您可在已有共享锁的记录上放置可提升锁。当程序要更改该锁定了 的对象时,您可将该可提升锁提升为排他锁,但仅当该锁可能从可提升的更改为 排他的时刻,该记录上没有包括共享锁在内的其他锁时。如果当设置了可提升锁 时,共享锁在该记录上,则在将可提升锁提升为排他锁之前,您必须删除共享锁。 |
11. 5.2锁作用域
您可将锁应用于整个数据库、整个表、磁盘页、单个行或索引键值。正被锁定的对象的大 小称之为锁的作用域(也称为锁颗粒度)。通常,锁的作用域越大,并发降得越低,但编 程越简单。
您可锁定整个数据库。打开数据库的操作在数据库的名称上放置一共享锁。使用
CONNECT, DATABASE或CREATE DATABASE语句打开数据库。只要程序有一数据 库是打开的,则该名称上的共享锁防止任何其他程序删除该数据库或在其上放置排他锁。
下列语句展示您可能如何排他地锁定整个数据库:
DATABASE database_one EXCLUSIVE
如果没有其他程序已打开了那个数据库,则此语句成功。放置该锁之后,其他程序不可打 开该数据库,即使是读取也不可,因为它在该数据库名称上放置共享锁的尝试会失败。
仅当关闭该数据库时,才释放数据库锁。可使用DISCONNECT或CLOSE DATABASE语 句来显式地执行那个操作,或通过执行另一 DATABASE语句来隐式地执行。
由于锁定数据库导致那个数据库中的并发降低为零,因此,它使得编程非常简单;不可发 生并发效果。然而,仅当其他程序不需要访问时,才应锁定数据库。在非高峰期间,对数 据进行大量更改之前,通常使用数据库锁定。
您可锁定整个表。在某些情况下,数据库服务器自动地执行此操作。您还可使用LOCK TABLE语句来显式地锁定整个表。
LOCK TABLE语句或数据库服务器可放置下列类型的表锁:
共享锁
任何用户都不可写表。在共享模式下,数据库服务器在表上放置一个共享锁,其通 知其他用户不可执行更新。此外,数据库服务器为每个更新了的、删除了的或插入 了的行添加锁。
排他锁
任何其他用户不可从该表读取或写该表。在排他模式下,数据库服务器仅在该表上 放置一个排他锁,不论它更新多少行。排他的表锁防止该表的任何并发使用,因此, 如果其他程序正在争夺对该表的使用,则可严重影响性能。然而,当您需要更新表 中的大部分行时,请在表上放置排他锁。
使用LOCK TABLE语句来锁定表
事务告诉数据库服务器通过LOCK TABLE语句来使用表级别锁定。下列示例展示如何在 表上放置排他锁:
LOCK TABLE tab1 IN EXCLUSIVE MODE
下列示例展示如何在表上放置共享锁:
LOCK TABLE tab2 IN SHARE MODE
提示:在提供更大的并发时,您可为数据库服务器设置隔离级别来获得与共享的表锁相同 的保护程度。
在数据库服务器对任何下列语句执行操作时,它总是锁定整个表:
ALTER FRAGMENT
ALTER INDEX
ALTER TABLE
CREATE INDEX
DROP INDEX
RENAME COLUMN
RENAME TABLE
完成该语句(或事务结束)会释放该锁。在某些查询期间也可自动地锁定整个表。
使用ONLINE关键字来避免表锁定
对于未以IN TABLE关键字选项定义的索引,当您使用ONLINE关键字来CREATE或 DROP索引时,您可最小化索引了的表上的排他锁的持续时间。
在联机创建或删除索引时,不支持表上的DDL操作,但当发出了 CREATE INDEX或 DROP INDEX语句时,可完成并发了的操作。直到没有其他进程正在并发地访问该表时, 才创建或删除指定的索引。然后,短暂地保持锁来写与该索引相关联的系统目录数据。这 提高了系统的可用性,因为通过正在进行的会话或新会话仍可读该表。下列语句展示如何 使用ONLINE关键字来避免在使用CREATE INDEX语句时的自动表锁定:
CREATE INDEX idx_1 ON customer (Iname) ONLINE;
然而,对于使用IN TABLE关键字选项定义的“表中”索引,在包括ONLINE关键字的 CREATE INDEX或DROP INDEX操作期间,该索引了的表保持锁定。其他会话尝试访问 该锁定了的表时,会失败并发出这些错误之一:
107: ISAM error: record is locked.
211: Cannot read system catalog (systables).
710: Table (table.tix) has been dropped, altered or renamed.
您可锁定表的一行。在其他程序继续处理同一表的其他行时,程序可锁定一行或选择的多 行。
行和键锁定不是缺省的行为。当您创建表时,您必须指定行级别锁定。下列示例创建带有 行级别锁定的表:
CREATE TABLE tab1