【问题标题】:xamarin forms azure mobile apps slow syncxamarin 形成 azure 移动应用程序慢速同步
【发布时间】:2018-06-23 21:35:55
【问题描述】:

我正在使用带有 Xamarin.Forms 的 Azure 移动应用程序来创建支持脱机的移动应用程序。

我的解决方案是基于https://adrianhall.github.io/develop-mobile-apps-with-csharp-and-azure/chapter3/client/

这是我用于离线同步的代码:

public class AzureDataSource
    {
        private async Task InitializeAsync()
        {
            // Short circuit - local database is already initialized
            if (client.SyncContext.IsInitialized)
            {
                return;
            }

            // Define the database schema
            store.DefineTable<ArrayElement>();
            store.DefineTable<InputAnswer>();
            //Same thing with 16 others table
            ...

            // Actually create the store and update the schema
            await client.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler());
        }

        public async Task SyncOfflineCacheAsync()
        {
            await InitializeAsync();

            //Check if authenticated
            if (client.CurrentUser != null)
            {
                // Push the Operations Queue to the mobile backend
                await client.SyncContext.PushAsync();

                // Pull each sync table
                var arrayTable = await GetTableAsync<ArrayElement>();
                await arrayTable.PullAsync();

                var inputAnswerInstanceTable = await GetTableAsync<InputAnswer>();
                await inputAnswerInstanceTable.PullAsync();

                //Same thing with 16 others table
                ...
            }
        }

        public async Task<IGenericTable<T>> GetTableAsync<T>() where T : TableData
        {
            await InitializeAsync();
            return new AzureCloudTable<T>(client);
        }
    }
    public class AzureCloudTable<T>
    {
        public AzureCloudTable(MobileServiceClient client)
        {
            this.client = client;
            this.table = client.GetSyncTable<T>();
        }

        public async Task PullAsync()
        {
            //Query name used for incremental pull
            string queryName = $"incsync_{typeof(T).Name}";

            await table.PullAsync(queryName, table.CreateQuery());
        }
    }

问题在于,即使没有任何东西可以拉取,同步也需要很长时间(Android 设备上需要 8-9 秒,拉取整个数据库需要 25 秒以上)。

我查看了Fiddler 以了解移动应用后端响应需要多长时间,每个请求大约需要 50 毫秒,所以问题似乎不是来自这里。

有人遇到同样的问题吗?是否有什么我做错了什么或提示可以提高我的同步性能?

【问题讨论】:

  • 你解决了吗?我也看到了
  • 这方面也有很多麻烦。我们有相当大的数据集(最高为 160 行)。尝试做一个 50 组的拉动大约需要 2 分半钟。更进一步的问题是,即使用户手机上已经存在数据,即使没有进行任何更改,加载仍需要大约 30-40 秒。如果设备离线,从手机上的 SQLiteDB 访问相同的数据,几乎是即时的。
  • 经历同样的事情。对我来说,这看起来像是一个内存问题。表同步之间的同步暂停以允许 GC.Collect()。使用 Xamarin Profiler,同步结果在 400 - 600 Megs 之间 - 哎哟:(
  • @InquisitorJax 你能从你的发现中做出任何改进吗?
  • @Bejasc 很遗憾没有 - 我认为 MS 并没有对 Azure App Service atm 给予太多关注 :(

标签: xamarin.forms xamarin.android azure-mobile-services offline-caching


【解决方案1】:

Pull() 缓慢的原因之一是当超过 (10) 行获得相同的 UpdatedAt 值时。当您一次更新行时会发生这种情况,例如运行 SQL 命令。

解决此问题的一种方法是修改表上的默认触发器。为了确保每一行都有一个唯一的 UpdateAt,我们做了这样的事情:

ALTER TRIGGER [dbo].[TR_dbo_Items_InsertUpdateDelete] ON [dbo].[TableName]
AFTER INSERT, UPDATE, DELETE
AS
     BEGIN

         DECLARE @InsertedAndDeleted TABLE
         (
              Id        NVARCHAR(128) 
         );
         DECLARE @Count     INT, 
                 @Id        NVARCHAR(128);

         INSERT INTO @InsertedAndDeleted
                SELECT Id
                FROM inserted;
         INSERT INTO @InsertedAndDeleted
                SELECT Id
                FROM deleted
                WHERE Id NOT IN
                (
                    SELECT Id
                    FROM @InsertedAndDeleted
                );

         --select * from @InsertedAndDeleted;
         SELECT @Count = Count(*)
         FROM @InsertedAndDeleted;

         -- ************************ UpdatedAt ************************
         -- while loop
         WHILE @Count > 0
         BEGIN
             -- selecting
             SELECT TOP (1) @Id = Id
             FROM @InsertedAndDeleted;

             -- updating
             UPDATE [dbo].[TableName]
                    SET UpdatedAt = Convert(DATETIMEOFFSET, DateAdd(MILLISECOND, @Count, SysUtcDateTime()))
             WHERE Id = @Id;

             -- deleting
             DELETE FROM @InsertedAndDeleted
             WHERE id = @Id;

             -- counter
             SET @Count = @Count - 1;
         END;
     END;

【讨论】:

    【解决方案2】:

    我们的特定问题与我们的数据库迁移有关。数据库中的每一行都有相同的updatedAt 值。我们运行了一个 SQL 脚本来修改它们,使它们都是唯一的。

    此修复实际上是针对我们遇到的其他一些问题,即由于某种未知原因并非所有行都被返回,但我们也看到了显着的速度提升。


    此外,另一个改进加载时间的奇怪修复如下。

    在我们第一次提取所有数据之后(可以理解,这需要一些时间) - 我们对返回的一个行执行了UpdateAsync(),并且我们执行了不要事后推它。

    我们已经了解到离线同步的工作方式是,它将提取日期更新的任何内容,而不是最近更新的日期。与此相关的速度略有提高。


    最后,我们为提高速度所做的最后一件事是不要再次获取数据,如果它已经在视图中缓存了一个副本。不过,这可能不适用于您的用例。

    public List<Foo> fooList = new List<Foo>
    
    public void DisplayAllFoo()
    {
        if(fooList.Count == 0)
            fooList = await SyncClass.GetAllFoo();
    
        foreach(var foo in fooList)
        {
            Console.WriteLine(foo.bar);
        }
    }
    

    2019 年 3 月 20 日编辑: 有了这些改进,我们仍然看到非常慢的同步操作,其使用方式与 OP 中提到的相同,还包括我在此处的答案中列出的改进。

    我鼓励所有人分享他们关于如何提高速度的解决方案或想法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-10-09
      • 2016-08-24
      • 2018-02-20
      • 2011-09-28
      • 1970-01-01
      • 1970-01-01
      • 2017-01-03
      相关资源
      最近更新 更多