5

我正在使用对象数据库 (ZODB) 来存储许多对象之间的复杂关系,但遇到了性能问题。因此,我开始构建索引以加快对象检索和插入。这是我的故事,希望对你有所帮助。

最初,当我将一个对象添加到数据库中时,我会将其插入专用于该对象类型的分支中。为了防止多个对象代表同一个实体,我添加了一个方法,该方法将遍历分支中的现有对象以查找重复项。这起初是可行的,但随着数据库规模的增长,将每个对象加载到内存中并检查属性所花费的时间呈指数增长,令人无法接受。

为了解决这个问题,我开始根据对象中的属性创建索引,以便在添加对象时将其保存在类型分支以及属性值索引分支中。例如,假设我正在保存一个具有属性 firstName = 'John' 和 lastName = 'Smith' 的人员对象,该对象将被附加到人员对象类型分支,并且还将被附加到属性索引分支内的列表中,带有键'约翰”和“史密斯”。

这节省了大量重复检查的时间,因为可以分析新对象并且只需要检查与属性索引相交的对象集。

但是,我很快遇到了另一个关于更新对象时的处理问题。索引需要更新以反映它们可能不再准确的事实。这需要记住旧值以便可以直接访问它们并删除对象,或者迭代属性类型的所有值以便找到然后删除对象。无论哪种方式,性能都很快开始再次下降,我无法找到解决它的方法。

你以前有过这样的问题吗?你做了什么解决它,或者这只是我在使用 OODBMS 时必须处理的事情?

提前感谢您的帮助。

4

2 回答 2

8

是的,repoze.catalog 很好,并且有据可查。

简而言之:不要让索引成为您网站结构的一部分!

  1. 查看使用容器/项目层次结构来存储和遍历内容项目对象;计划能够通过(a)路径(图形边缘看起来像文件系统)或(b)通过识别某个不同位置的单例容器来遍历内容。

  2. 使用 RFC 4122 UUID(uuid.UUID 类型)或 64 位整数识别您的内容。

  3. 使用中央目录进行索引(例如 repoze.catalog);目录应位于相对于 ZODB 的根应用程序对象的已知位置。并且您的目录可能会索引对象的属性并在查询时返回记录 ID(通常是整数)。您的工作是将这些整数 id 映射到(可能通过 UUID 间接)到您存储内容的数据库中的某个物理遍历路径。如果您使用 zope.location 和 zope.container 作为通用接口,以便从根/应用程序向下遍历对象图,它会有所帮助。

  4. 使用 zope.lifecycleevent 处理程序来索引内容并保持新鲜。

问题——泛化

ZODB 太灵活了:它只是一个带有事务的持久对象图,但这为您在自己的数据结构和接口中下沉或游泳留下了空间。

解决方案——泛化

通常,只需从 ZODB 周围的社区中挑选预先存在的习语就可以了:zope.lifecycleevent 处理程序,使用 zope.container 和 zope.location 的“容器式”遍历,以及类似 repoze.catalog 的东西。

更具体

只有当你用尽了通用的习语并知道它们为什么不起作用时,才尝试使用 ZODB 中各种风格的 BTrees 来构建自己的索引。实际上,我这样做比我愿意承认的要多,但通常有充分的理由。

在所有情况下,保持索引(搜索、发现)和站点(遍历和存储)结构不同。

问题领域的成语

  • 掌握 ZODB BTrees:您可能想要:

    • 将内容对象作为 Persistent 的子类存储在容器中,这些容器是提供容器接口的 OOBTree 的子类(见下文)。
    • 为您的目录或全局索引存储 BTree,或使用 repoze.catalog 和 zope.index 之类的包来抽象该细节(提示:目录解决方案通常将索引存储为 OIBTree,这将为搜索结果生成整数记录 id;然后您通常有一些一种文档映射实用程序,可将这些记录 ID 转换为应用程序中可解析的内容,例如 uuid(前提是您可以将图形遍历到 UUID)或路径(Zope2 目录的方式)。
  • 恕我直言,不要费心使用 intids 和 key-references 之类的东西(如果你不需要它们,这些就不那么惯用了,而且更难)。只需使用 repoze.catalog 中的 Catalog 和 DocumentMap 以整数到 uuid 或路径形式获取结果,然后弄清楚如何获取您的对象。请注意,您可能需要一些实用程序/单例,它可以在给定从搜索返回的 id 或 uuid 的情况下检索您的对象。

  • 使用 zope.lifecycleevent 或提供同步事件回调(处理程序)注册的类似包。每当对您的对象进行原子编辑(可能每个事务一次,但不在事务机制中)时,您应该调用这些处理程序。

  • 学习 Zope 组件架构;不是绝对要求,但肯定有帮助,即使只是为了了解上游包的 zope.interface 接口,如 zope.container

  • 了解 Zope2 (ZCatalog) 是如何做到这一点的:一个目录前端用于多个索引或各种排序,每个搜索一个查询,每个都有专门的数据结构,每个返回整数记录 id 序列。这些通过执行集合交集的目录跨索引合并,并作为包含元数据存根的“大脑”对象的延迟映射返回(每个大脑都有一个 getObject() 方法来获取实际的内容对象)。从目录搜索中获取实际对象依赖于 Zope2 习惯用法,即使用来自根应用程序对象的路径来识别编目项目的位置。

于 2011-07-13T05:23:47.187 回答
0

考虑使用属性散列(类似于 Java 的 hashCode()),然后使用 32 位散列值作为键。Python 有一个哈希函数,但我对它并不熟悉。

于 2011-07-12T18:05:46.787 回答