为确保数据完整性,所有数据库管理系统 (DBMS) 都采用了数据锁定机制。例如,当某个编辑者开始更新某些行时,这些行即会锁定,以防止其他编辑者进行更改。当事务完成后,锁定就会解除。
每个 DBMS 锁定和解释隔离级别的方式都各不相同, 因此,您需要通过研究您所使用的 DBMS 的行为来确定上设置何种锁定级别、如何设置隔离级别以及如何处理锁定超时及死锁情况。有关详细信息,请参阅您的 DBMS 文档。
另外,ArcGIS 使用 DBMS 的方式也不尽相同。因此,在不同的 DBMS 间,执行非版本化编辑时出现并发问题的可能性会略有不同。本主题简要介绍了在 ArcGIS 上下文中并发与锁定的机制,但数据库锁定是一个复杂的主题。
ArcGIS 与隔离级别
当您在非版本化编辑会话中编辑 Oracle、DB2 或 Informix 的地理数据库时,ArcGIS 不会通过设置隔离级别来修改此环境。而是使用在 DBMS 中设置的当前隔离级别。因此,在非版本化的编辑会话中进行编辑时,您可以将隔离设置为任何级别并进行使用。
从 ArcGIS 10.4 开始,SQL Server 地理数据库必须将 SQL Server 数据库选项 READ_COMMITTED_SNAPSHOT 和 ALLOW_SNAPSHOT_ISOLATION 设置为 ON。当您在非版本化编辑会话中编辑 SQL Server 地理数据库时,ArcGIS 将使用事务的“读取已提交”隔离级别。
以下几节介绍了在通常条件下可能发生的并发问题。除非另有说明,否则本文中所述内容均假设已在底层 DBMS 中将默认隔离级别设置为“提交后读取”或与之相当的级别。
Oracle
写入者阻止写入者:对某个要素或一组要素执行编辑操作(例如,移动要素或修改其属性)时,DBMS 会将行锁定。要素将一直处于锁定状态,直到进行了保存或停止编辑且未保存。因此,在编辑会话期间,您所编辑的所有要素或记录都将处于锁定状态。
当两个人试图同时编辑同一要素时,该要素会在第一个编辑者完成某个操作后锁定。锁定状态会继续保持,即使该编辑者去处理其他要素也是如此。要素会保持锁定状态,直到该编辑者执行保存操作(从而将更改提交到数据库),或停止该编辑会话并放弃保存(从而回滚在该编辑会话中执行的所有编辑)。
当要素处于锁定状态时,第二个编辑者试图修改同一要素。第二个编辑者的 ArcMap 会话需等待锁定解除,并会显示一个沙漏以显示会话正在处理操作。沙漏会一直显示直到锁定解除,即直到第一个编辑者保存更改(将更改提交到数据库)或结束编辑会话并放弃保存(回滚编辑)。此时,第二个编辑者才可编辑表格。(请注意,这意味着第二个编辑者的更改将覆盖第一个编辑者的更改。)
当出现以下情况时,也会在两个编辑者之间同时发生这种锁定问题:
- 两者同时进行编辑。
- 两个编辑者已在各自的当前编辑会话中修改了行。
- 每个编辑者都试图修改一个已被另一个编辑者修改过的行。
两个编辑者中第一个试图修改已锁定行的人将看到一个沙漏,表示 ArcMap 会话需等待锁定解除。当第二个编辑者试图修改已被第一个编辑者锁定的行时,就会出现被称之为死锁的情况,因为两个编辑者都在同时阻止对方。DBMS 会立即选择将其中一个事务回滚,以便另一个用户能够继续操作。事务被回滚的编辑者必须重做自上次保存编辑后执行的所有编辑。
写入者不阻止读取者:无论隔离级别如何设置,将数据写入到数据库的编辑者都不会阻止其他编辑者读取同一数据。对于读取被锁定数据的人或应用程序,数据将显示为当前事务开始前的状态。
读取者不阻止写入者:无论隔离级别如何设置,读取数据库的人或应用程序都不会阻止其他用户修改同一数据。
DB2 和 Informix
写入者阻止写入者:DB2 和 Informix 中写入者阻止写入者的方式与在 Oracle 中类似。有关详细信息,请参阅 Oracle 部分的说明。
写入者阻止读取者:在 DB2 和 Informix 中,在位于“未提交读取”之上的任何隔离级别,写入者都将阻止其他人及应用程序读取同一数据。在这些更高的隔离级别上,在保存或回滚编辑之前一直锁定数据可能会导致并发问题,也就是说,当您处理编辑会话时,其他人将无法读取您正在编辑的数据。这可能会导致出现以下情况:
- 如果您向 ArcMap 中添加相同的图层,会出现沙漏,且只有锁定被解除后才能绘制图层。
- 如果您试图平移到正被编辑的数据,ArcMap 会等待锁定解除,之后才会更新显示。
- 如果您标识一个锁定的要素,则会出现沙漏,并且直到锁定解除之后才会返回信息。
读取者阻止写入者:在 DB2 和 Informix 中,在位于“未提交读取”之上的任何隔离级别,读取者都将阻止其他编辑者修改同一数据。不过,实际上在 ArcGIS 中却很少见到这种情况,这是因为读取行锁定的持续时间很短,而当数据出现时锁定就已经被解除了。只有在以下应用中,读取者才能真正地阻止写入者:在 DBMS 中打开游标、每次提取一行并在其处理数据时遍历整个结果集。此时,DB2 和 Informix 会在处理结果集时开始获取并保持锁定状态。
PostgreSQL
写入者阻止写入者:在 PostgreSQL 中,直到对行进行了更改的首个事务已提交到数据库或回滚时,才能更新行。当两个编辑者试图同时更新或删除同一要素时,第一个编辑者会阻止另一个编辑者对该行进行更新。在该编辑者执行保存操作(从而将更改提交到数据库),或停止编辑会话并放弃保存(从而回滚在该编辑会话中执行的所有编辑)之后,其他编辑者才能编辑该行。
当要素处于锁定状态时,第二个编辑者试图修改同一要素。第二个编辑者的 ArcMap 会话需等待锁定解除,并会显示一个沙漏以显示会话正在处理操作。沙漏会一直显示,即直到第一个编辑者保存更改(将更改提交到数据库)或结束编辑会话并放弃保存(回滚编辑)。此时,第二个编辑者才可编辑表格。(请注意,这意味着第二个编辑者的更改将覆盖第一个编辑者的更改。)
写入者不阻止读取者:如果使用 PostgreSQL 的多版本并发控制 (MVCC)(此为数据库的默认和推荐行为),则写入到数据库的事务不会阻止读取者查询数据库。无论您是在数据库中使用默认的隔离级别“读取已提交”,还是将隔离级别设置为“序列化”,都不会阻止。
读取者不阻止写入者:无论在数据库中设置何种隔离级别,读取者都不会锁定数据。
SQL Server
从 ArcGIS 10.4 开始,SQL Server 地理数据库必须将 SQL Server 数据库选项 READ_COMMITTED_SNAPSHOT 和 ALLOW_SNAPSHOT_ISOLATION 设置为 ON,且 ArcGIS 将使用事务的“读取已提交”隔离级别。有关“读取已提交”隔离级别的详细信息,请参阅 SQL Server 文档。
写入者阻止写入者:SQL Server 中写入者阻止写入者的方式与 Oracle 中写入者阻止写入者的方式类似。
写入者不阻止读取者:在语句或事务开始时,读取者将检索已存在的行的已提交版本。
读取者不阻止写入者:当事务(在基于行版本的隔离下运行)读取数据时,读取操作不能获取被读取的数据共享的锁,因此也不会阻止编辑者修改数据。这还可以减少获取的锁的数量,并使锁定资源的开销达到最小化。使用行版本读取已提交隔离以及为隔离拍摄快照,旨在为版本化数据提供语句级别和事务级别的读取一致性。
避免并发问题
通过执行下列建议可将并发问题降至最低:
设计应用和工作流时兼顾考虑锁定机制
等待锁定解除的请求常常由应用或工作流的设计欠佳所导致。开发应用或工作流时,应确保锁定请求有序进行。可以通过标准化所有表的更新顺序来实现这一目的,并且能够避免死锁。要缩短锁定的持续时间,最好在执行事务的应用逻辑或工作流单元完成后发出所有数据修改请求。
设置适当的隔离级别(Oracle、DB2、Informix)
隔离级别会影响事务锁定数据的时间长短。隔离级别越高,事务锁定的持续时间越长。事务锁定的持续时间越长,数据的完整性就越好,但这样就要减少并发。只要在可接受范围内,可以通过降低隔离级别来改善并发。
将数据注册为版本
通过将数据注册为版本来改善并发。如果使用第三方应用程序维护数据,可考虑将数据注册为版本,从而可以将编辑内容移到基表,进而改善并发。这样,第三方应用程序可查看应用在“默认”地理数据库版本中的更改,从而增强 ArcGIS 和 ArcObjects 应用程序编辑和管理多版本数据的能力,否则这些用户可能会导致并发问题或受到其影响。当编辑者修改某个版本的表时,不使用锁定机制,从而使数据在与其他编辑者完全隔离的情况下进行编辑。