如:同一物品有20只,两个客户程序都同时要对该物品进行出库操作,A客户要出
库3只,B客户要出库5只,同时要出库,程序应怎么处理呢?
20 个解决方案
#1
排队,先进现处理...
估计的...
估计的...
#2
一个程序操作同一记录时,并不知道另一个用户也要出库操作的,怎么办呢
#3
一般处理提成时会提示找不到记录或无法定位该记录。
#4
同步更新问题
需要建一个加锁表来实现对某条记录的锁、其锁的有效期以及锁该记录的用户名等~
需要建一个加锁表来实现对某条记录的锁、其锁的有效期以及锁该记录的用户名等~
#5
单靠DBMS来控制是不行的
#6
你说的这种情况就是数据库中的丢失更新,可以在操作时先利用锁机制,防止其它进程进行更新(先等待),
待操作完毕后,再进行
待操作完毕后,再进行
#7
楼上说得很对,当遇到这种情况时会发生死锁.
#8
原来老掉牙的FOXPRO系列有lock() unlock()
加锁、解锁指令,现在到了delphi,bcb怎没有这
些指令呢?
加锁、解锁指令,现在到了delphi,bcb怎没有这
些指令呢?
#9
楼主,可以用事务处理
#10
数据库事务包起来,同楼上
#11
使用事务
#12
ADODataSet1打开mdb库,
用DBGRID做试验,如果两个程序用DBGRID同时打开同一个数据库,
并同时编辑同一个记录的同一字段;
一个程序修改了内容,且POST;
另一程序也修改它,且POST,出现错误:
"无法为更新定位行.一些值可能在最后一次读取后已更改";
因为,第一个程序修改写入后,每二个程序的缓冲区与实际记录值
已不一致,称为"脏数据",再修改写入时,系统要检查是否"不一致",
只要出错了,说明"已脏";所以必须为"脏数据"刷新成"新数据"才能
写入,这是关键了;如果绕开这种"一致性"检查,是很危险的,如:
总数量:10只,
第一程序,得到的数量为:10,每二个程序,也得到了数量为:10,
第一个程序出库:3只,写入后,数量变成:7;
第二个程序也出库:3只,数量为10(脏数据),10-3=7,写回7,
这样10-3-3=7,乱了;
用DBGRID做试验,如果两个程序用DBGRID同时打开同一个数据库,
并同时编辑同一个记录的同一字段;
一个程序修改了内容,且POST;
另一程序也修改它,且POST,出现错误:
"无法为更新定位行.一些值可能在最后一次读取后已更改";
因为,第一个程序修改写入后,每二个程序的缓冲区与实际记录值
已不一致,称为"脏数据",再修改写入时,系统要检查是否"不一致",
只要出错了,说明"已脏";所以必须为"脏数据"刷新成"新数据"才能
写入,这是关键了;如果绕开这种"一致性"检查,是很危险的,如:
总数量:10只,
第一程序,得到的数量为:10,每二个程序,也得到了数量为:10,
第一个程序出库:3只,写入后,数量变成:7;
第二个程序也出库:3只,数量为10(脏数据),10-3=7,写回7,
这样10-3-3=7,乱了;
#13
POST()时,
如果用try() carch(...)()捕捉到缓冲中的当前记录
为"脏数据",如何刷新当前记录呢?(刷新所有记录太慢了)
如果用try() carch(...)()捕捉到缓冲中的当前记录
为"脏数据",如何刷新当前记录呢?(刷新所有记录太慢了)
#14
建议到网上看看这篇文章
Sqlserver2000中的并发问题
对于Access是单机版,最好尽量控制并发操作。
Sqlserver2000中的并发问题
对于Access是单机版,最好尽量控制并发操作。
#15
这称为"并发"操作,搜到不少ADO有关的并发操作问题
http://www.java-asp.net/vb/200602/t_17625.html
http://www.java-asp.net/vb/200602/t_17625.html
#16
ADO数据库并发操作和解决并发冲突
数据库并发问题已经说明了并发的严重性与危害性。下面讲述VB+ADO来处理并发操作的实际案例:
在以前DAO中可以对数据库进行记录锁,页面锁,表锁来处理并发操作,还可以使用事务处理,那么现在
怎么用ADO来检测并处理数据库的并发操作呢?
相关背景知识:
ADO中对数据库的也是采用锁定的方法来实现的,还可以用事务来做。事务有个特点就是:要么全成功,
要么就全失败。那么在实际工作中有可能只有几条或一小部分的记录有冲突,只要对那一小部分的记录进
行处理就行了。ADO也使用来锁定来实现。
那么什么是锁定?
锁定是一种进程,DBMS 通过该进程限制多用户环境中对行的访问。当一行或一列被独占锁定时,不允许
其他用户在释放锁定之前访问锁定的数据。这确保了两个用户无法同时更新一行中的同一列。
从资源角度而言,锁定的成本可能非常高昂,只有在需要保持数据完整性的情况下才应当使用此功能。在
每秒有数百或数千用户试图访问某个记录的数据库(例如连接到 Internet 的数据库)中,不必要的锁定
将很快导致应用程序性能的下降。
可选择适当的锁定选项来控制数据源和 ADO 游标库管理并发性的方式。
打开 Recordset 之前先设置 LockType 属性,以指定提供者打开它时使用的锁定类型。读取该属性以返
回打开的 Recordset 对象中使用的锁定类型。
提供者可能不支持所有锁定类型。如果提供者不支持请求的 LockType 设置,则替换为另一种锁定类型。
若要确定 Recordset 对象中实际可用的锁定功能,将soppurts 方法与 adUpdate 和 adUpdateBatch 一
起使用。
如果 CursorLocation 属性设置为 adUseClient,则不支持 adLockPessimistic 设置。如果设置了不支
持的值,将不产生错误,而使用所支持的最相近的 LockType。
LockType 属性在 Recordset 关闭时为读/写,而在 Recordset 打开时为只读。
锁定类型种类(LockType 属性):
1.adLockBatchOptimistic
指示开放式批更新。需要批更新模式。
许多应用程序都一次提取多行,然后需要进行相应的更新,这些更新包括所有需要插入、更新或删除的行
的完整集合。使用批游标只需一次往返服务器,因而导致更新性能的提高和网络通信量的降低。使用批游
标库可创建静态游标,然后断开到数据源的连接。这时就可以对行进行更改,然后重新连接到数据源并将
更改以批的形式发布到数据源。
2.adLockOptimistic
提供者使用开放式锁定,仅在调用 Update 方法时锁定记录。这意味着另一个用户有可能会利用您编辑记
录和调用 Update 的时间间隔更改数据,这就会引起冲突。而使用此锁定类型时发生冲突的几率很低,即
使发生了冲突也会很快得到解决。
3.adLockPessimistic
指示逐个记录保守式锁定。提供者要确保记录编辑成功,通常在编辑之前立即在数据源锁定记录。当然,
这意味着一旦您开始编辑记录,这些记录就会对其他用户不可用,知道您通过调用 Update 释放锁定。如
果您的系统不提供对数据的并发更改,例如预定系统,那么可使用此锁定类型。
4.adLockReadOnly
指示只读记录。无法改变数据。只读锁定是速度“最快”的锁定类型,因为它不要求服务器保持对记录的
锁定。冲突
5.adLockUnspecified
未指定锁定类型。
检测和解决冲突:
如果在立即模式中处理 Recordset,则很少出现并发问题。另一方面,如果应用程序使用批模式更新,则
在保存由一个正在编辑记录的用户所作的更改之前,编辑同一个记录的另一个用户比较有可能会更改记录
。在这种情况下,需要应用程序准确处理冲突。无论是哪种情况,都可以使用 ADO 提供的 Field 对象的
UnderlyingValue 和 OriginalValue 属性来处理这些类型的冲突。将这些属性与 Recordset 的 Resync
方法和 Filter 属性配合使用。
检测错误
在批更新期间 ADO 遇到冲突时,将在 Errors 集合中放入警告。因此,调用 BatchUpdate 之后,一定要
立即检查是否有错误,如果找到了错误,则应当假设已遇到冲突并开始进行测试。第一步要将 Recordset
的 Filter 属性设置为等于 adFilterConflictingRecords。该设置将使 Recordset对象限制为只显示那
些发生冲突的记录。如果这一步之后 RecordCount 属性等于零,就说明错误是由冲突以外的其他原因引
起的。
调用 BatchUpdate 时,ADO 和提供者将生成对数据源执行更新的 SQL 语句。下一步,调用 Recordset
的 Resync 方法,并且将 AffectRecords 参数设置为等于 adAffectGroup,将 ResyncValues 参数设置
为等于 adResyncUnderlyingValues。Resync 方法将用来自基本数据库中的数据刷新在当前 Recordset
对象中的数据。通过使用 adAffectGroup,可以确保只有使用当前筛选设置的情况下可见的记录(即只有
冲突记录)会与数据库重新同步。如果处理的是大型 Recordset,该操作会对性能有较大影响。通过在调
用 Resync 时将 ResyncValues 参数设置为 adResyncUnderlyingValues,可以确保 UnderlyingValue 属
性将包含数据库中的(冲突)值,并确保 Value 属性仍然是由用户输入的值,还会确保 OriginalValue
属性将持有字段的原始值(即在上一次成功进行 UpdateBatch 调用之前该字段所拥有的值)。然后,可
以使用这些值通过编程方式解决冲突,或要求用户选择将要使用的值。
数据库并发问题已经说明了并发的严重性与危害性。下面讲述VB+ADO来处理并发操作的实际案例:
在以前DAO中可以对数据库进行记录锁,页面锁,表锁来处理并发操作,还可以使用事务处理,那么现在
怎么用ADO来检测并处理数据库的并发操作呢?
相关背景知识:
ADO中对数据库的也是采用锁定的方法来实现的,还可以用事务来做。事务有个特点就是:要么全成功,
要么就全失败。那么在实际工作中有可能只有几条或一小部分的记录有冲突,只要对那一小部分的记录进
行处理就行了。ADO也使用来锁定来实现。
那么什么是锁定?
锁定是一种进程,DBMS 通过该进程限制多用户环境中对行的访问。当一行或一列被独占锁定时,不允许
其他用户在释放锁定之前访问锁定的数据。这确保了两个用户无法同时更新一行中的同一列。
从资源角度而言,锁定的成本可能非常高昂,只有在需要保持数据完整性的情况下才应当使用此功能。在
每秒有数百或数千用户试图访问某个记录的数据库(例如连接到 Internet 的数据库)中,不必要的锁定
将很快导致应用程序性能的下降。
可选择适当的锁定选项来控制数据源和 ADO 游标库管理并发性的方式。
打开 Recordset 之前先设置 LockType 属性,以指定提供者打开它时使用的锁定类型。读取该属性以返
回打开的 Recordset 对象中使用的锁定类型。
提供者可能不支持所有锁定类型。如果提供者不支持请求的 LockType 设置,则替换为另一种锁定类型。
若要确定 Recordset 对象中实际可用的锁定功能,将soppurts 方法与 adUpdate 和 adUpdateBatch 一
起使用。
如果 CursorLocation 属性设置为 adUseClient,则不支持 adLockPessimistic 设置。如果设置了不支
持的值,将不产生错误,而使用所支持的最相近的 LockType。
LockType 属性在 Recordset 关闭时为读/写,而在 Recordset 打开时为只读。
锁定类型种类(LockType 属性):
1.adLockBatchOptimistic
指示开放式批更新。需要批更新模式。
许多应用程序都一次提取多行,然后需要进行相应的更新,这些更新包括所有需要插入、更新或删除的行
的完整集合。使用批游标只需一次往返服务器,因而导致更新性能的提高和网络通信量的降低。使用批游
标库可创建静态游标,然后断开到数据源的连接。这时就可以对行进行更改,然后重新连接到数据源并将
更改以批的形式发布到数据源。
2.adLockOptimistic
提供者使用开放式锁定,仅在调用 Update 方法时锁定记录。这意味着另一个用户有可能会利用您编辑记
录和调用 Update 的时间间隔更改数据,这就会引起冲突。而使用此锁定类型时发生冲突的几率很低,即
使发生了冲突也会很快得到解决。
3.adLockPessimistic
指示逐个记录保守式锁定。提供者要确保记录编辑成功,通常在编辑之前立即在数据源锁定记录。当然,
这意味着一旦您开始编辑记录,这些记录就会对其他用户不可用,知道您通过调用 Update 释放锁定。如
果您的系统不提供对数据的并发更改,例如预定系统,那么可使用此锁定类型。
4.adLockReadOnly
指示只读记录。无法改变数据。只读锁定是速度“最快”的锁定类型,因为它不要求服务器保持对记录的
锁定。冲突
5.adLockUnspecified
未指定锁定类型。
检测和解决冲突:
如果在立即模式中处理 Recordset,则很少出现并发问题。另一方面,如果应用程序使用批模式更新,则
在保存由一个正在编辑记录的用户所作的更改之前,编辑同一个记录的另一个用户比较有可能会更改记录
。在这种情况下,需要应用程序准确处理冲突。无论是哪种情况,都可以使用 ADO 提供的 Field 对象的
UnderlyingValue 和 OriginalValue 属性来处理这些类型的冲突。将这些属性与 Recordset 的 Resync
方法和 Filter 属性配合使用。
检测错误
在批更新期间 ADO 遇到冲突时,将在 Errors 集合中放入警告。因此,调用 BatchUpdate 之后,一定要
立即检查是否有错误,如果找到了错误,则应当假设已遇到冲突并开始进行测试。第一步要将 Recordset
的 Filter 属性设置为等于 adFilterConflictingRecords。该设置将使 Recordset对象限制为只显示那
些发生冲突的记录。如果这一步之后 RecordCount 属性等于零,就说明错误是由冲突以外的其他原因引
起的。
调用 BatchUpdate 时,ADO 和提供者将生成对数据源执行更新的 SQL 语句。下一步,调用 Recordset
的 Resync 方法,并且将 AffectRecords 参数设置为等于 adAffectGroup,将 ResyncValues 参数设置
为等于 adResyncUnderlyingValues。Resync 方法将用来自基本数据库中的数据刷新在当前 Recordset
对象中的数据。通过使用 adAffectGroup,可以确保只有使用当前筛选设置的情况下可见的记录(即只有
冲突记录)会与数据库重新同步。如果处理的是大型 Recordset,该操作会对性能有较大影响。通过在调
用 Resync 时将 ResyncValues 参数设置为 adResyncUnderlyingValues,可以确保 UnderlyingValue 属
性将包含数据库中的(冲突)值,并确保 Value 属性仍然是由用户输入的值,还会确保 OriginalValue
属性将持有字段的原始值(即在上一次成功进行 UpdateBatch 调用之前该字段所拥有的值)。然后,可
以使用这些值通过编程方式解决冲突,或要求用户选择将要使用的值。
#17
范例:
在调用 UpdateBatch 之前,本范例通过使用单独的 Recordset 来更改基本表中的值,人为地创建一个冲
突。
'Begin
On Error GoTo ErrHandler:
Dim objRs1 As New ADODB.Recordset
Dim objRs2 As New ADODB.Recordset
Dim strSQL As String
Dim strMsg As String
strSQL = "SELECT * FROM Shippers WHERE ShipperID = 2"’SQL查询
objRs1.CursorLocation = adUseClient’设置客户端游标
objRs1.Open strSQL, strConn, adOpenStatic, adLockBatchOptimistic, adCmdText’执行查询生
成objrs1记录集
objRs1("Phone") = "(111) 555-1111"’更改表中一条记录phone字段的值
objRs2.Open strSQL, strConn, adOpenKeyset, adLockOptimistic, adCmdText’执行查询生成
objrs2记录集
objRs2("Phone") = "(999) 555-9999" ’更改表中记录phone字段的值
objRs2.Update
objRs2.Close
Set objRs2 = Nothing
On Error Resume Next
objRs1.UpdateBatch
If objRs1.ActiveConnection.Errors.Count <> 0 Then
Dim intConflicts As Integer
intConflicts = 0
objRs1.Filter = adFilterConflictingRecords
intConflicts = objRs1.RecordCount
objRs1.Resync adAffectGroup, adResyncUnderlyingValues
If intConflicts > 0 Then
strMsg = "A conflict occurred with updates for " & intConflicts & "records." &
vbCrLf & "The values will be restored" " to their original values."
objRs1.MoveFirst
While Not objRs1.EOF
strMsg = strMsg & "SHIPPER = " & objRs1("CompanyName") & vbCrLf
strMsg = strMsg & "Value = " & objRs1("Phone").Value & vbCrLf
strMsg = strMsg & "UnderlyingValue = " & _
objRs1("Phone").UnderlyingValue & vbCrLf
strMsg = strMsg & "OriginalValue = " & _
objRs1("Phone").OriginalValue & vbCrLf
strMsg = strMsg & vbCrLf & "Original value has been restored."
MsgBox strMsg, vbOKOnly, _
"Conflict " & objRs1.AbsolutePosition & _
" of " & intConflicts
objRs1("Phone").Value = objRs1("Phone").OriginalValue
objRs1.MoveNext
Wend
objRs1.UpdateBatch adAffectGroup
Else
strMsg = "Errors occurred during the update. " & _
objRs1.ActiveConnection.Errors(0).Number & " " & _
objRs1.ActiveConnection.Errors(0).Description
End If
On Error GoTo 0
End If
objRs1.MoveFirst
objRs1.Close
Set objRs1 = Nothing
Exit Sub
ErrHandler:
If Not objRs1 Is Nothing Then
If objRs1.State = adStateOpen Then objRs1.Close
Set objRs1 = Nothing
End If
If Not objRs2 Is Nothing Then
If objRs2.State = adStateOpen Then objRs2.Close
Set objRs2 = Nothing
End If
If Err <> 0 Then
MsgBox Err.Source & "-->" & Err.Description, , "Error"
End If
'End
还可以使用当前 Record 的或特定 Field 的 Status 属性来确定已发生的冲突种类。
至此发生冲突的记录检查出来了,那么下一步就是处理失败的更新。
如何解决错误将取决于错误的性质和严重性以及应用程序的逻辑。实际中的数据库大都数是多个用户共享
,典型的错误是其他人在你修改某个字段之前先修改了该字段。这种类型的错误称为“冲突”。ADO 将检
测这种情况并报告错误。
如果存在更新错误,将被错误处理例程捕获。通过使用 adFilterConflictingRecords 常量来筛选
Recordset,可以只显示发生冲突行。
范例:
本错误解决方案将打印发生冲突的记录中的作者的名和姓(au_fname 和 au_lname)。
'Begin:
objRs.Filter = adFilterConflictingRecords
objRs.MoveFirst
Do While Not objRst.EOF
Debug.Print "Conflict: Name = "; objRs!au_fname; " "; objRs!au_lname
objRs.MoveNext
Loop
'END
如果上述情况用事务来处理的话,那么对于用户数量较大网络较繁忙且更新记录数很多的情况来说将严重
影响速度。上述方法只要处理发生错误的记录,这样就减轻了网络传输的负荷,有利提高速度。
在调用 UpdateBatch 之前,本范例通过使用单独的 Recordset 来更改基本表中的值,人为地创建一个冲
突。
'Begin
On Error GoTo ErrHandler:
Dim objRs1 As New ADODB.Recordset
Dim objRs2 As New ADODB.Recordset
Dim strSQL As String
Dim strMsg As String
strSQL = "SELECT * FROM Shippers WHERE ShipperID = 2"’SQL查询
objRs1.CursorLocation = adUseClient’设置客户端游标
objRs1.Open strSQL, strConn, adOpenStatic, adLockBatchOptimistic, adCmdText’执行查询生
成objrs1记录集
objRs1("Phone") = "(111) 555-1111"’更改表中一条记录phone字段的值
objRs2.Open strSQL, strConn, adOpenKeyset, adLockOptimistic, adCmdText’执行查询生成
objrs2记录集
objRs2("Phone") = "(999) 555-9999" ’更改表中记录phone字段的值
objRs2.Update
objRs2.Close
Set objRs2 = Nothing
On Error Resume Next
objRs1.UpdateBatch
If objRs1.ActiveConnection.Errors.Count <> 0 Then
Dim intConflicts As Integer
intConflicts = 0
objRs1.Filter = adFilterConflictingRecords
intConflicts = objRs1.RecordCount
objRs1.Resync adAffectGroup, adResyncUnderlyingValues
If intConflicts > 0 Then
strMsg = "A conflict occurred with updates for " & intConflicts & "records." &
vbCrLf & "The values will be restored" " to their original values."
objRs1.MoveFirst
While Not objRs1.EOF
strMsg = strMsg & "SHIPPER = " & objRs1("CompanyName") & vbCrLf
strMsg = strMsg & "Value = " & objRs1("Phone").Value & vbCrLf
strMsg = strMsg & "UnderlyingValue = " & _
objRs1("Phone").UnderlyingValue & vbCrLf
strMsg = strMsg & "OriginalValue = " & _
objRs1("Phone").OriginalValue & vbCrLf
strMsg = strMsg & vbCrLf & "Original value has been restored."
MsgBox strMsg, vbOKOnly, _
"Conflict " & objRs1.AbsolutePosition & _
" of " & intConflicts
objRs1("Phone").Value = objRs1("Phone").OriginalValue
objRs1.MoveNext
Wend
objRs1.UpdateBatch adAffectGroup
Else
strMsg = "Errors occurred during the update. " & _
objRs1.ActiveConnection.Errors(0).Number & " " & _
objRs1.ActiveConnection.Errors(0).Description
End If
On Error GoTo 0
End If
objRs1.MoveFirst
objRs1.Close
Set objRs1 = Nothing
Exit Sub
ErrHandler:
If Not objRs1 Is Nothing Then
If objRs1.State = adStateOpen Then objRs1.Close
Set objRs1 = Nothing
End If
If Not objRs2 Is Nothing Then
If objRs2.State = adStateOpen Then objRs2.Close
Set objRs2 = Nothing
End If
If Err <> 0 Then
MsgBox Err.Source & "-->" & Err.Description, , "Error"
End If
'End
还可以使用当前 Record 的或特定 Field 的 Status 属性来确定已发生的冲突种类。
至此发生冲突的记录检查出来了,那么下一步就是处理失败的更新。
如何解决错误将取决于错误的性质和严重性以及应用程序的逻辑。实际中的数据库大都数是多个用户共享
,典型的错误是其他人在你修改某个字段之前先修改了该字段。这种类型的错误称为“冲突”。ADO 将检
测这种情况并报告错误。
如果存在更新错误,将被错误处理例程捕获。通过使用 adFilterConflictingRecords 常量来筛选
Recordset,可以只显示发生冲突行。
范例:
本错误解决方案将打印发生冲突的记录中的作者的名和姓(au_fname 和 au_lname)。
'Begin:
objRs.Filter = adFilterConflictingRecords
objRs.MoveFirst
Do While Not objRst.EOF
Debug.Print "Conflict: Name = "; objRs!au_fname; " "; objRs!au_lname
objRs.MoveNext
Loop
'END
如果上述情况用事务来处理的话,那么对于用户数量较大网络较繁忙且更新记录数很多的情况来说将严重
影响速度。上述方法只要处理发生错误的记录,这样就减轻了网络传输的负荷,有利提高速度。
#18
数据库并发问题
问题背景及特点:
我们在使用多用户数据库时常常会碰到数据更新失败、删除失等情况,如果有多个用户且同时访问一个数
据库则当他们的事务同时使用相同的数据时可能会发生并发问题。
并发问题包括:
1.丢失或覆盖更新。(幻像读)
2.未确认的相关性(脏读)。
3.不一致的分析(非重复读)。
详细描述:
1.丢失更新
当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,会发生丢失更新问题。每个事务都不
知道其它事务的存在。最后的更新将重写由其它事务所做的更新,这将导致数据丢失。
例如,两个编辑人员制作了同一文档的电子复本。每个编辑人员独立地更改其复本,然后保存更改后的复
本,这样就覆盖了原始文档。最后保存其更改复本的编辑人员覆盖了第一个编辑人员所做的更改。如果在
第一个编辑人员完成之后第二个编辑人员才能进行更改,则可以避免该问题。
2.未确认的相关性(脏读)
当第二个事务选择其它事务正在更新的行时,会发生未确认的相关性问题。第二个事务正在读取的数据还
没有确认并且可能由更新此行的事务所更改。
例如,一个编辑人员正在更改电子文档。在更改过程中,另一个编辑人员复制了该文档(该复本包含到目
前为止所做的全部更改)并将其分发给预期的用户。此后,第一个编辑人员认为目前所做的更改是错误的
,于是删除了所做的编辑并保存了文档。分发给用户的文档包含不再存在的编辑内容,并且这些编辑内容
应认为从未存在过。如果在第一个编辑人员确定最终更改前任何人都不能读取更改的文档,则可以避免该
问题。
3.不一致的分析(非重复读)
当第二个事务多次访问同一行而且每次读取不同的数据时,会发生不一致的分析问题。不一致的分析与未
确认的相关性类似,因为其它事务也是正在更改第二个事务正在读取的数据。然而,在不一致的分析中,
第二个事务读取的数据是由已进行了更改的事务提交的。而且,不一致的分析涉及多次(两次或更多)读
取同一行,而且每次信息都由其它事务更改;因而该行被非重复读取。
例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次读取
文档时,文档已更改。原始读取不可重复。如果只有在作者全部完成编写后编辑人员才可以读取文档,则
可以避免该问题。
4.幻像读
当对某行执行插入或删除操作,而该行属于某个事务正在读取的行的范围时,会发生幻像读问题。事务第
一次读的行范围显示出其中一行已不复存在于第二次读或后续读中,因为该行已被其它事务删除。同样,
由于其它事务的插入操作,事务的第二次或后续读显示有一行已不存在于原始读中。
例如,一个编辑人员更改作者提交的文档,但当生产部门将其更改内容合并到该文档的主复本时,发现作
者已将未编辑的新材料添加到该文档中。如果在编辑人员和生产部门完成对原始文档的处理之前,任何人
都不能将新材料添加到文档中,则可以避免该问题。
-----------------------------------------------------------------
从上面可以看到,解决并发主要是用到了锁和事务。
锁 :给记录或表加上锁是为了对当前操作对象加上一个状态表示位,
让其它用户在获取编辑权限时有了判断。
事务:是为了保证一组操作的完整性。(要么就全部成功,要么就全部失败)
------------------------------------------------------------------
一般处理并发问题时我这样做:
1.开启事务。
2.申请写权限,也就是给对象(表或记录)加锁。
3.如果失败,则结束事务,过一会重试。
4.如果成功,也就是给对象加锁成功,防止其它用户再用同样的方式打开。
5.进行编辑操作。
6.写入所进行的编辑结果。
7.如果写入成功,则提交事务,完成操作。
8.如果写入失败,则回滚事务,取消提交。
9.(7.8)两步操作已释放了锁定的对象,恢复到操作前的状态。
问题背景及特点:
我们在使用多用户数据库时常常会碰到数据更新失败、删除失等情况,如果有多个用户且同时访问一个数
据库则当他们的事务同时使用相同的数据时可能会发生并发问题。
并发问题包括:
1.丢失或覆盖更新。(幻像读)
2.未确认的相关性(脏读)。
3.不一致的分析(非重复读)。
详细描述:
1.丢失更新
当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,会发生丢失更新问题。每个事务都不
知道其它事务的存在。最后的更新将重写由其它事务所做的更新,这将导致数据丢失。
例如,两个编辑人员制作了同一文档的电子复本。每个编辑人员独立地更改其复本,然后保存更改后的复
本,这样就覆盖了原始文档。最后保存其更改复本的编辑人员覆盖了第一个编辑人员所做的更改。如果在
第一个编辑人员完成之后第二个编辑人员才能进行更改,则可以避免该问题。
2.未确认的相关性(脏读)
当第二个事务选择其它事务正在更新的行时,会发生未确认的相关性问题。第二个事务正在读取的数据还
没有确认并且可能由更新此行的事务所更改。
例如,一个编辑人员正在更改电子文档。在更改过程中,另一个编辑人员复制了该文档(该复本包含到目
前为止所做的全部更改)并将其分发给预期的用户。此后,第一个编辑人员认为目前所做的更改是错误的
,于是删除了所做的编辑并保存了文档。分发给用户的文档包含不再存在的编辑内容,并且这些编辑内容
应认为从未存在过。如果在第一个编辑人员确定最终更改前任何人都不能读取更改的文档,则可以避免该
问题。
3.不一致的分析(非重复读)
当第二个事务多次访问同一行而且每次读取不同的数据时,会发生不一致的分析问题。不一致的分析与未
确认的相关性类似,因为其它事务也是正在更改第二个事务正在读取的数据。然而,在不一致的分析中,
第二个事务读取的数据是由已进行了更改的事务提交的。而且,不一致的分析涉及多次(两次或更多)读
取同一行,而且每次信息都由其它事务更改;因而该行被非重复读取。
例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次读取
文档时,文档已更改。原始读取不可重复。如果只有在作者全部完成编写后编辑人员才可以读取文档,则
可以避免该问题。
4.幻像读
当对某行执行插入或删除操作,而该行属于某个事务正在读取的行的范围时,会发生幻像读问题。事务第
一次读的行范围显示出其中一行已不复存在于第二次读或后续读中,因为该行已被其它事务删除。同样,
由于其它事务的插入操作,事务的第二次或后续读显示有一行已不存在于原始读中。
例如,一个编辑人员更改作者提交的文档,但当生产部门将其更改内容合并到该文档的主复本时,发现作
者已将未编辑的新材料添加到该文档中。如果在编辑人员和生产部门完成对原始文档的处理之前,任何人
都不能将新材料添加到文档中,则可以避免该问题。
-----------------------------------------------------------------
从上面可以看到,解决并发主要是用到了锁和事务。
锁 :给记录或表加上锁是为了对当前操作对象加上一个状态表示位,
让其它用户在获取编辑权限时有了判断。
事务:是为了保证一组操作的完整性。(要么就全部成功,要么就全部失败)
------------------------------------------------------------------
一般处理并发问题时我这样做:
1.开启事务。
2.申请写权限,也就是给对象(表或记录)加锁。
3.如果失败,则结束事务,过一会重试。
4.如果成功,也就是给对象加锁成功,防止其它用户再用同样的方式打开。
5.进行编辑操作。
6.写入所进行的编辑结果。
7.如果写入成功,则提交事务,完成操作。
8.如果写入失败,则回滚事务,取消提交。
9.(7.8)两步操作已释放了锁定的对象,恢复到操作前的状态。
#19
受教了,建议在事务中操作。
#20
三层数据库的事务处理不好写,很麻烦.
#1
排队,先进现处理...
估计的...
估计的...
#2
一个程序操作同一记录时,并不知道另一个用户也要出库操作的,怎么办呢
#3
一般处理提成时会提示找不到记录或无法定位该记录。
#4
同步更新问题
需要建一个加锁表来实现对某条记录的锁、其锁的有效期以及锁该记录的用户名等~
需要建一个加锁表来实现对某条记录的锁、其锁的有效期以及锁该记录的用户名等~
#5
单靠DBMS来控制是不行的
#6
你说的这种情况就是数据库中的丢失更新,可以在操作时先利用锁机制,防止其它进程进行更新(先等待),
待操作完毕后,再进行
待操作完毕后,再进行
#7
楼上说得很对,当遇到这种情况时会发生死锁.
#8
原来老掉牙的FOXPRO系列有lock() unlock()
加锁、解锁指令,现在到了delphi,bcb怎没有这
些指令呢?
加锁、解锁指令,现在到了delphi,bcb怎没有这
些指令呢?
#9
楼主,可以用事务处理
#10
数据库事务包起来,同楼上
#11
使用事务
#12
ADODataSet1打开mdb库,
用DBGRID做试验,如果两个程序用DBGRID同时打开同一个数据库,
并同时编辑同一个记录的同一字段;
一个程序修改了内容,且POST;
另一程序也修改它,且POST,出现错误:
"无法为更新定位行.一些值可能在最后一次读取后已更改";
因为,第一个程序修改写入后,每二个程序的缓冲区与实际记录值
已不一致,称为"脏数据",再修改写入时,系统要检查是否"不一致",
只要出错了,说明"已脏";所以必须为"脏数据"刷新成"新数据"才能
写入,这是关键了;如果绕开这种"一致性"检查,是很危险的,如:
总数量:10只,
第一程序,得到的数量为:10,每二个程序,也得到了数量为:10,
第一个程序出库:3只,写入后,数量变成:7;
第二个程序也出库:3只,数量为10(脏数据),10-3=7,写回7,
这样10-3-3=7,乱了;
用DBGRID做试验,如果两个程序用DBGRID同时打开同一个数据库,
并同时编辑同一个记录的同一字段;
一个程序修改了内容,且POST;
另一程序也修改它,且POST,出现错误:
"无法为更新定位行.一些值可能在最后一次读取后已更改";
因为,第一个程序修改写入后,每二个程序的缓冲区与实际记录值
已不一致,称为"脏数据",再修改写入时,系统要检查是否"不一致",
只要出错了,说明"已脏";所以必须为"脏数据"刷新成"新数据"才能
写入,这是关键了;如果绕开这种"一致性"检查,是很危险的,如:
总数量:10只,
第一程序,得到的数量为:10,每二个程序,也得到了数量为:10,
第一个程序出库:3只,写入后,数量变成:7;
第二个程序也出库:3只,数量为10(脏数据),10-3=7,写回7,
这样10-3-3=7,乱了;
#13
POST()时,
如果用try() carch(...)()捕捉到缓冲中的当前记录
为"脏数据",如何刷新当前记录呢?(刷新所有记录太慢了)
如果用try() carch(...)()捕捉到缓冲中的当前记录
为"脏数据",如何刷新当前记录呢?(刷新所有记录太慢了)
#14
建议到网上看看这篇文章
Sqlserver2000中的并发问题
对于Access是单机版,最好尽量控制并发操作。
Sqlserver2000中的并发问题
对于Access是单机版,最好尽量控制并发操作。
#15
这称为"并发"操作,搜到不少ADO有关的并发操作问题
http://www.java-asp.net/vb/200602/t_17625.html
http://www.java-asp.net/vb/200602/t_17625.html
#16
ADO数据库并发操作和解决并发冲突
数据库并发问题已经说明了并发的严重性与危害性。下面讲述VB+ADO来处理并发操作的实际案例:
在以前DAO中可以对数据库进行记录锁,页面锁,表锁来处理并发操作,还可以使用事务处理,那么现在
怎么用ADO来检测并处理数据库的并发操作呢?
相关背景知识:
ADO中对数据库的也是采用锁定的方法来实现的,还可以用事务来做。事务有个特点就是:要么全成功,
要么就全失败。那么在实际工作中有可能只有几条或一小部分的记录有冲突,只要对那一小部分的记录进
行处理就行了。ADO也使用来锁定来实现。
那么什么是锁定?
锁定是一种进程,DBMS 通过该进程限制多用户环境中对行的访问。当一行或一列被独占锁定时,不允许
其他用户在释放锁定之前访问锁定的数据。这确保了两个用户无法同时更新一行中的同一列。
从资源角度而言,锁定的成本可能非常高昂,只有在需要保持数据完整性的情况下才应当使用此功能。在
每秒有数百或数千用户试图访问某个记录的数据库(例如连接到 Internet 的数据库)中,不必要的锁定
将很快导致应用程序性能的下降。
可选择适当的锁定选项来控制数据源和 ADO 游标库管理并发性的方式。
打开 Recordset 之前先设置 LockType 属性,以指定提供者打开它时使用的锁定类型。读取该属性以返
回打开的 Recordset 对象中使用的锁定类型。
提供者可能不支持所有锁定类型。如果提供者不支持请求的 LockType 设置,则替换为另一种锁定类型。
若要确定 Recordset 对象中实际可用的锁定功能,将soppurts 方法与 adUpdate 和 adUpdateBatch 一
起使用。
如果 CursorLocation 属性设置为 adUseClient,则不支持 adLockPessimistic 设置。如果设置了不支
持的值,将不产生错误,而使用所支持的最相近的 LockType。
LockType 属性在 Recordset 关闭时为读/写,而在 Recordset 打开时为只读。
锁定类型种类(LockType 属性):
1.adLockBatchOptimistic
指示开放式批更新。需要批更新模式。
许多应用程序都一次提取多行,然后需要进行相应的更新,这些更新包括所有需要插入、更新或删除的行
的完整集合。使用批游标只需一次往返服务器,因而导致更新性能的提高和网络通信量的降低。使用批游
标库可创建静态游标,然后断开到数据源的连接。这时就可以对行进行更改,然后重新连接到数据源并将
更改以批的形式发布到数据源。
2.adLockOptimistic
提供者使用开放式锁定,仅在调用 Update 方法时锁定记录。这意味着另一个用户有可能会利用您编辑记
录和调用 Update 的时间间隔更改数据,这就会引起冲突。而使用此锁定类型时发生冲突的几率很低,即
使发生了冲突也会很快得到解决。
3.adLockPessimistic
指示逐个记录保守式锁定。提供者要确保记录编辑成功,通常在编辑之前立即在数据源锁定记录。当然,
这意味着一旦您开始编辑记录,这些记录就会对其他用户不可用,知道您通过调用 Update 释放锁定。如
果您的系统不提供对数据的并发更改,例如预定系统,那么可使用此锁定类型。
4.adLockReadOnly
指示只读记录。无法改变数据。只读锁定是速度“最快”的锁定类型,因为它不要求服务器保持对记录的
锁定。冲突
5.adLockUnspecified
未指定锁定类型。
检测和解决冲突:
如果在立即模式中处理 Recordset,则很少出现并发问题。另一方面,如果应用程序使用批模式更新,则
在保存由一个正在编辑记录的用户所作的更改之前,编辑同一个记录的另一个用户比较有可能会更改记录
。在这种情况下,需要应用程序准确处理冲突。无论是哪种情况,都可以使用 ADO 提供的 Field 对象的
UnderlyingValue 和 OriginalValue 属性来处理这些类型的冲突。将这些属性与 Recordset 的 Resync
方法和 Filter 属性配合使用。
检测错误
在批更新期间 ADO 遇到冲突时,将在 Errors 集合中放入警告。因此,调用 BatchUpdate 之后,一定要
立即检查是否有错误,如果找到了错误,则应当假设已遇到冲突并开始进行测试。第一步要将 Recordset
的 Filter 属性设置为等于 adFilterConflictingRecords。该设置将使 Recordset对象限制为只显示那
些发生冲突的记录。如果这一步之后 RecordCount 属性等于零,就说明错误是由冲突以外的其他原因引
起的。
调用 BatchUpdate 时,ADO 和提供者将生成对数据源执行更新的 SQL 语句。下一步,调用 Recordset
的 Resync 方法,并且将 AffectRecords 参数设置为等于 adAffectGroup,将 ResyncValues 参数设置
为等于 adResyncUnderlyingValues。Resync 方法将用来自基本数据库中的数据刷新在当前 Recordset
对象中的数据。通过使用 adAffectGroup,可以确保只有使用当前筛选设置的情况下可见的记录(即只有
冲突记录)会与数据库重新同步。如果处理的是大型 Recordset,该操作会对性能有较大影响。通过在调
用 Resync 时将 ResyncValues 参数设置为 adResyncUnderlyingValues,可以确保 UnderlyingValue 属
性将包含数据库中的(冲突)值,并确保 Value 属性仍然是由用户输入的值,还会确保 OriginalValue
属性将持有字段的原始值(即在上一次成功进行 UpdateBatch 调用之前该字段所拥有的值)。然后,可
以使用这些值通过编程方式解决冲突,或要求用户选择将要使用的值。
数据库并发问题已经说明了并发的严重性与危害性。下面讲述VB+ADO来处理并发操作的实际案例:
在以前DAO中可以对数据库进行记录锁,页面锁,表锁来处理并发操作,还可以使用事务处理,那么现在
怎么用ADO来检测并处理数据库的并发操作呢?
相关背景知识:
ADO中对数据库的也是采用锁定的方法来实现的,还可以用事务来做。事务有个特点就是:要么全成功,
要么就全失败。那么在实际工作中有可能只有几条或一小部分的记录有冲突,只要对那一小部分的记录进
行处理就行了。ADO也使用来锁定来实现。
那么什么是锁定?
锁定是一种进程,DBMS 通过该进程限制多用户环境中对行的访问。当一行或一列被独占锁定时,不允许
其他用户在释放锁定之前访问锁定的数据。这确保了两个用户无法同时更新一行中的同一列。
从资源角度而言,锁定的成本可能非常高昂,只有在需要保持数据完整性的情况下才应当使用此功能。在
每秒有数百或数千用户试图访问某个记录的数据库(例如连接到 Internet 的数据库)中,不必要的锁定
将很快导致应用程序性能的下降。
可选择适当的锁定选项来控制数据源和 ADO 游标库管理并发性的方式。
打开 Recordset 之前先设置 LockType 属性,以指定提供者打开它时使用的锁定类型。读取该属性以返
回打开的 Recordset 对象中使用的锁定类型。
提供者可能不支持所有锁定类型。如果提供者不支持请求的 LockType 设置,则替换为另一种锁定类型。
若要确定 Recordset 对象中实际可用的锁定功能,将soppurts 方法与 adUpdate 和 adUpdateBatch 一
起使用。
如果 CursorLocation 属性设置为 adUseClient,则不支持 adLockPessimistic 设置。如果设置了不支
持的值,将不产生错误,而使用所支持的最相近的 LockType。
LockType 属性在 Recordset 关闭时为读/写,而在 Recordset 打开时为只读。
锁定类型种类(LockType 属性):
1.adLockBatchOptimistic
指示开放式批更新。需要批更新模式。
许多应用程序都一次提取多行,然后需要进行相应的更新,这些更新包括所有需要插入、更新或删除的行
的完整集合。使用批游标只需一次往返服务器,因而导致更新性能的提高和网络通信量的降低。使用批游
标库可创建静态游标,然后断开到数据源的连接。这时就可以对行进行更改,然后重新连接到数据源并将
更改以批的形式发布到数据源。
2.adLockOptimistic
提供者使用开放式锁定,仅在调用 Update 方法时锁定记录。这意味着另一个用户有可能会利用您编辑记
录和调用 Update 的时间间隔更改数据,这就会引起冲突。而使用此锁定类型时发生冲突的几率很低,即
使发生了冲突也会很快得到解决。
3.adLockPessimistic
指示逐个记录保守式锁定。提供者要确保记录编辑成功,通常在编辑之前立即在数据源锁定记录。当然,
这意味着一旦您开始编辑记录,这些记录就会对其他用户不可用,知道您通过调用 Update 释放锁定。如
果您的系统不提供对数据的并发更改,例如预定系统,那么可使用此锁定类型。
4.adLockReadOnly
指示只读记录。无法改变数据。只读锁定是速度“最快”的锁定类型,因为它不要求服务器保持对记录的
锁定。冲突
5.adLockUnspecified
未指定锁定类型。
检测和解决冲突:
如果在立即模式中处理 Recordset,则很少出现并发问题。另一方面,如果应用程序使用批模式更新,则
在保存由一个正在编辑记录的用户所作的更改之前,编辑同一个记录的另一个用户比较有可能会更改记录
。在这种情况下,需要应用程序准确处理冲突。无论是哪种情况,都可以使用 ADO 提供的 Field 对象的
UnderlyingValue 和 OriginalValue 属性来处理这些类型的冲突。将这些属性与 Recordset 的 Resync
方法和 Filter 属性配合使用。
检测错误
在批更新期间 ADO 遇到冲突时,将在 Errors 集合中放入警告。因此,调用 BatchUpdate 之后,一定要
立即检查是否有错误,如果找到了错误,则应当假设已遇到冲突并开始进行测试。第一步要将 Recordset
的 Filter 属性设置为等于 adFilterConflictingRecords。该设置将使 Recordset对象限制为只显示那
些发生冲突的记录。如果这一步之后 RecordCount 属性等于零,就说明错误是由冲突以外的其他原因引
起的。
调用 BatchUpdate 时,ADO 和提供者将生成对数据源执行更新的 SQL 语句。下一步,调用 Recordset
的 Resync 方法,并且将 AffectRecords 参数设置为等于 adAffectGroup,将 ResyncValues 参数设置
为等于 adResyncUnderlyingValues。Resync 方法将用来自基本数据库中的数据刷新在当前 Recordset
对象中的数据。通过使用 adAffectGroup,可以确保只有使用当前筛选设置的情况下可见的记录(即只有
冲突记录)会与数据库重新同步。如果处理的是大型 Recordset,该操作会对性能有较大影响。通过在调
用 Resync 时将 ResyncValues 参数设置为 adResyncUnderlyingValues,可以确保 UnderlyingValue 属
性将包含数据库中的(冲突)值,并确保 Value 属性仍然是由用户输入的值,还会确保 OriginalValue
属性将持有字段的原始值(即在上一次成功进行 UpdateBatch 调用之前该字段所拥有的值)。然后,可
以使用这些值通过编程方式解决冲突,或要求用户选择将要使用的值。
#17
范例:
在调用 UpdateBatch 之前,本范例通过使用单独的 Recordset 来更改基本表中的值,人为地创建一个冲
突。
'Begin
On Error GoTo ErrHandler:
Dim objRs1 As New ADODB.Recordset
Dim objRs2 As New ADODB.Recordset
Dim strSQL As String
Dim strMsg As String
strSQL = "SELECT * FROM Shippers WHERE ShipperID = 2"’SQL查询
objRs1.CursorLocation = adUseClient’设置客户端游标
objRs1.Open strSQL, strConn, adOpenStatic, adLockBatchOptimistic, adCmdText’执行查询生
成objrs1记录集
objRs1("Phone") = "(111) 555-1111"’更改表中一条记录phone字段的值
objRs2.Open strSQL, strConn, adOpenKeyset, adLockOptimistic, adCmdText’执行查询生成
objrs2记录集
objRs2("Phone") = "(999) 555-9999" ’更改表中记录phone字段的值
objRs2.Update
objRs2.Close
Set objRs2 = Nothing
On Error Resume Next
objRs1.UpdateBatch
If objRs1.ActiveConnection.Errors.Count <> 0 Then
Dim intConflicts As Integer
intConflicts = 0
objRs1.Filter = adFilterConflictingRecords
intConflicts = objRs1.RecordCount
objRs1.Resync adAffectGroup, adResyncUnderlyingValues
If intConflicts > 0 Then
strMsg = "A conflict occurred with updates for " & intConflicts & "records." &
vbCrLf & "The values will be restored" " to their original values."
objRs1.MoveFirst
While Not objRs1.EOF
strMsg = strMsg & "SHIPPER = " & objRs1("CompanyName") & vbCrLf
strMsg = strMsg & "Value = " & objRs1("Phone").Value & vbCrLf
strMsg = strMsg & "UnderlyingValue = " & _
objRs1("Phone").UnderlyingValue & vbCrLf
strMsg = strMsg & "OriginalValue = " & _
objRs1("Phone").OriginalValue & vbCrLf
strMsg = strMsg & vbCrLf & "Original value has been restored."
MsgBox strMsg, vbOKOnly, _
"Conflict " & objRs1.AbsolutePosition & _
" of " & intConflicts
objRs1("Phone").Value = objRs1("Phone").OriginalValue
objRs1.MoveNext
Wend
objRs1.UpdateBatch adAffectGroup
Else
strMsg = "Errors occurred during the update. " & _
objRs1.ActiveConnection.Errors(0).Number & " " & _
objRs1.ActiveConnection.Errors(0).Description
End If
On Error GoTo 0
End If
objRs1.MoveFirst
objRs1.Close
Set objRs1 = Nothing
Exit Sub
ErrHandler:
If Not objRs1 Is Nothing Then
If objRs1.State = adStateOpen Then objRs1.Close
Set objRs1 = Nothing
End If
If Not objRs2 Is Nothing Then
If objRs2.State = adStateOpen Then objRs2.Close
Set objRs2 = Nothing
End If
If Err <> 0 Then
MsgBox Err.Source & "-->" & Err.Description, , "Error"
End If
'End
还可以使用当前 Record 的或特定 Field 的 Status 属性来确定已发生的冲突种类。
至此发生冲突的记录检查出来了,那么下一步就是处理失败的更新。
如何解决错误将取决于错误的性质和严重性以及应用程序的逻辑。实际中的数据库大都数是多个用户共享
,典型的错误是其他人在你修改某个字段之前先修改了该字段。这种类型的错误称为“冲突”。ADO 将检
测这种情况并报告错误。
如果存在更新错误,将被错误处理例程捕获。通过使用 adFilterConflictingRecords 常量来筛选
Recordset,可以只显示发生冲突行。
范例:
本错误解决方案将打印发生冲突的记录中的作者的名和姓(au_fname 和 au_lname)。
'Begin:
objRs.Filter = adFilterConflictingRecords
objRs.MoveFirst
Do While Not objRst.EOF
Debug.Print "Conflict: Name = "; objRs!au_fname; " "; objRs!au_lname
objRs.MoveNext
Loop
'END
如果上述情况用事务来处理的话,那么对于用户数量较大网络较繁忙且更新记录数很多的情况来说将严重
影响速度。上述方法只要处理发生错误的记录,这样就减轻了网络传输的负荷,有利提高速度。
在调用 UpdateBatch 之前,本范例通过使用单独的 Recordset 来更改基本表中的值,人为地创建一个冲
突。
'Begin
On Error GoTo ErrHandler:
Dim objRs1 As New ADODB.Recordset
Dim objRs2 As New ADODB.Recordset
Dim strSQL As String
Dim strMsg As String
strSQL = "SELECT * FROM Shippers WHERE ShipperID = 2"’SQL查询
objRs1.CursorLocation = adUseClient’设置客户端游标
objRs1.Open strSQL, strConn, adOpenStatic, adLockBatchOptimistic, adCmdText’执行查询生
成objrs1记录集
objRs1("Phone") = "(111) 555-1111"’更改表中一条记录phone字段的值
objRs2.Open strSQL, strConn, adOpenKeyset, adLockOptimistic, adCmdText’执行查询生成
objrs2记录集
objRs2("Phone") = "(999) 555-9999" ’更改表中记录phone字段的值
objRs2.Update
objRs2.Close
Set objRs2 = Nothing
On Error Resume Next
objRs1.UpdateBatch
If objRs1.ActiveConnection.Errors.Count <> 0 Then
Dim intConflicts As Integer
intConflicts = 0
objRs1.Filter = adFilterConflictingRecords
intConflicts = objRs1.RecordCount
objRs1.Resync adAffectGroup, adResyncUnderlyingValues
If intConflicts > 0 Then
strMsg = "A conflict occurred with updates for " & intConflicts & "records." &
vbCrLf & "The values will be restored" " to their original values."
objRs1.MoveFirst
While Not objRs1.EOF
strMsg = strMsg & "SHIPPER = " & objRs1("CompanyName") & vbCrLf
strMsg = strMsg & "Value = " & objRs1("Phone").Value & vbCrLf
strMsg = strMsg & "UnderlyingValue = " & _
objRs1("Phone").UnderlyingValue & vbCrLf
strMsg = strMsg & "OriginalValue = " & _
objRs1("Phone").OriginalValue & vbCrLf
strMsg = strMsg & vbCrLf & "Original value has been restored."
MsgBox strMsg, vbOKOnly, _
"Conflict " & objRs1.AbsolutePosition & _
" of " & intConflicts
objRs1("Phone").Value = objRs1("Phone").OriginalValue
objRs1.MoveNext
Wend
objRs1.UpdateBatch adAffectGroup
Else
strMsg = "Errors occurred during the update. " & _
objRs1.ActiveConnection.Errors(0).Number & " " & _
objRs1.ActiveConnection.Errors(0).Description
End If
On Error GoTo 0
End If
objRs1.MoveFirst
objRs1.Close
Set objRs1 = Nothing
Exit Sub
ErrHandler:
If Not objRs1 Is Nothing Then
If objRs1.State = adStateOpen Then objRs1.Close
Set objRs1 = Nothing
End If
If Not objRs2 Is Nothing Then
If objRs2.State = adStateOpen Then objRs2.Close
Set objRs2 = Nothing
End If
If Err <> 0 Then
MsgBox Err.Source & "-->" & Err.Description, , "Error"
End If
'End
还可以使用当前 Record 的或特定 Field 的 Status 属性来确定已发生的冲突种类。
至此发生冲突的记录检查出来了,那么下一步就是处理失败的更新。
如何解决错误将取决于错误的性质和严重性以及应用程序的逻辑。实际中的数据库大都数是多个用户共享
,典型的错误是其他人在你修改某个字段之前先修改了该字段。这种类型的错误称为“冲突”。ADO 将检
测这种情况并报告错误。
如果存在更新错误,将被错误处理例程捕获。通过使用 adFilterConflictingRecords 常量来筛选
Recordset,可以只显示发生冲突行。
范例:
本错误解决方案将打印发生冲突的记录中的作者的名和姓(au_fname 和 au_lname)。
'Begin:
objRs.Filter = adFilterConflictingRecords
objRs.MoveFirst
Do While Not objRst.EOF
Debug.Print "Conflict: Name = "; objRs!au_fname; " "; objRs!au_lname
objRs.MoveNext
Loop
'END
如果上述情况用事务来处理的话,那么对于用户数量较大网络较繁忙且更新记录数很多的情况来说将严重
影响速度。上述方法只要处理发生错误的记录,这样就减轻了网络传输的负荷,有利提高速度。
#18
数据库并发问题
问题背景及特点:
我们在使用多用户数据库时常常会碰到数据更新失败、删除失等情况,如果有多个用户且同时访问一个数
据库则当他们的事务同时使用相同的数据时可能会发生并发问题。
并发问题包括:
1.丢失或覆盖更新。(幻像读)
2.未确认的相关性(脏读)。
3.不一致的分析(非重复读)。
详细描述:
1.丢失更新
当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,会发生丢失更新问题。每个事务都不
知道其它事务的存在。最后的更新将重写由其它事务所做的更新,这将导致数据丢失。
例如,两个编辑人员制作了同一文档的电子复本。每个编辑人员独立地更改其复本,然后保存更改后的复
本,这样就覆盖了原始文档。最后保存其更改复本的编辑人员覆盖了第一个编辑人员所做的更改。如果在
第一个编辑人员完成之后第二个编辑人员才能进行更改,则可以避免该问题。
2.未确认的相关性(脏读)
当第二个事务选择其它事务正在更新的行时,会发生未确认的相关性问题。第二个事务正在读取的数据还
没有确认并且可能由更新此行的事务所更改。
例如,一个编辑人员正在更改电子文档。在更改过程中,另一个编辑人员复制了该文档(该复本包含到目
前为止所做的全部更改)并将其分发给预期的用户。此后,第一个编辑人员认为目前所做的更改是错误的
,于是删除了所做的编辑并保存了文档。分发给用户的文档包含不再存在的编辑内容,并且这些编辑内容
应认为从未存在过。如果在第一个编辑人员确定最终更改前任何人都不能读取更改的文档,则可以避免该
问题。
3.不一致的分析(非重复读)
当第二个事务多次访问同一行而且每次读取不同的数据时,会发生不一致的分析问题。不一致的分析与未
确认的相关性类似,因为其它事务也是正在更改第二个事务正在读取的数据。然而,在不一致的分析中,
第二个事务读取的数据是由已进行了更改的事务提交的。而且,不一致的分析涉及多次(两次或更多)读
取同一行,而且每次信息都由其它事务更改;因而该行被非重复读取。
例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次读取
文档时,文档已更改。原始读取不可重复。如果只有在作者全部完成编写后编辑人员才可以读取文档,则
可以避免该问题。
4.幻像读
当对某行执行插入或删除操作,而该行属于某个事务正在读取的行的范围时,会发生幻像读问题。事务第
一次读的行范围显示出其中一行已不复存在于第二次读或后续读中,因为该行已被其它事务删除。同样,
由于其它事务的插入操作,事务的第二次或后续读显示有一行已不存在于原始读中。
例如,一个编辑人员更改作者提交的文档,但当生产部门将其更改内容合并到该文档的主复本时,发现作
者已将未编辑的新材料添加到该文档中。如果在编辑人员和生产部门完成对原始文档的处理之前,任何人
都不能将新材料添加到文档中,则可以避免该问题。
-----------------------------------------------------------------
从上面可以看到,解决并发主要是用到了锁和事务。
锁 :给记录或表加上锁是为了对当前操作对象加上一个状态表示位,
让其它用户在获取编辑权限时有了判断。
事务:是为了保证一组操作的完整性。(要么就全部成功,要么就全部失败)
------------------------------------------------------------------
一般处理并发问题时我这样做:
1.开启事务。
2.申请写权限,也就是给对象(表或记录)加锁。
3.如果失败,则结束事务,过一会重试。
4.如果成功,也就是给对象加锁成功,防止其它用户再用同样的方式打开。
5.进行编辑操作。
6.写入所进行的编辑结果。
7.如果写入成功,则提交事务,完成操作。
8.如果写入失败,则回滚事务,取消提交。
9.(7.8)两步操作已释放了锁定的对象,恢复到操作前的状态。
问题背景及特点:
我们在使用多用户数据库时常常会碰到数据更新失败、删除失等情况,如果有多个用户且同时访问一个数
据库则当他们的事务同时使用相同的数据时可能会发生并发问题。
并发问题包括:
1.丢失或覆盖更新。(幻像读)
2.未确认的相关性(脏读)。
3.不一致的分析(非重复读)。
详细描述:
1.丢失更新
当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,会发生丢失更新问题。每个事务都不
知道其它事务的存在。最后的更新将重写由其它事务所做的更新,这将导致数据丢失。
例如,两个编辑人员制作了同一文档的电子复本。每个编辑人员独立地更改其复本,然后保存更改后的复
本,这样就覆盖了原始文档。最后保存其更改复本的编辑人员覆盖了第一个编辑人员所做的更改。如果在
第一个编辑人员完成之后第二个编辑人员才能进行更改,则可以避免该问题。
2.未确认的相关性(脏读)
当第二个事务选择其它事务正在更新的行时,会发生未确认的相关性问题。第二个事务正在读取的数据还
没有确认并且可能由更新此行的事务所更改。
例如,一个编辑人员正在更改电子文档。在更改过程中,另一个编辑人员复制了该文档(该复本包含到目
前为止所做的全部更改)并将其分发给预期的用户。此后,第一个编辑人员认为目前所做的更改是错误的
,于是删除了所做的编辑并保存了文档。分发给用户的文档包含不再存在的编辑内容,并且这些编辑内容
应认为从未存在过。如果在第一个编辑人员确定最终更改前任何人都不能读取更改的文档,则可以避免该
问题。
3.不一致的分析(非重复读)
当第二个事务多次访问同一行而且每次读取不同的数据时,会发生不一致的分析问题。不一致的分析与未
确认的相关性类似,因为其它事务也是正在更改第二个事务正在读取的数据。然而,在不一致的分析中,
第二个事务读取的数据是由已进行了更改的事务提交的。而且,不一致的分析涉及多次(两次或更多)读
取同一行,而且每次信息都由其它事务更改;因而该行被非重复读取。
例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次读取
文档时,文档已更改。原始读取不可重复。如果只有在作者全部完成编写后编辑人员才可以读取文档,则
可以避免该问题。
4.幻像读
当对某行执行插入或删除操作,而该行属于某个事务正在读取的行的范围时,会发生幻像读问题。事务第
一次读的行范围显示出其中一行已不复存在于第二次读或后续读中,因为该行已被其它事务删除。同样,
由于其它事务的插入操作,事务的第二次或后续读显示有一行已不存在于原始读中。
例如,一个编辑人员更改作者提交的文档,但当生产部门将其更改内容合并到该文档的主复本时,发现作
者已将未编辑的新材料添加到该文档中。如果在编辑人员和生产部门完成对原始文档的处理之前,任何人
都不能将新材料添加到文档中,则可以避免该问题。
-----------------------------------------------------------------
从上面可以看到,解决并发主要是用到了锁和事务。
锁 :给记录或表加上锁是为了对当前操作对象加上一个状态表示位,
让其它用户在获取编辑权限时有了判断。
事务:是为了保证一组操作的完整性。(要么就全部成功,要么就全部失败)
------------------------------------------------------------------
一般处理并发问题时我这样做:
1.开启事务。
2.申请写权限,也就是给对象(表或记录)加锁。
3.如果失败,则结束事务,过一会重试。
4.如果成功,也就是给对象加锁成功,防止其它用户再用同样的方式打开。
5.进行编辑操作。
6.写入所进行的编辑结果。
7.如果写入成功,则提交事务,完成操作。
8.如果写入失败,则回滚事务,取消提交。
9.(7.8)两步操作已释放了锁定的对象,恢复到操作前的状态。
#19
受教了,建议在事务中操作。
#20
三层数据库的事务处理不好写,很麻烦.