我正在开发一个用于iPad的企业销售应用程序,它使用Sqlite作为其内部数据库,最近出现了一种奇怪的行为.
我有一个巨大的表,其中充满了来自其他几个表(有点像"物化视图")的信息,这些表可以包含超过200万行,具体取决于用户的设置方式.当用户想要搜索某个项目时,该应用程序会对这个具有索引列的大型表以及用作过滤器和/或元数据的其他列执行查询.我将在下面发布查询和基本想法.无论如何,这个查询通常在iPad第4代上返回2~3秒,不超过这个,这很好.每次用户点击按钮以将其数据与我们的服务器同步时,都会删除,重新创建和填充此表.
但是,最近同一个查询在同一个表中(根本没有相关变化),随机开始需要40~50秒.如果你以后在相同的设备上使用相同的过滤器(甚至更改过滤器!)执行相同的操作,同一个表上的相同查询将再次花费2~3秒.我没有发现导致这种减速的任何特定情况,该应用程序是当时唯一运行的应用程序.设备不是问题,我们已经看到这种情况发生在至少5个不同的iPad上,一个是iPad 3,另一个是iPad第四代.
我认为这不是某种缓存,因为应用程序不会缓存任何内容,而且这些时间相当随机.有时它们连续10次需要40秒,然后突然再次开始只需2秒,而另一方面也是如此.对我来说唯一清楚的是,这种减速只发生在密集使用后(使用应用程序工作1-2天),所以我也遇到麻烦导致我在iPad上调试时出现这种情况.
我尝试过的:
巨大的表构造如下,并且没有任何外键或其他任何约束:
CREATE TABLE FMV_CATALOG(
UNIQUE_ID TEXT,
PRODUCT_ID INTEGER,
<一堆元数据/已过滤列 - 总共20列>
);
并且查找产品的查询是:
SELECT
PRODUCT_ID
,UNIQUE_ID
<所有其他必需列,~20列>
FROM
FMV_CATALOG
WHERE
UNIQUE_ID ='<some id> _ <other id>'
AND PRODUCT_NAME LIKE'%iPhone%'
<和其他可选的,很少使用的过滤器.>
我完全没有想法,所以任何帮助都将受到赞赏.
谢谢!
更新(更多信息):
我忘了提到的重要信息,Rob让我想起了它.我的数据库连接始终处于打开状态,仅在用户注销时才会关闭.当我们保持连接打开时,我们注意到应用程序的所有部分都有巨大的性能,因为我们有数百个小查询在其他情况下执行(但在浏览/搜索产品目录时却没有).
用于创建索引的查询如下:
在MV_CATALOG(UNIQUE_ID)上创建INDEX IDX_MV_CATALOG;
此外,即使该列名为UNIQUE_ID,它也不是唯一的.它应该是最初的,但现在重复了N次.我知道这是错的,我们会尽快改变.
通过连接两个其他表的ID来填充此"UNIQUE_ID"(这不是唯一的).这样,当用户搜索我们的目录时,我们的"物化视图"消除了至少三个连接的需要,这将查询时间从大约20秒提高到大约2秒.
我们不直接在查询上调用sqlite3 API,我们已经开发了一个包装类,我们已经使用它至少2年了.这是我们第一次遇到这种情况,但这是我们第一次处理这么多数据.
有几点想法:
您没有向我们展示在 上创建任何索引FMV_CATALOG。如果没有别的,如果UNIQUE_ID是,顾名思义,唯一的,那么我倾向于用以下方式定义表PRIMARY KEY:
CREATE TABLE FMV_CATALOG(
UNIQUE_ID TEXT PRIMARY KEY,
PRODUCT_ID INTEGER,
<bunch of metadata/filtered columns - total of 20 columns>
);
Run Code Online (Sandbox Code Playgroud)您应该尝试使用 SQLiteEXPLAIN QUERY PLAN命令来查看查询并查看其计划,并确保它正在利用您的索引。按原样执行此操作,然后再次使用PRIMARY KEY(如果仍然不行,则在WHERE子句中的字段上建立索引),并确保最终查询肯定使用您的索引。
我不知道为什么,如果你有唯一的 ID,为什么你还要查看其他字段。如果添加主键(以及可能的其他索引)不能解决问题,我可能会尝试根据唯一 id 检索记录,然后检查与代码中其他参数的一致性。我不认为你需要这样做,但这是最坏的情况。
至于为什么它会变慢,在没有看到代码的情况下很难猜测发生了什么(我确信这太复杂了,无法在一个简单的问题中分享)。我可以想象奇怪的行为,例如,如果您在执行sqlite3_finalize某个sqlite3_prepare_v2语句后失败,或者您意外地未能关闭数据库,然后在其他地方再次打开它。sqlite3我可以想象,如果调用顺序不完全正确,可能会出现性能问题。使用FMDB之类的工具可以最大限度地减少发生此类问题的可能性(并简化 SQLite 代码)。或者,如果这一步太过激进,请尝试编写自己的宏来调用 SQLite 调用,但也要记录您已调用该sqlite3函数的事实,并仔细检查该日志并仔细检查 SQLite 调用的顺序。
我唯一可以建议的是您是否可以构建一个可以重现异常行为的简化项目。追踪Heisenbug可能会令人恼火:除非您能够持续重现该错误,否则很难追踪。
| 归档时间: |
|
| 查看次数: |
314 次 |
| 最近记录: |