Pymongo / MongoDB: 创建索引还是确保索引?

56 投票
6 回答
39124 浏览
提问于 2025-04-16 17:07

我不太明白在pymongo中,create_indexensure_index有什么区别。在MongoDB索引页面上,它提到你可以通过调用ensureIndex()来创建一个索引。

但是在pymongo中,有两个不同的命令:create_indexensure_index,而关于创建索引的文档中提到:

create_index()不同,后者会无条件尝试创建索引,ensure_index()则利用了驱动程序中的一些缓存,只会尝试创建那些可能还不存在的索引。当PyMongo创建(或确保)一个索引时,它会在ttl秒内“记住”这个索引。在这个时间限制内,重复调用ensure_index()会很轻量——它们不会实际尝试创建索引。

我理解得对吗?ensure_index会创建一个永久的索引吗?还是我需要使用create_index来做到这一点?

6 个回答

10

在交互式命令行中使用的ensureIndex方法和Python驱动中的ensure_index其实是两回事,虽然名字一样。Python驱动中的create_indexensure_index这两个方法都是用来永久创建索引的。

在某些情况下,可能会用ensure_index并设置一个合理的TTL(生存时间),因为我不确定每次调用create_index时是否会重新创建索引。重新创建通常是不希望的,因为这可能会消耗很多资源。不过,即使是ensure_index(无论是Python还是Ruby驱动)也可能在TTL过期时,或者从不同的客户端实例调用时,甚至在重启后重新创建索引。我对此并不确定。

也许更好的办法是先用index_information()方法检查一下索引是否已经存在。如果已经存在,就不需要再创建了。

接下来我将演示ensure_index(或ensureIndex)这个术语的两种不同含义:

1) 如果数据库中还不存在索引,就创建一个索引

这就是交互式命令行中的ensureIndex()方法的作用:

http://www.mongodb.org/display/DOCS/Indexes#Indexes-Basics

Node.JS的MongoDB驱动也是这样:

https://github.com/mongodb/node-mongodb-native/blob/master/lib/mongodb/collection.js

(在collection.js文件中搜索function ensureIndex。)

2) 如果索引不在“驱动缓存”中,就创建一个索引

在这里使用了相同的名称,但含义却不同,这让我有点困惑。

Python和Ruby驱动会在内存中存储最近创建的索引信息,这种行为被称为“缓存”。

它们不会把这个缓存的信息告诉数据库。

这个机制的结果是,如果你第一次调用create_indexensure_index并设置了TTL值(生存时间),那么驱动会在数据库中插入索引,并记住这个插入,同时在内存中存储TTL信息。这里缓存的是时间和索引的类型。

下次你在同一个驱动实例上调用ensure_index,如果自第一次调用以来TTL的秒数还没有过去,ensure_index命令只会再次插入索引。

而如果你调用create_index,无论自第一次调用以来过去了多少时间,索引都会被插入,当然如果这是第一次调用也是如此。

这是Python驱动,在collection.py文件中搜索def ensure_index

https://github.com/mongodb/mongo-python-driver/blob/master/pymongo/collection.py

而Ruby驱动,在collection.rb文件中搜索def ensure_index

https://github.com/mongodb/mongo-ruby-driver/blob/master/lib/mongo/collection.rb

(注意,不同的客户端实例之间并不知道彼此的缓存信息,这些信息只保存在内存中,并且是针对每个实例的。如果你重启客户端应用,新实例不会知道旧的“缓存”索引插入情况。其他客户端也不知道,它们之间不会相互告知。)

我还没有完全理解,当Python驱动或Ruby驱动插入一个已经存在的索引时,数据库会发生什么。我猜测在这种情况下它们不会做任何事情,这样更合理,也符合交互式命令行和JS驱动的行为。

41

@andreas-jung 说得对,ensure_index()create_index() 的一个封装。我觉得大家会困惑的地方在于这句话:

当 PyMongo 创建(或确保)一个索引时,它会在 ttl 秒内被“记住”。

这里并不是说这个索引是临时的或者“短暂”的,而是说在指定的秒数内,如果你再次调用 ensure_index() 来创建同样的索引,它不会产生任何效果,也不会调用底层的 create_index()。但是当这个“缓存”过期后,再次调用 ensure_index()再次调用 create_index()

我完全理解你的困惑,因为坦白说,PyMongo 的文档在解释这个过程时并不太清晰。不过,如果你去看看 Ruby 的文档,解释会清楚一些:

  • (String) ensure_index(spec, opts = {})

调用 create_index,并设置一个标志,表示在接下来的 X 分钟内不再执行这个操作。这个时间可以在初始化 Mongo::DB 对象时作为选项指定,选项为 [:cache_time]。任何对索引的更改都会被传播,无论缓存时间如何(例如,索引方向的改变)。

这个方法的参数和选项与 Collection#create_index 的参数和选项是一样的。

示例:

调用顺序:

时间 t: @posts.ensure_index([['subject', Mongo::ASCENDING]) -- 调用 create_index 并设置 5 分钟的缓存

时间 t+2min : @posts.ensure_index([['subject', Mongo::ASCENDING]) -- 不做任何事情

时间 t+3min : @posts.ensure_index([['something_else', Mongo::ASCENDING]) -- 调用 create_index 并设置 5 分钟的缓存

时间 t+10min : @posts.ensure_index([['subject', Mongo::ASCENDING]) -- 调用 create_index 并重置 5 分钟的计时器

我并不是说各个驱动的工作方式完全一样,只是为了说明,这里的解释在我看来稍微好一些。

23

请注意,在Mongo 3.x版本中,ensureIndex这个方法已经不推荐使用了,最好不要再用它。

从3.0.0版本开始,db.collection.ensureIndex()现在只是db.collection.createIndex()的一个别名,也就是说它的功能已经被替代了。

pymongo中也是一样的情况:

不推荐使用 - 确保这个集合上存在一个索引。

这意味着你应该始终使用create_index这个方法。

撰写回答