【发布时间】:2014-07-19 05:25:45
【问题描述】:
我有一个使用 C# 开发的 WCF 服务和客户端,它处理从服务器上的 SQL Server 数据库到客户端上的 SQL Server 数据库的数据传输。我正面临当前架构的一些问题,并计划将其修改为我的想法,并想知道是否有可能实现它,或者我如何才能最好地修改架构以满足我的需求。
服务器端数据库服务器是 SQL 2008 R2 SP1,客户端服务器是 SQL 2000
在我陈述这个想法之前,下面是我正在使用的架构设计的概述和当前缺点。
概述:
客户端请求表的数据。
WCF 服务查询服务器数据库以获取所请求表的所有待处理数据。该数据被加载到数据集中。
WCF 使用 GZIP 压缩压缩数据集并将其转换为字节以供客户端下载。
客户端接收字节流,解压缩并将数据从数据集复制到客户端数据库的物理表中。此数据逐行插入,因为需要将主键列字段返回到服务器,以便将其标记为已传输。
客户端完成数据复制后,会将成功的行主键字段上传回服务器,服务器依次更新每个字段。 上述过程使用基本的 http 绑定,具有流传输模式。
缺点:
这对于少量数据非常有效,但对于批量数据,在下载进行时将数据集保存在内存中,并且在复制进行时在客户端维护数据集变得不可能,因为有时数据集大小高达4GB。服务器可以容纳这么多数据,因为它是一个 32gb RAM 服务器,但是在客户端我得到 System out of memory 异常,因为客户端机器有 2gb RAM。
由于我使用事务模式作为已提交读,在运行选择查询以及更新时会出现许多死锁。
对于批量数据,它非常慢,并且在 DTS 正在进行时会完全挂起客户端计算机。
想法:
保持相同的服务和逐行传输的逻辑,因为由于数据的敏感性,我无法更改这一点,但我计划使用http://code.msdn.microsoft.com/Custom-WCF-Streaming-436861e6 中给出的示例,而不是下载批量数据。 因此,新流程将是:
在收到下载请求后,服务器将使用快照隔离作为事务级别打开与数据库的连接。
在服务器上逐行构建对象并在请求的通道上将其发送给客户端,当客户端接收到每个行对象时,它会被处理并将成功或失败响应发送回服务器相同的方法相同的通道,因为我需要更新同一个快照事务上的数据。 这样,我将减少内存中的批量对象,并依赖 SQL 获取事务启动后将在 temdb 中维护的快照数据。
挑战:
-
如何在发送下一个对象之前发送行对象并等待确认,因为对服务器行的更新必须发生在同一个快照事务上。因为如果我在服务上创建另一种方法来执行标记关闭,快照会有所不同,这将导致数据完整性问题,以防在启动快照事务后数据发生更改。
李> 如果这是错误的方法,请提出更好的方法,因为我愿意接受任何建议。
如果我对快照隔离的理解有误,请纠正我,因为我是新手。
更新 1:
当客户是请求者时,我想实现这样的目标:
//Client Invokes this method on the server
public Stream GetData(string sTableName)
{
//Open the Snapshot Transaction on the Server
SqlDataReader rdr = operations.InitSnapshotTrans("Select * from " + sTableName + " Where Isnull(ColToCheck,'N') <> 'Y'");
//Check if there are rows available
if(rdr.HasRows)
{
while rdr.read()
{
SendObj sendobj = Logic.CreateObejct(rdr);
//Here is where i am stuck
//At this point I want to write the object to the Stream
...Write sendobj to Stream
//Once the client is done processing it reverts with a true for success or false for failuer.
if (returnObj == true)
{
operations.updateMovedRecord(rdr);
}
}
}
}
对于发送的服务器,我已经编写了这样的代码(我为此使用了 Pub Sub 模型):
public void ServerData(string sServerText)
{
List<SubList> subscribers = Filter.GetClients();
if (subscribers == null) return;
Type type = typeof(ITransfer);
MethodInfo publishMethodInfo = type.GetMethod("ServerData");
foreach (SubList subscriber in subscribers)
{
try
{
//Open the Snapshot Transaction on the Server
SqlDataReader rdr = operations.InitSnapshotTrans("Select * from " + sTableName + " Where Isnull(ColToCheck,'N') <> 'Y'");
//Check if there are rows available
if(rdr.HasRows)
{
while rdr.read()
{
SendObj sendobj = Logic.CreateObejct(rdr);
bool rtnVal = Convert.ToBoolean(publishMethodInfo.Invoke(subscriber.CallBackId, new object[] { sendobj }));
if (rtnVal == true)
{
operations.updateMovedRecord(rdr);
}
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
}
【问题讨论】:
-
两个看似完全无关的问题。首先,您传输的数据是如何相关的,您是一次在一个表中发送,还是在具有所有依赖项的整个实体中发送?第二,你有没有客户端服务器的控制权,你能修改上面的SQL脚本吗?
-
回答第一个问题:传输的数据是SQL表中的数据。基本上我所做的是从服务器表中选择一行,将该行转换为我的实体,将实体发送到客户端,并根据数据和配置创建插入、更新或删除语句,并在客户端执行该语句边。第二个问题的答案是肯定的,应用程序对 SQL 服务器具有完全控制权。
标签: c# performance wcf sql-server-2008 sql-server-2000