因为本人专业方向不是数据库,只是在实验搭建时会用到一点,所以探讨的难免有些肤浅,只是记录一些遇到的问题和解决方法供大家讨论。之前写过一篇VS2010下配置C++ driver的文章,也许是我操作新版本的方法不对,至少到目前我仍旧在用着旧版本。自从opencv对java支持以后,为了开发效率,希望把部分程序用java改写,而且在java中配置MongoDB十分简单。此外,在java和C++的driver中,游标的使用也有细微差别,在这里也会做一点讨论。
先从游标说起吧,在数据库中想要以任何条件访问数据都会用到游标。我的数据大概有35万左右,在java中会用一个“大大”的游标将找到它们并一一进行处理。但是在C++中如此做却出现了问题,如果这个cursor过大,每次都会以异常退出。反复排查之后发现是游标已经死掉了,即使你反复的调用more和next函数。在官网中说10分钟没响应游标会死掉,在C++中我没有计时,但至少这两个函数是救不活游标的。在官网里看到可以将游标设置为immortal,但是在这两个驱动里没有这个函数。实在不知道为什么会有如此差别,感觉底层实现应该类似的。如果游标死掉后,如何继续上一次的结尾继续操作呢?官网的答案是tailable cursor。顾名思义就是下一个游标从上一次结尾位置开始,可这种游标也是有要求的,必须是capped collection。这种将集合封装是以空间大小为单位的,一旦封死,空间不够时新进来的数据会挤掉最早的那些来充满集合。显然这并不实用。我的曲线救国方法是,用小游标频繁取用的方式。为了保证不重复取用,需要我们加入一个控制键,比如“STATUS”,根据状态键值一般只有两或三种。如果没有索引,每次找到我们需要的数据就需要比对之前所有处理过的,这种方式会效率极低。为了快速查找,只好对这个键进行索引。在“实战MongoDB”一书中,不建议对这种低选择性的键进行索引。对目前这种问题,我也在找寻更好的解决办法。(其实应该让10 gen改进c++ driver)
继续说说MongoDB中的索引使用。在刚才提到的问题里,逐个处理文件,并对数据库update。对数据库指定文件更新时,需要一个唯一性的键值来查找该文件。起初没有对这个键进行索引,所以要30多万个文件一个个的去寻找。写到这里,不难发现我们需要两个索引了,一个控制键索引用来查找要更改的文件和一个唯一键索引用来更改文件。起初我用了一个文件地址键,因为程序中会读出使用。但后来发现在MongoDB中,即使你没有建立任何索引,也会有个文件id索引,基于“_id”键。补充两句,这个键是所有文件会被自动加入的保证文件唯一性,当中包含时间戳,当然也可以自己指定。在我的实验里,需要彻底shuffle这个数据库,所以我有一个随机数的键并用cursor.sort来排序先后。如果使用对这个键排序并且读入所有符合条件数据时,数据库会要求对这个排序键必须进行索引。在别人的博客中看到这个排序键的索引使用降序排列(-1)效率会高,也许和底层实现有关,没有亲自比较。写到这里会发现一共使用了三个索引键了,最好的办法就是使用组合组合索引。但是排序索引必须在第一位,否则MongoDB是找不到这个索引并报错的。大概使用就是:(排序:-1, 控制:1 , _id:1)。
数据库的存放安全和索引查询效率是大家主要关心的。书中还谈论了很多实际应用的注意事项,和慢索引的优化。但是目前我没有使用到,了解不多不敢瞎写,以后有机会再补充。