建立了一个测试集合one,这个集合有1亿条文档,文档数据有6.3G左右,一共建立了三个索引,如上图,三个索引分别都在1G的大小,三个索引一共有3.2G大小。
第一次对这个集合执行查询时,查询条件没有使用索引字段,进行1亿条的全表扫描,可以从内存占用量中看到,内存的占用量在不断的飙升,上升了6G左右的占用量。
结束掉这个Mongo进程后重启,以索引字段进行查询,瞬间把目标文档找出来了,但是并没有看到内存的占用量有什么变化(有点变化也就在启动mongodb进程时候内存上升了100M左右的占用量),但是三个索引中任何一个索引都是1G的大小,mongodb到底把索引数据加载到了内存中没有?
mongodb到底是怎么使用索引数据的?它如果把它加载到内存中,为什么内存占用量基本没变化?对于上图中三个索引,如果这唯一一次查询仅仅只用到了c字段的索引查询,mongodb是只加载c字段这一个索引的数据1.1G,还是把三个索引的数据3.2G都直接全部加载进来?
其实这大部分是一个操作系统原理的问题。操作系统在读取文件时会把文件内容放到空闲内存中,这样下次再有程序尝试读取同样的文件内容的时候,就可以直接从内存中给而不用读磁盘,从而大幅度提高读取速度。这个缓存就是文件系统缓存。
其实很容易理解:这些内存如果没有人用,空着也是浪费,为什么不缓存一点东西在里面呢?不管缓存什么,只要命中一次就赚到一次。至于怎么怎么赚更多,那就要看你怎么选择在有限的内存空间中缓存的什么内容,怎么能让缓存的内容被更多地命中。这部分内容跟问题无关,不细说了,有兴趣可以看看操作系统原理。
回到你的问题,当你重启了MongoDB实例时,MongoDB占用的内存当然已经都释放掉了。但是无论是数据还是索引,其实都还缓存在文件系统缓存中,因为它们都来自于数据文件和索引文件(前提是没有别人要使用这些内存)。索引的使用是按需加载,这点基本上从逻辑推理就可以猜出来:假设你的10GB的索引,难道第一次读取的时候就要等10GB索引加载到内存中?假如索引容量比内存要大呢?所以一次性加载全部索引显然是不合理的。即使是一个索引,也是按需部分加载而不是全部。所以你需要用到的只是这1GB中的很小一部分。记住索引的时间复杂度是log2(n),要从1亿数据中找出需要的一条,最坏的情况下只需要查询27次比较,当然是瞬间就出来了。