我正在使用对象数据库(ZODB)来存储许多对象之间的复杂关系,但遇到了性能问题。因此,我开始构建索引以加快对象检索和插入速度。这是我的故事,希望对您有所帮助。
最初,当我向数据库添加对象时,我会将其插入专用于该对象类型的分支中。为了防止多个对象代表同一实体,我添加了一个方法,该方法将迭代分支中的现有对象以查找重复项。这种方法一开始是有效的,但随着数据库规模的增长,将每个对象加载到内存中并检查属性所需的时间呈指数级增长,令人无法接受。
为了解决这个问题,我开始根据对象中的属性创建索引,以便在添加对象时将其保存在类型分支以及属性值索引分支中。例如,假设我正在保存一个具有属性firstName =“John”和lastName=“Smith”的人员对象,该对象将被附加到人员对象类型分支,并且还将被附加到带有键“的属性索引分支内的列表”约翰”和“史密斯”。
这通过重复检查节省了大量时间,因为可以分析新对象,并且只需要检查在属性索引内相交的对象集。
然而,我很快就遇到了另一个关于更新对象时处理的问题。这些索引需要更新以反映它们可能不再准确的事实。这需要记住旧值以便可以直接访问它们并删除对象,或者迭代属性类型的所有值以便找到然后删除对象。不管怎样,性能很快又开始下降,我找不到解决它的方法。
您以前遇到过此类问题吗?你是怎么解决这个问题的,或者这只是我在使用 OODBMS 时必须处理的问题?
预先感谢您的帮助。
是的,repoze.catalog 很好,并且有详细的文档。
简而言之:不要将索引作为网站结构的一部分!
考虑使用容器/项目层次结构来存储和遍历内容项目对象;计划能够通过(a)路径(图形边缘看起来像文件系统)或(b)通过识别某个不同位置的单例容器来遍历内容。
使用 RFC 4122 UUID(uuid.UUID 类型)或 64 位整数标识您的内容。
使用中央目录进行索引(例如repoze.catalog);该目录应该位于相对于 ZODB 的根应用程序对象的已知位置。您的目录可能会索引对象的属性并在查询时返回记录 ID(通常是整数)。您的工作是将这些整数 ID 映射到(可能通过 UUID 间接)到您存储内容的数据库中的某个物理遍历路径。如果您使用 zope.location 和 zope.container 作为通用接口来从根/应用程序向下遍历对象图,这会有所帮助。
使用 zope.lifecycleevent 处理程序来索引内容并保持内容新鲜。
问题——普遍化
ZODB 太灵活了:它只是一个带有事务的持久对象图,但这为您在自己的数据结构和接口中沉浮或游泳留下了空间。
解决方案——通用化
通常,只需从 ZODB 社区中选择预先存在的习惯用法即可:zope.lifecycleevent 处理程序、使用 zope.container 和 zope.location 进行“容器化”遍历,以及诸如 repoze.catalog 之类的东西。
更具体
只有当您用尽了通用的习惯用法并知道它们为什么不起作用时,才尝试使用 ZODB 中各种风格的 BTree 来构建您自己的索引。事实上,我这样做的次数超出了我愿意承认的范围,但通常都有充分的理由。
在所有情况下,请保持索引(搜索、发现)和站点(遍历和存储)结构不同。
问题域的习语
-
掌握 ZODB BTree:您可能想要:
- 将内容对象作为持久性的子类存储在容器中,这些容器是提供容器接口的 OOBTree 的子类(见下文)。
- 要为目录或全局索引存储 BTree,或者使用像 repoze.catalog 和 zope.index 这样的包来提取该详细信息(提示:目录解决方案通常将索引存储为 OIBTree,这将为搜索结果生成整数记录 id;然后您通常会得到一些一种文档映射器实用程序,它将这些记录 id 转换为应用程序中可解析的内容,例如 uuid(假设您可以遍历图形到 UUID)或路径(Zope2 目录的方式)。
恕我直言,不要费心使用 intids 和关键引用等(如果您不需要它们,这些不太惯用并且更困难)。只需使用 repoze.catalog 中的 Catalog 和 DocumentMap 即可获取整数到 uuid 或路径形式的结果,然后弄清楚如何获取对象。请注意,您可能需要一些实用程序/单例来根据搜索返回的 id 或 uuid 来检索对象。
使用 zope.lifecycleevent 或提供同步事件回调(处理程序)注册的类似包。每当对对象进行原子编辑时(可能每个事务一次,但不在事务机制中),您应该调用这些处理程序。
学习 Zope 组件架构;不是绝对要求,但肯定有帮助,即使只是了解 zope.container 等上游包的 zope.interface 接口
了解 Zope2 (ZCatalog) 是如何做到这一点的:目录前面有多个索引或各种排序,每个索引都搜索一个查询,每个索引都有专门的数据结构,每个索引都返回整数记录 id 序列。这些通过执行集合交集的目录在索引之间进行合并,并作为包含元数据存根的“大脑”对象的惰性映射返回(每个大脑都有一个 getObject() 方法来获取实际的内容对象)。从目录搜索中获取实际对象依赖于 Zope2 习惯用法,即使用根应用程序对象的路径来标识编目项目的位置。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)