Sqlserver2000中的并发问题
一、并发问题的产生:
如果没有锁定且多个用户同时访问一个数据库,则当他们的事务同时使用相同的数据时可能会发生问题。并发问题包括:
- 丢失或覆盖更新。
- 未确认的相关性(脏读)。
- 不一致的分析(非重复读)。
- 幻像读。
1、丢失更新:
当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,会发生丢失更新问题。每个事务都不知道其它事务的存在。最后的更新将重写由其它事务所做的更新,这将导致数据丢失。
例如,两个编辑人员制作了同一文档的电子复本。每个编辑人员独立地更改其复本,然后保存更改后的复本,这样就覆盖了原始文档。最后保存其更改复本的编辑人员覆盖了第一个编辑人员所做的更改。如果在第一个编辑人员完成之后第二个编辑人员才能进行更改,则可以避免该问题。
2、未确认的相关性(脏读)
当第二个事务选择其它事务正在更新的行时,会发生未确认的相关性问题。第二个事务正在读取的数据还没有确认并且可能由更新此行的事务所更改。
例如,一个编辑人员正在更改电子文档。在更改过程中,另一个编辑人员复制了该文档(该复本包含到目前为止所做的全部更改)并将其分发给预期的用户。此后,第一个编辑人员认为目前所做的更改是错误的,于是删除了所做的编辑并保存了文档。分发给用户的文档包含不再存在的编辑内容,并且这些编辑内容应认为从未存在过。如果在第一个编辑人员确定最终更改前任何人都不能读取更改的文档,则可以避免该问题。
3、不一致的分析(非重复读)
当第二个事务多次访问同一行而且每次读取不同的数据时,会发生不一致的分析问题。不一致的分析与未确认的相关性类似,因为其它事务也是正在更改第二个事务正在读取的数据。然而,在不一致的分析中,第二个事务读取的数据是由已进行了更改的事务提交的。而且,不一致的分析涉及多次(两次或更多)读取同一行,而且每次信息都由其它事务更改;因而该行被非重复读取。
例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次读取文档时,文档已更改。原始读取不可重复。如果只有在作者全部完成编写后编辑人员才可以读取文档,则可以避免该问题。
4、幻像读
当对某行执行插入或删除操作,而该行属于某个事务正在读取的行的范围时,会发生幻像读问题。事务第一次读的行范围显示出其中一行已不复存在于第二次读或后续读中,因为该行已被其它事务删除。同样,由于其它事务的插入操作,事务的第二次或后续读显示有一行已不存在于原始读中。
例如,一个编辑人员更改作者提交的文档,但当生产部门将其更改内容合并到该文档的主复本时,发现作者已将未编辑的新材料添加到该文档中。如果在编辑人员和生产部门完成对原始文档的处理之前,任何人都不能将新材料添加到文档中,则可以避免该问题。
二、并发问题的解决方案:
当锁定用作并发控制机制时,它可以解决并发问题。这使所有事务得以在彼此完全隔离的环境中运行,但是任何时候都可以有多个正在运行的事务。
可串行性是通过运行一组并发事务达到的数据库状态,等同于这组事务按某种顺序连续执行时所达到的数据库状态。
SQL-92 隔离级别
尽管可串行性对于事务确保数据库中的数据在所有时间内的正确性相当重要,然而许多事务并不总是要求完全的隔离。例如,多个作者工作于同一本书的不同章节。新章节可以在任意时候提交到项目中。但是,对于已经编辑过的章节,没有编辑人员的批准,作者不能对此章节进行任何更改。这样,尽管有未编辑的新章节,但编辑人员仍可以确保在任意时间该书籍项目的正确性。编辑人员可以查看以前编辑的章节以及最近提交的章节。
事务准备接受不一致数据的级别称为隔离级别。隔离级别是一个事务必须与其它事务进行隔离的程度。较低的隔离级别可以增加并发,但代价是降低数据的正确性。相反,较高的隔离级别可以确保数据的正确性,但可能对并发产生负面影响。应用程序要求的隔离级别确定了 SQL Server 使用的锁定行为。
SQL-92 定义了下列四种隔离级别,SQL Server 支持所有这些隔离级别:
- 未提交读(事务隔离的最低级别,仅可保证不读取物理损坏的数据)。
- 提交读(SQL Server 默认级别)。
- 可重复读。
- 可串行读(事务隔离的最高级别,事务之间完全隔离)。
如果事务在可串行读隔离级别上运行,则可以保证任何并发重叠事务均是串行的。
下面四种隔离级别允许不同类型的行为。
隔离级别 |
脏读 |
不可重复读取 |
幻像
|
未提交读 |
是 |
是 |
是 |
提交读 |
否 |
是 |
是 |
可重复读 |
否 |
否 |
是 |
可串行读 |
否 |
否 |
否 |
事务必须运行于可重复读或更高的隔离级别以防止丢失更新。当两个事务检索相同的行,然后基于原检索的值对行进行更新时,会发生丢失更新。如果两个事务使用一个 UPDATE 语句更新行,并且不基于以前检索的值进行更新,则在默认的提交读隔离级别不会发生丢失更新。
三、问题解决的实现:
1、 默认解决方案:
SET TRANSACTION ISOLATION LEVEL
控制由连接发出的所有 Microsoft® SQL Server™ SELECT 语句的默认事务锁定行为。
语法
SET TRANSACTION ISOLATION LEVEL { READ COMMITTED | READ UNCOMMITTED | REPEATABLE READ | SERIALIZABLE
}
参数
READ COMMITTED
指定在读取数据时控制共享锁以避免脏读,但数据可在事务结束前更改,从而产生不可重复读取或幻像数据。该选项是 SQL Server 的默认值。
READ UNCOMMITTED
执行脏读或 0 级隔离锁定,这表示不发出共享锁,也不接受排它锁。当设置该选项时,可以对数据执行未提交读或脏读;在事务结束前可以更改数据内的数值,行也可以出现在数据集中或从数据集消失。该选项的作用与在事务内所有语句中的所有表上设置 NOLOCK 相同。这是四个隔离级别中限制最小的级别。
REPEATABLE READ
锁定查询中使用的所有数据以防止其他用户更新数据,但是其他用户可以将新的幻像行插入数据集,且幻像行包括在当前事务的后续读取中。因为并发低于默认隔离级别,所以应只在必要时才使用该选项。
SERIALIZABLE
在数据集上放置一个范围锁,以防止其他用户在事务完成之前更新数据集或将行插入数据集内。这是四个隔离级别中限制最大的级别。因为并发级别较低,所以应只在必要时才使用该选项。该选项的作用与在事务内所有 SELECT 语句中的所有表上设置 HOLDLOCK 相同。
注释
一次只能设置这些选项中的一个,而且设置的选项将一直对那个连接保持有效,直到显式更改该选项为止。这是默认行为,除非在语句的 FROM 子句中在表级上指定优化选项。
SET TRANSACTION ISOLATION LEVEL 的设置是在执行或运行时设置,而不是在分析时设置。
示例
下例为会话设置 TRANSACTION ISOLATION LEVEL。对于每个后续 Transact-SQL 语句,SQL Server 将所有共享锁一直控制到事务结束为止。 SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
GO
BEGIN TRANSACTION
SELECT * FROM publishers
SELECT * FROM authors
...
COMMIT TRANSACTION
2、自定义事务隔离级别
默认情况下,Microsoft® SQL Server™ 2000 在 READ COMMITTED 的一个隔离级别上操作。但是,应用程序可能必须运行于不同的隔离级别。若要在应用程序中使用更严格或较宽松的隔离级别,可以通过使用 SET TRANSACTION ISOLATION LEVEL 语句设置会话的隔离级别,来自定义整个会话的锁定。
指定隔离级别后,SQL Server 会话中所有 SELECT 语句的锁定行为都运行于该隔离级别上,并一直保持有效直到会话终止或者将隔离级别设置为另一个级别。例如,若要设置事务隔离级别为可串行读,以确保并发事务不能在 authors 表中插入幻像行,请执行: USE pubs
GO
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
GO
BEGIN TRANSACTION
SELECT au_lname FROM authors
说明 如果必要,可以通过指定表级锁定提示来替代单个 SELECT 语句的隔离级别。指定表级锁定提示不会影响会话中的其它语句。建议仅在绝对必要时才使用表级锁定提示更改默认的锁定行为。
若要确定当前设置的事务隔离级别,请使用 DBCC USEROPTIONS 语句,例如: USE pubs
GO
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
GO
DBCC USEROPTIONS
GO
Set Option |
Value |
Textsize |
4096 |
Language |
us_english |
Dateformat |
mdy |
Datefirst |
7 |
isolation level |
repeatable read |
(5 rows affected)
DBCC execution completed. If DBCC printed error messages, see your System Administrator.
3、调整事务隔离级别
隔离属性是 ACID 的四个属性之一,逻辑工作单元必须具备这四个属性才能称为事务。该属性能够使事务免受其它并发事务所执行的更新的影响。每个事务的隔离级别实际上都是可以自定义的。
Microsoft® SQL Server™ 支持 SQL-92 中定义的事务隔离级别。设置事务隔离级别虽然使程序员承担了某些完整性问题所带来的风险,但可以换取对数据更大的并发访问权。与以前的隔离级别相比,每个隔离级别都提供了更大的隔离性,但这是通过在更长的时间内占用更多限制锁换来的。事务隔离级别有:
- READ UNCOMMITTED
- READ COMMITTED
- REPEATABLE READ
- SERIALIZABLE
可以使用 Transact-SQL 或通过数据库 API 来设置事务隔离级别:
Transact-SQL
Transact-SQL 脚本和 DB-Library 应用程序使用 SET TRANSACTION ISOLATION LEVEL 语句。
ADO
ADO 应用程序将 Connection 对象的 IsolationLevel 属性设置为 adXactReadUncommitted、adXactReadCommitted、adXactRepeatableRead 或 adXactReadSerializable。
OLE DB
OLE DB 应用程序调用 ITransactionLocal::StartTransaction,其中 isoLevel 设置为 ISOLATIONLEVEL_READUNCOMMITTED、ISOLATIONLEVEL_READCOMMITTED、ISOLATIONLEVEL_REPEATABLEREAD 或 ISOLATIONLEVEL_SERIALIZABLE。
ODBC
ODBC 应用程序调用 SQLSetConnectAttr,其中 Attribute 设置为 SQL_ATTR_TXN_ISOLATION,ValuePtr 设置为 SQL_TXN_READ_UNCOMMITTED、SQL_TXN_READ_COMMITTED、SQL_TXN_REPEATABLE_READ 或 SQL_TXN_SERIALIZABLE。
参考<<sqlserver2000帮助文件>>
|