【杂谈】通过MongoDB建立数据库索引

最近和老师做项目,用到了两个集合:空气质量pm和气象数据meteo,pm包含1500万条数据,meteo包含300万条数据。

目的是要找出这其中时间“time”属性能够相对应的所有数据。

例如:pm和meteo都有2013-12-01 12:00:00这一时刻的数据,其中pm有920条这一时刻的空气质量数据(因为有很多不同的空气质量监测站点),meteo有350条这一时刻的气象数据(因为有很多不同的气象站),那就把这些数据保留或者存入各自的新集合。

如果在2013-12-02 12:00:00这一时刻,pm有这一时刻的空气质量数据,但是meteo中没有这一天的气象数据,那么pm中这一时刻的数据就没有什么用了。因为我们要求:针对同一时刻,两边的数据缺一不可。

然后开始使用mongodb操作,想法比较简单:

(1) 设置时间格式,方便后面把字符串String转换成Date对象。

//设置时间格式和时区
SimpleDateFormat df = new SimpleDateFormat(("yyyy-MM-dd HH:mm:ss"));

df.setCalendar(new GregorianCalendar(new SimpleTimeZone(0, "GMT")));

Tips: MongoDB在把时间Date对象存入数据库的时候,会自动存储为GMT标准时间,它默认你的原始时间数据是你的当地时间(不好意思我不知道它是根据什么识别的,根据服务器地址或是什么),反正就是会自动识别到你当地时区,然后转化成GMT时间。比如我们都在中国:你的原始.txt文件里的时间是2013-12-01 10:00:00,当你直接使用java存入mongodb数据库的时候,它会自动减去8个小时,变成2013-12-01 02:00:00。

但是我的原始数据本来就是以GMT时间给我的,并且我也希望以GMT标准时间存储它们,不是北京时。所以如果再把GMT给我减去8个小时,我的数据就不知道变成什么时间了。所以一定要设置好TimeZone哦。

(2)设置开始和结束的时刻startDate和endDate:

Date startDate = df.parse("2013-12-01 00:00:00");
Date endDate = df.parse("2015-12-31 23:00:00");

Tips: MongoDB中的时间最好使用Date()对象存储,我们的原始数据处理的时候,时间也是使用JavaScript的Date()对象存储到mongodb的。另外,我这里命名虽然是xxxDate,其实时间是精确到时刻的哦。

(3)设置循环变量eachDate:

Calendar eachDate = Calendar.getInstance();
eachDate.setTime(startDate);

这里使用到了Calendar类,因为Calendar类提供了自动增加年份、月份、天数、小时的add(int filed, int amount)方法。

比如2013-12-31使用Calendar的add()方法加一天,自动会变成2014-01-01,不会变成2013-12-32这样。加一小时同理,在2013-12-31 23:00:00的基础上加一小时,会变成2014-01-01 00:00:00,很方便。

(4)开始日期的循环:

使用while,当startDate一直在endDate的前面,就一直去做详细的查找匹配(详细操作这里不讨论,在下面讨论)。

while(eachDate.getTime().before(endDate)){
    eachDate.add(Calendar.HOUR, 1); //按小时增减循环
    Date date = eachDate.getTime();

    //详细的匹配操作下面详细讨论,上面两句只是写明如何进行时间循环
    //相信如果你清楚我的问题是什么,自己也会有自己的想法,希望一起讨论,小弟还是个新手呢。
}

Tips: eachDate是Calendar对象,所以要调用其getTime()方法,返回一个Date对象,再使用Date对象的before()或者after()方法判断循环。

(5)重要,开始讨论while里面的语句吧。

我最起初的想法很简单,设置好了eachDate的循环,直接使用eachDate去分别在pm和meteo两个表查询符合这个时间的所有文档。

/*设置mongodb的游标cursor,必须*/
BasicDBObject queryObject = new BasicDBObject();
queryObject.put("time", date);
DBCursor queryCursorMeteo = dbMeteoCollection.find(queryObject);
DBCursor queryCursorPM = dbPMCollection.find(queryObject);

/*若都包含此天,则把对应的文档分别插入各自新表中*/
if ((queryCursorMeteo.hasNext()) && (queryCursorPM.hasNext())) {    //判断是否都有这一天,如有则继续下面的
    while (queryCursorMeteo.hasNext()) {
        dbMeteoSameTimeCollection.insert(queryCursorMeteo.next());      //存入meteo库的meteo_data_same_time集合
    }

    while (queryCursorPM.hasNext()) {
        dbPMSameTimeCollection.insert(queryCursorPM.next());            //存入pmdata库的pm_data_same_time集合
    }

}

想法单纯入如我,直接根据eachDate去两个集合中查,如果均查到了(也就是上面的if判断),则用while把所查到的所有文档存入新的集合中去。没有做任何的处理,没有索引,没有排序,没有批量插入,没有,什么都没有,宝宝心累了所以什么都没有。

这样简单的想法的代价就是:时间开销巨大!!面对1500w和300w的两个集合,这个代码跑了我电脑14个小时。宝宝真是蠢毙了。

(6)好了,宝宝要开始优化代码了。

两个想法:

1,在(5)的基础上,增加对于时间的索引,索引的代价是在原、集合中插入会多用时间,但是我不在原集合中插入,而是插入到新集合中。所以我对原集合的处理只是查询,而索引可以大大大大的节省时间;然后插入到新集合的时候不使用while一条一条插入,而是使用批量插入。

2,对原始两个集合进行按照时间排序,设置两个变量indexPm和i分别指向pm和meteo,外层循环使用数据量较小的meteo的指针i。比较pm的时间和meteo的时间是否相等,如果相等,pm指针后移一次。伪代码如下:

//先对两组数据排序,可以用mongodb的sort命令
int indexPm = 0;

for(int i = 0; i < dateLength; i ++){
  while( date at indexPm of collection pm == date at i of collection climate){
    indexPm ++; //就是 pm的数据的cursor向前一个 while(date at indexPm of collection pm != date at i+1 of collection climate){
    delete current data of pm;
    indexPm ++;
  }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值