我正在开发一个使用C#编写的应用程序,将数据存储在Azure SQL数据库中。
正如您可能知道的那样,Azure SQL数据库位于互联网上。不是在局域网(但这个问题也适用于可靠的网络,比如局域网)。
我注意到,有时候会出现诸如“连接已关闭”(或其他网络错误)之类的错误。可以使用Clumsy轻松模拟这些错误。这些错误的原因是网络状况不佳。
因此,我解决这个问题的第一个想法是“再试一次”。当我遇到这个错误时,我只需简单地再试一次,然后它就可以正常工作了,就像魔术一样。
这可能解决了问题,但同时又带来了另一种问题。并非所有情况都适用于这种解决方案。我来解释一下:
我将场景分为两种类型:
1. 重试不会造成任何损坏 - 操作,如SELECT或DELETE。重试将产生相同的预期结果。因此,在这种类型的问题上,我的解决方案很好!
2. 插入或更新 - 重试将损坏信息。
我将关注点放在第二点上。例如,假设我有:
在让用户支付之前,我会检查版本ID(随机GUID),如果在支付期间发生网络故障后此GUID未更改,则会尝试再次支付(证明数据未在数据库上更改)。如果此VersionId已更改,则表示用户已支付服务费用。
问题是当我同时使用多台机器时,这个解决方案就会出现问题。因为另一个实例可能已经对版本ID进行了支付,而我会认为我的更改是由我执行的(这是错误的)。
怎么办?
正如您可能知道的那样,Azure SQL数据库位于互联网上。不是在局域网(但这个问题也适用于可靠的网络,比如局域网)。
我注意到,有时候会出现诸如“连接已关闭”(或其他网络错误)之类的错误。可以使用Clumsy轻松模拟这些错误。这些错误的原因是网络状况不佳。
因此,我解决这个问题的第一个想法是“再试一次”。当我遇到这个错误时,我只需简单地再试一次,然后它就可以正常工作了,就像魔术一样。
这可能解决了问题,但同时又带来了另一种问题。并非所有情况都适用于这种解决方案。我来解释一下:
我将场景分为两种类型:
1. 重试不会造成任何损坏 - 操作,如SELECT或DELETE。重试将产生相同的预期结果。因此,在这种类型的问题上,我的解决方案很好!
2. 插入或更新 - 重试将损坏信息。
我将关注点放在第二点上。例如,假设我有:
- 一个用户表。该表的列包括:ID、UserName、Credits。
- 存储过程,通过用户ID让用户支付他的一些积分。
"Pay" 存储过程如下:
UPDATE tblUsers SET [Credits] -= @requestedCredits WHERE ID=@ID
调用存储过程是一个棘手的问题:
- 如果这能够无误地运行,那我们就没问题了。
- 但如果出现错误,我们不知道操作是否已在数据库上完成。在此重试可能会导致用户“支付两次”!
因此,“重试”策略在这里不可行。
我考虑的解决方案:
我想通过为每一行添加“VersionID”来解决这个问题。我的存储过程现在是:
UPDATE tblUsers SET [Credits] -= @requestedCredits, VersionId=NEWID() WHERE ID=@ID AND VersionID=@OldVersionId
在让用户支付之前,我会检查版本ID(随机GUID),如果在支付期间发生网络故障后此GUID未更改,则会尝试再次支付(证明数据未在数据库上更改)。如果此VersionId已更改,则表示用户已支付服务费用。
问题是当我同时使用多台机器时,这个解决方案就会出现问题。因为另一个实例可能已经对版本ID进行了支付,而我会认为我的更改是由我执行的(这是错误的)。
怎么办?