Pymongo / MongoDB: 创建索引还是确保索引?
我不太明白在pymongo中,create_index
和ensure_index
有什么区别。在MongoDB索引页面上,它提到你可以通过调用ensureIndex()
来创建一个索引。
但是在pymongo中,有两个不同的命令:
create_index
和ensure_index
,而关于创建索引的文档中提到:
与
create_index()
不同,后者会无条件尝试创建索引,ensure_index()
则利用了驱动程序中的一些缓存,只会尝试创建那些可能还不存在的索引。当PyMongo创建(或确保)一个索引时,它会在ttl秒内“记住”这个索引。在这个时间限制内,重复调用ensure_index()
会很轻量——它们不会实际尝试创建索引。
我理解得对吗?ensure_index
会创建一个永久的索引吗?还是我需要使用create_index
来做到这一点?
6 个回答
在交互式命令行中使用的ensureIndex
方法和Python驱动中的ensure_index
其实是两回事,虽然名字一样。Python驱动中的create_index
和ensure_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_index
或ensure_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驱动的行为。
@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 分钟的计时器
我并不是说各个驱动的工作方式完全一样,只是为了说明,这里的解释在我看来稍微好一些。
请注意,在Mongo 3.x版本中,ensureIndex这个方法已经不推荐使用了,最好不要再用它。
从3.0.0版本开始,db.collection.ensureIndex()现在只是db.collection.createIndex()的一个别名,也就是说它的功能已经被替代了。
在pymongo中也是一样的情况:
不推荐使用 - 确保这个集合上存在一个索引。
这意味着你应该始终使用create_index
这个方法。