xamarin 形成 azure 移动应用程序慢速同步

Posted

技术标签:

【中文标题】xamarin 形成 azure 移动应用程序慢速同步【英文标题】:xamarin forms azure mobile apps slow sync 【发布时间】: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 给予太多关注 :( 【参考方案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 中提到的相同,还包括我在此处的答案中列出的改进。

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

【讨论】:

以上是关于xamarin 形成 azure 移动应用程序慢速同步的主要内容,如果未能解决你的问题,请参考以下文章

Xamarin MonoAndroid Azure 移动服务 InsertAsync

Azure 移动服务 Xamarin.Android 推送通知示例应用程序出现问题

在 Xamarin 移动应用程序中显示存储在 Azure 存储中的 Blob/照片

使用 PCL 和 Xamarin 对 Azure 移动服务执行单元测试时程序集版本不匹配

为啥我的 Xamarin Forms iOS 应用程序在使用 Microsoft Azure 移动服务时崩溃

多表离线同步 Xamarin.Forms & Azure