【问题标题】:Row by row streaming of data while waiting for response在等待响应时逐行流式传输数据
【发布时间】:2014-07-19 05:25:45
【问题描述】:

我有一个使用 C# 开发的 WCF 服务和客户端,它处理从服务器上的 SQL Server 数据库到客户端上的 SQL Server 数据库的数据传输。我正面临当前架构的一些问题,并计划将其修改为我的想法,并想知道是否有可能实现它,或者我如何才能最好地修改架构以满足我的需求。

服务器端数据库服务器是 SQL 2008 R2 SP1,客户端服务器是 SQL 2000

在我陈述这个想法之前,下面是我正在使用的架构设计的概述和当前缺点。

概述:

  1. 客户端请求表的数据。

  2. WCF 服务查询服务器数据库以获取所请求表的所有待处理数据。该数据被加载到数据集中。

  3. WCF 使用 GZIP 压缩压缩数据集并将其转换为字节以供客户端下载。

  4. 客户端接收字节流,解压缩并将数据从数据集复制到客户端数据库的物理表中。此数据逐行插入,因为需要将主键列字段返回到服务器,以便将其标记为已传输。

  5. 客户端完成数据复制后,会将成功的行主键字段上传回服务器,服务器依次更新每个字段。 上述过程使用基本的 http 绑定,具有流传输模式。

缺点:

  1. 这对于少量数据非常有效,但对于批量数据,在下载进行时将数据集保存在内存中,并且在复制进行时在客户端维护数据集变得不可能,因为有时数据集大小高达4GB。服务器可以容纳这么多数据,因为它是一个 32gb RAM 服务器,但是在客户端我得到 System out of memory 异常,因为客户端机器有 2gb RAM。

  2. 由于我使用事务模式作为已提交读,在运行选择查询以及更新时会出现许多死锁。

  3. 对于批量数据,它非常慢,并且在 DTS 正在进行时会完全挂起客户端计算机。

想法:

保持相同的服务和逐行传输的逻辑,因为由于数据的敏感性,我无法更改这一点,但我计划使用http://code.msdn.microsoft.com/Custom-WCF-Streaming-436861e6 中给出的示例,而不是下载批量数据。 因此,新流程将是:

  1. 在收到下载请求后,服务器将使用快照隔离作为事务级别打开与数据库的连接。

  2. 在服务器上逐行构建对象并在请求的通道上将其发送给客户端,当客户端接收到每个行对象时,它会被处理并将成功或失败响应发送回服务器相同的方法相同的通道,因为我需要更新同一个快照事务上的数据。 这样,我将减少内存中的批量对象,并依赖 SQL 获取事务启动后将在 temdb 中维护的快照数据。

挑战:

  1. 如何在发送下一个对象之前发送行对象并等待确认,因为对服务器行的更新必须发生在同一个快照事务上。因为如果我在服务上创建另一种方法来执行标记关闭,快照会有所不同,这将导致数据完整性问题,以防在启动快照事务后数据发生更改。

    李>
  2. 如果这是错误的方法,请提出更好的方法,因为我愿意接受任何建议。

  3. 如果我对快照隔离的理解有误,请纠正我,因为我是新手。

更新 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


【解决方案1】:

就在我的脑海中,这听起来可能需要更长的时间。这可能是也可能不是问题。

鉴于挑战 1 中的要求(一切都发生在一个方法调用的上下文中),听起来实际上需要发生的是 服务器 调用 上的方法客户端,发送一条记录,然后等待客户端返回确认。这样,需要发生的一切都发生在单个调用(服务器到客户端)的上下文中。我不知道这在你的情况下是否可行。

另一种选择可能是使用某种双队列系统(可能使用 MSMQ?),以便服务器和客户端可以在单个会话中保持正在进行的对话。

我认为您不能将要下载的数据分成可管理的块并在这些块上重复执行原始过程是有原因的。这听起来是最不雄心勃勃的选择,但如果它满足您的所有需求,您可能已经这样做了。

【讨论】:

  • 上述更新可以使用MSMQ吗?如果可以,请您指导我,我还没有与 MSMQ 合作过。谢谢
  • 我不知道 MSMQ 是否可以用于此:我只是提到它是一种可能性。我在 MSMQ 上的工作很少。抱歉,我对此无能为力。
猜你喜欢
  • 1970-01-01
  • 2021-11-04
  • 2023-03-30
  • 2012-09-05
  • 2019-06-04
  • 1970-01-01
  • 2018-02-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多