多个用户同时读取和写入相同数据时,可能发生阻塞和死锁。快照隔离旨在通过维护数据的不同版本提高并发性和一致性。在事务中使用快照隔离时,数据库服务器会返回数据的已提交版本,以响应任何读请求。它在进行此操作时不获取读锁定,因此不会对正在写入数据的用户造成干扰。
快照是数据库中已提交的一组数据。使用快照隔离时,事务中的所有查询都会使用同一组数据。数据库表不会获取任何锁,因此其它事务可以无阻塞地访问并修改数据。SQL Anywhere 支持以下三种快照隔离级别,允许您控制何时使用快照:
快照 从事务读取、插入、更新或删除第一行时开始,使用已提交数据的快照。
语句快照 从语句读取第一行开始,使用已提交数据的快照。事务内的每个语句看到的都是不同时间的数据快照。
只读语句快照 对于只读语句,从读取第一行时开始,使用已提交数据的快照。事务内的每个只读语句看到的都是不同时间的数据快照。对于插入、更新和删除语句,使用由 updatable_statement_isolation 选项指定的隔离级别(可以是 0(缺省值)、1、2 或 3)。
您还可以使用 BEGIN SNAPSHOT 语句,指定何时启动事务的快照。请参见BEGIN SNAPSHOT 语句。
快照隔离在许多情况下都很有用,例如:
执行大量读取操作和很少更新操作的应用程序 快照事务仅为那些修改数据库的语句获取写锁定。如果事务主要执行读取操作,则快照事务不会获取可能干扰其他用户的事务的读锁定。
其他用户需要访问数据时执行长时间运行的事务的应用程序 快照事务不获取读锁定,因此快照事务发生时,其他用户可以读取和更新数据。
必须从数据库读取一致的数据集的应用程序 快照显示特定时间点的已提交数据集,因此您可以使用快照隔离来查看在整个事务中未更改的一致数据,即使其他用户在您的事务运行时对数据进行更改。
快照隔离仅影响所有用户共享的基表和全局临时表。对其它表类型的读取操作决不会看到数据的旧版本,且从不启动快照。仅当将 isolation_level 选项设置为快照,且更新启动一个事务时,对其它表类型的更新才启动快照。
当存在使用 WITH HOLD 子句打开的使用语句或事务快照的游标时,以下语句无法执行。
当使用 WITH HOLD 子句打开游标时,可看到在快照启动时所提交的所有行的快照。还可看到从在其内打开游标的事务启动以来,由当前连接完成的所有修改。
仅当未执行快速截断时允许执行 TRUNCATE TABLE,因为在此情况下,单独的 DELETE 操作随后会被记录在事务日志中。请参见TRUNCATE 语句。
此外,如果从非快照事务中执行这些语句中的任何语句,则已运行的快照事务在随后尝试使用表时,会返回一条错误,指示模式已更改。
如果视图在事务的快照开始后进行刷新,则实例化视图匹配将避免使用该视图。
所有编程接口都支持快照隔离级别。可以使用 SET OPTION 语句设置隔离级别。有关使用快照隔离的信息,请参见:
为数据库启用快照隔离后,每次更新行时,数据库服务器会将原始行的副本添加到存储在临时文件内的版本中。原始行版本条目会一直存储,直到可能需要访问原始行值的所有活动的快照事务完成。使用快照隔离的事务只能看到已提交的值,因此,如果在快照事务开始之前未提交或回退对行的更新,则快照事务需要能够访问原始行值。这就允许使用快照隔离的事务在查看数据时,不会在基础表上放置任何锁。
VersionStorePages 数据库属性返回当前用于版本存储的临时文件中的页数。要获得此值,请执行以下查询:
SELECT DB_PROPERTY ( 'VersionStorePages' ); |
当不再需要旧行版本条目时,会将它们删除。旧版本的 BLOB 会一直存储在原始表(不是临时表)中,直到不再需要它们,旧行版本的索引条目也会一直存储在原始索引中,直到不再需要它们。
可以使用 sa_disk_free_space 系统过程检索临时文件中可用空间的大小。请参见sa_disk_free_space 系统过程。
如果触发了更新行值的触发器,则那些行的原始值也会存储在临时文件中。
将应用程序设计成使用更短的事务和更短的快照,会减少临时文件的空间需求。
如果您担心临时文件的增大,则可建立一个 GrowTemp 系统事件,指定临时文件达到特定大小时要采取的操作。请参见了解系统事件。
快照事务在更新时获取写锁定,而使用快照的事务或语句永远不会获取读锁定。因此,读取程序从不阻塞写入程序,写入程序也从不阻塞读取程序,但尝试更新相同的行时,写入程序之间可能会互相阻塞。
请注意,就快照隔离而言,事务不以 BEGIN TRANSACTION 语句开始,而是以事务中的第一个读取、插入、更新或删除操作开始,具体取决于用于事务的快照隔离级别。以下示例说明快照隔离事务何时开始:
SET OPTION PUBLIC.allow_snapshot_isolation = 'On'; SET TEMPORARY OPTION isolation_level = 'snapshot'; SELECT * FROM Products; --transaction begins and the statement only --sees changes that are already committed INSERT INTO Products SELECT ID + 30, Name, Description, 'Extra large', Color, 50, UnitPrice, NULL FROM Products WHERE Name = 'Tee Shirt'; COMMIT; --transaction ends |
Copyright © 2009, iAnywhere Solutions, Inc. - SQL Anywhere 11.0.1 |