【发布时间】:2013-07-21 16:06:19
【问题描述】:
我有一个现成的适用于 iPad 的企业(非 AppStore)旧版 iOS 应用程序,我需要对其进行重构(它是由另一位开发人员编写的,我目前工作的前任)。
此应用程序通过 JSON 从具有 MSSQL 数据库的服务器获取其数据。数据库模式有大约 30 个表,最大容量是:Client、City、Agency,每个表都有大约 10.000 条记录,预计未来会进一步增长。收到 JSON 后(每个表有一个 JSON 请求和响应对) - 它被映射到 CoreData - 该过程还包括将相应的 CoreData 实体(客户、城市、代理机构等)彼此粘合在一起,即在 CoreData 层上设置这些实体之间的关系。
项目的CoreData fetch-part(或read-part)本身已经过高度优化 - 我猜它使用了CoreData几乎所有可能的性能和内存调整,这就是为什么应用程序的UI层非常快速且响应迅速,所以我认为它的工作是完全令人满意和充分的。
问题是CoreData层的准备过程,即服务器到客户端的同步过程:耗时太长。考虑 30 个网络请求产生 30 个 JSON 包(“包”我的意思是“一个表 - 一个 JSON”),然后映射到 30 个 CoreData 实体,然后将它们粘合在一起(在它们之间设置适当的 CoreData 关系)。当我第一次看到这一切是如何在这个项目中完成的(太慢了)时,我脑海中浮现的第一个想法是:
“第一次执行完整的同步(应用程序的第一次启动时间) - 在一个存档文件(类似于数据库转储)中执行 整个数据库 数据的获取,然后以某种方式将其作为一个整体导入核心数据领域”。
但后来我意识到,即使这种单一文件转储的传输是可能的,CoreData 仍然需要我对相应的 CoreData 实体进行粘合以设置它们之间的适当关系,所以很难想象如果我依赖这个方案,我可以在性能上受益。
另外,我的同事建议我将 SQLite 视为 Core Data 的完全替代品,但不幸的是我没有使用它的经验,这就是为什么我完全无法预见如此严肃的设计决策的所有后果(即使同步过程非常缓慢,我的应用确实可以工作,尤其是它的 UI 性能现在非常好)。关于 SQLite,我唯一能想象到的是,与 Core Data 相比,它不会促使我在客户端粘合一些额外的关系,因为 SQLite 有其良好的旧外键系统,不是吗?
以下是问题(受访者,请不要在回答时混淆这些观点 - 我对所有这些观点都感到困惑):
有没有人有像我上面描述的那样采用“首次大量导入整个数据库”方法的经验?如果他们是否利用 JSONCoreData 对,我将非常感谢了解任何解决方案。
Core Data 是否有一些全局导入机制,可以允许大量创建相应的 30 表模式(可能使用上述“30 包 JSON”以外的某些特定源)而无需设置30个实体的对应关系?
如果 2) 是不可能的,是否有可能加快同步过程?这里我指的是我的应用使用的当前 JSONCoreData 方案的改进。
迁移到 SQLite:我应该考虑这种迁移吗?我会从中得到什么好处?那么复制->传输->客户端准备的整个过程会是怎样的呢?
CoreData 和 SQLite 的其他替代品 - 它们会是什么或看起来像什么?
您对我所描述的情况还有其他想法或看法吗?
更新 1
虽然 Mundi 写的答案很好(一个大的 JSON,对于使用 SQLite “否”),我仍然对我所描述的问题有任何其他见解感兴趣。
更新 2
我确实尝试使用我的俄语英语以最好的方式来描述我的情况,希望我的问题对所有阅读它的人来说都非常清楚。通过第二次更新,我将尝试为其提供更多指南,以使我的问题更加清晰。
请考虑两种二分法:
- 我可以/应该使用什么作为 iOS 客户端上的数据层 - CoreData 与 SQLite?
- 我可以/应该使用什么作为传输层 - JSON(答案中建议的一次性 JSON,甚至可能压缩)或一些 DB 本身转储(当然,如果可能的话 -请注意,我也在我的问题中提出这个问题)。
我认为由这两个二分法相交形成的“扇区”很明显,从第一个中选择 CoreData,从第二个中选择 JSON,是 iOS 开发世界中最广泛使用的默认设置,它也被使用来自这个问题的我的应用程序。
话虽如此,我声称我会很高兴看到有关 CoreData-JSON 对的任何答案以及考虑使用任何其他“部门”的答案(选择 SQLite 及其某种转储方法怎么样,为什么不?)
另外,需要注意的重要一点是,我不想仅仅放弃当前选项而使用其他一些替代方案,我只想让解决方案在其使用的同步和 UI 阶段都能快速运行。因此,欢迎提供有关改进当前方案的答案以及建议其他方案的答案!
现在,请查看以下更新 #3,它提供了我当前 CoreData-JSON 情况的更多详细信息:
更新 3
正如我所说,目前我的应用程序接收 30 包 JSON - 一个包用于整个表。让我们以大容量表为例:Client、Agency、City。
它是核心数据,所以如果 client 记录有非空的 agency_id 字段,我需要创建新的核心数据实体 Agency (NSManagedObject subclass) 并用该记录的 JSON 数据填充它,这就是我需要的原因已经为Agency (NSManagedObject's subclass) 类的这个机构有相应的核心数据实体,最后我需要做类似client.agency = agency; 的事情,然后调用[currentManagedObjectContext save:&error]。以这种方式完成后,我可以要求获取此客户端并要求其.agency 属性查找相应的实体。我希望当我这样做时我是完全清醒的。
现在想象一下这种模式应用于以下情况:
我刚刚收到以下 3 个单独的 JSON 包:10000 个客户和 4000 个城市和 6000 个代理(客户有一个城市,城市有很多客户;客户有代理,代理有很多客户,代理有一个城市,城市有很多机构)。
现在我想在核心数据级别设置以下关系:我希望我的客户实体client 连接到相应的城市和相应的机构。
目前这个在项目中的实现做了一件很丑陋的事情:
由于依赖顺序如下:City -> Agency -> Client,即需要先烘焙 City,应用程序开始为 City 创建实体并将它们持久化到 Core Data。
然后它处理机构的 JSON:它遍历每个 JSON 记录 - 对于每个机构,它创建一个新实体
agency并通过其city_id,它获取相应的实体city并连接它使用agency.city = city。在完成整个机构 JSON 数组的迭代后,保存当前的托管对象上下文(实际上 -[managedObjectContext save:] 会执行多次,每次处理 500 条记录后)。在这一步,很明显,为 6000 个代理机构中的每一个的每个客户获取 4000 个城市中的一个对整个同步过程有巨大的性能影响。然后,最后处理客户端的JSON:和前2阶段一样,遍历整个10000个元素的JSON数组,逐个执行对应机构和ZOMG城市的获取,这会影响整体表现与之前的第 2 阶段相同。
一切都很糟糕。
我在这里看到的唯一性能优化是,第一阶段可以留下一个带有城市 ID(我的意思是 NSNumber 的真实 ID)和错误城市实体作为值的大型字典,因此可以防止丑陋 find 下一个阶段 2 的过程,然后使用类似的缓存技巧在阶段 3 上做同样的事情,但问题是在刚刚描述的所有 30 个表之间有更多的关系 [Client-City , Client-Agency, Agency-City ] 所以涉及缓存所有实体的最终过程最有可能影响 iPad 设备为我的应用程序保留的资源。
更新 4
致未来受访者的信息:我已尽力使此答案详细且格式正确,我真的希望您能以详细的答案来回答。如果您的回答能够真正解决此处讨论的问题的复杂性,并补充我为使我的问题尽可能清晰和笼统而做出的努力,那就太好了。谢谢。
更新 5
相关主题:Core Data on client (iOS) to cache data from a server Strategy、Trying to make a POST request with RestKit and map the response to Core Data。
更新 6
即使不再可能打开新的赏金并且有已接受的答案,我仍然很高兴看到包含有关本主题解决的问题的其他信息的任何其他答案。提前致谢。
【问题讨论】:
-
那么,您的问题主要是关于如何有效地建立关系?你的代码现在是如何做到的?有一些优化,但您已经在使用哪些优化?
-
请注意我说的是 fetch-part 的优化——“fetch-part(或 read-part)被高度优化”——实际上这意味着它只是一个常规的 CoreData 获取——如文档中所述,其中描述了此处描述的所有获取调整(预获取,仅获取需要的属性等),即没有什么神奇的,但我称它为“重”,因为如果你曾经见过初始状态我得到了这个我的遗留项目,你会想知道 CoreData 怎么会被滥用这么多!..
-
话虽如此,我将尝试更新我的问题,为它提供更多关于我试图通过这个问题实现的目标的指南。我希望它也能解决@Alex 的需求。给我一分钟)
-
请看我刚刚写的更新#3。我希望我已经很好地描述了我的项目的问题。
-
您的服务器上是否启用了 gzip/deflate 压缩?