【发布时间】:2011-08-03 01:17:38
【问题描述】:
我正在用 C++/Qt 编写一个项目,它能够连接到 QtSQL (http://doc.qt.nokia.com/latest/qtsql.html) 支持的任何类型的 SQL 数据库。这包括本地服务器和外部服务器。
但是,当有问题的数据库是外部的时,查询速度开始成为问题(UI 缓慢,...)。 原因:存储在数据库中的每个对象都是延迟加载,因此每次需要属性时都会发出查询。屏幕上平均显示大约 20 个对象,每个对象显示大约 5 个属性。这意味着,对于我展示的每个屏幕,都会执行大约 100 个查询。查询在数据库服务器本身上执行得非常快,但是在网络上运行的实际查询的开销是相当大的(以秒为单位测量整个屏幕)。
我一直在考虑解决问题的几种方法,最重要的方法似乎是(根据我):
- 减少查询次数
- 加快查询速度
应对(一)
- 我可以找到某种方式来延迟属性的实际获取(启动 transaction),然后当程序员编写 endTransaction() 时,数据库会尝试一次获取所有内容(使用 SQL UNION 或循环......)。这可能需要对惰性对象的工作方式进行相当多的修改,但如果人们评论说这是一个不错的解决方案,我认为它可以优雅地解决。如果这个解决方案能够加快一切速度,那么甚至可能不需要精心设计的缓存方案,从而省去很多麻烦
- 我可以尝试预加载属性数据,方法是在一个查询中为所有请求的对象获取所有属性数据,从而有效地使它们非惰性。当然,在这种情况下,我将不得不担心过时的数据。我如何在不至少向外部数据库发送一个查询的情况下检测陈旧数据? (注意:发送一个查询来检查每个属性检查的陈旧数据将提供最佳情况下的 0x 性能提升和最坏情况下的 2x 性能下降,当数据实际上是陈旧的时)
应对(2)
例如,可以通过保持数据库的本地同步副本运行来加快查询速度。但是,我在客户端机器上并没有太多可能运行例如与服务器上完全相同的数据库类型。因此,本地副本例如是 SQLite 数据库。这也意味着我不能使用特定于 db-vendor 的解决方案。 我有哪些选择?在这种情况下,什么对人们有效?
担心
我主要担心的是:
- 过时数据:可以想象有很多查询会更改数据库,从而禁止执行对拥有过时数据的用户来说可能的操作。
- 可维护性:我可以在这个新层中松散地耦合到什么程度?如果它不必了解我的内部惰性对象系统以及每个对象和可能的查询的所有信息,这显然会更好
最后一个问题
将查询成本降至最低的好方法是什么?好的意思是某种组合:可维护,易于实现,不太特定于应用程序。如果归结为选择任何2,那就这样吧。我想听听人们谈论他们的经历以及他们为解决这个问题所做的工作。
如您所见,我已经想到了一些问题和处理方法,但我不知道什么才是明智的方法。由于它可能涉及大量工作和对程序中许多层的密集更改(希望尽可能少),因此我考虑在对此事做出最终决定之前询问这里的所有专家。也有可能我只是忽略了一个非常简单的解决方案,在这种情况下,将非常感谢指向它的指针!
假设所有相关的服务器端调整都已完成(例如:MySQL 缓存、最佳索引……)
*注意:我检查了有类似问题但不完全满足我的问题的用户的问题:例如Suggestion on a replication scheme for my use-case? 和Best practice for a local database cache?)
如果需要任何其他信息来提供答案,请告诉我,我会及时更新我的问题。对任何拼写/语法错误深表歉意,英语不是我的母语。
关于“懒惰”的注意事项
我的代码是什么样子的一个小例子(当然是简化的):
QList<MyObject> myObjects = database->getObjects(20, 40); // fetch and construct object 20 to 40 from the db
// ...some time later
// screen filling time!
foreach (const MyObject& o, myObjects) {
o->getInt("status", 0); // == db request
o->getString("comment", "no comment!"); // == db request
// about 3 more of these
}
【问题讨论】:
-
如果屏幕上的对象有些恒定,为什么不将所有属性映射到屏幕,然后执行“选择屏幕 x 所需的所有属性”。这样,您将获得 1 个带有“大”结果集的查询。之后,您唯一可以改进的就是查询本身。
-
我不确定我是否理解您对延迟加载的使用。您是否需要同时加载所有 20 个对象(每个对象有 5 个属性)?
-
@Maiku 是的,这是我想过的事情之一,我在“解决 (1)”的第二个项目中设想了类似的东西,它可能是“更简单”的解决方案之一,但是实施起来仍然很丑陋。目前这是我最喜欢的方式之一,但我正在等待更多建议。
-
@TimMeyer 是的,应用程序的主视图同时加载它们。发生的情况是:主视图请求 20 个对象,数据库在一次查询中从数据库中获取核心属性,应用程序构造 20 个对象,将它们交给主视图。但是,主视图将取决于某些设置,要求比核心属性更多,这些非核心属性(一种元数据)中的每一个都必须从数据库中单独请求。这就是缓慢的来源。请注意,主视图不是唯一可能的视图,有时需要所有对象
-
如果您需要同时加载这些对象,您应该对每种类型进行一次查询,例如一个用于对象,一个用于属性(如果确实需要,可能不止一个)。如果这 100 个查询将在您的新的两个(或更多)查询被执行的同时被执行,那么在陈旧数据方面应该没有区别,但在执行时间方面应该有很大的区别。如果你有通用的延迟加载算法,实现这一点可能会很困难,但值得付出努力。
标签: c++ sql qt lazy-evaluation database-caching