可靠连接 Azure SQL 数据库

4
我正在开发一个使用C#编写的应用程序,将数据存储在Azure SQL数据库中。
正如您可能知道的那样,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进行了支付,而我会认为我的更改是由我执行的(这是错误的)。
怎么办?

看一下 rowversion。https://msdn.microsoft.com/zh-cn/library/ms182776.aspx 这是确保插入和更新的行一致性的可靠方法。 - jvanrhyn
不解决问题。例如,如果在“Pay()”时出现异常,并且行版本表明执行了2个修改,则我无法确定是我还是另一个用户与我完全相同地进行了修改。 - No1Lives4Ever
1
如果您正在进行交易,我不希望出现任何混乱。如果提交成功,则数据库处于正确状态;如果提交失败,则会被您或服务器回滚,并且您可以重试整个事务。 - rene
我不确定数据库服务器是否收到了我的请求。网络故障可能出现在我的请求或服务器的响应中。 - No1Lives4Ever
无法从多台机器管理单个数据库。是否可以将用户分区,以便任何用户都由单个机器处理? - marnun
你考虑过使用事务吗? - Rom Eh
2个回答

1

听起来你正在从本地/本地服务器/远程(即非Azure属性)向SQL Azure数据库进行SQL查询。

处理这个问题的一些可能机制是:

  • Azure托管数据访问层与API

    考虑创建一个托管在Azure WebApp或VM上的薄数据访问层API,从远程机器调用此API服务。该API服务可以可靠地与SQL Azure交互。

    相比于HTTP端点,SQL对超时和网络问题更为敏感。特别是如果你的查询涉及大量数据的传输。

  • 配置超时时间增加

    问题中没有指定C#应用程序使用的数据库访问机制。许多数据访问库或函数允许您为连接指定增加的超时时间。

  • 虚拟专用网络

    Azure允许您创建站点到站点或点到站点VPN,以获得更好的网络连接性。然而,这是最不受欢迎的机制。


1
您永远不会盲目重试。在出现错误时,您需要读取当前状态,然后重新应用逻辑,然后写入新状态。"应用逻辑"的含义因情况而异。可以再次向用户呈现表单、刷新网页、运行业务逻辑中的方法或其他任何操作。
总体来说,您不能仅在重新加载持久化状态之前简单地重试操作。唯一的真相是数据库中的内容,错误是缓存状态过期的一个大警告。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接