首页 » mongo

我们经常碰到按某个key查询,然后sort的情况。尤其是热点数据(即经常limit 前 n条的数据),优化方案是,按{"sortKey" : 1, "queryCriteria" : 1} 建立索引。

1.有人可能疑惑,为什么,不按查询的queryCriteria排在前面呢,因为我们一般要先定位到查询的数据。这个是因为一个索引首先是一棵树。那么我们按sortkey在前的话,那么热点数据会集中在树的右边。又称--右平衡。我们limit的时候,几乎只遍历右边部分。虽然比queryCriteria 前面多便利了一部分记录,但是总体时间是比queryCriteria放前面快的。因为你queryCriteria找到以后,还要整个排序。相对很慢。
2.比如我们查询某篇文章的评论----按日期排序。那么我们一般只会获取前几页的评论。所以我们的符合索引如下:

db.comments.ensureIndex({"time" : 1, "articleId" : 1})
//查询时可以显式指定索引
db.comments.find({"articleId" : 3}).
... sort({"time" : -1}).
... hint({"time" : 1, "articleId" : 1}).

3.注意mongodb中,你point query 点查询---索引1和-1没有区别的,mongo可以从索引命中后的开头遍历,也可以从结尾遍历。但是 多值查询(multivalue) 如

db.users.find({"age" : {"$gte" : 21, "$lte" : 30}}).
... sort({"username" : 1}).

因为索引命中age比较多,那么排序索引的顺序就很重要了,一般建议直接按经常排序的方向建立索引。如果两个方向都有索引,就建立两个索引---不同的方向,如果我们这样建立索引

db.users.ensureIndex({"age" : 1, "username" : -1})

那么索引的排序如下:
mongo索引实际排序

最近需要同步mongodb数据到远程的mongodb实例,但是又没法组成replicaset,因为mongo要求replicaset必须互相能连通,就是互相都有识别对方的监听端口。而远程的monggodb实例无法链接---我本地的mongodb实例,因为我是局域网,没有公网ip

1.解决办法就是用mongodb connector,下载安装,mongo connector

sudo pacman -Sy
sudo pacman -S python 
sudo pacman -S python-pip
pip install mongo-connector

3.更改本地mongo为replSet模式,这里的members就一个,因为我本地就开了一个实例,目的主要是开启副本模式

config = {
"_id" : "mmm",
"members" : [
    {"_id" : 0, "host" : "127.0.0.1:27018"}
    ]
}

rs.initiate(config)

2.然后启动mongo connector就行了, xxx.xxx.xxx.xxx是我要同步数据到这个ip,所以我隐藏了

 mongo-connector -m localhost:27017 -t xxx.xxx.xxx.xxx:27017 -d mongo_doc_manager

3.惊喜的是,居然把我以前的数据全部同步过去了

No server chosen by WritableServerSelector from cluster description ClusterDescription,mongodb
过一段时间还会报错:Timed out after 30000 ms while waiting for a server that matches WritableServerSelector,这个原因是MOngodb只有一个实例,没有满足可以写的条件(mongo会根据大多数选票来决定master副本,master副本才可以写,大多数的条件是,至少3个副本才能进行大多数选举,生产上一般是5个),解决办法就是创建一个选举仲裁者Arbiters

1.运行下面的脚本,在master实例,我们就一个。

rs.addArb("localhost:27017")

spring 自带的mongoRepository ,只能replace更新,即全量更新,我们可以自定义一个repository 方法。来做部分更新

1.解决思路就是生成一个map,对map中不为空的key生成update查询

Map<String, Object> objectMap = user.toMap();
    objectMap.values().removeIf(Objects::isNull);
    Update update = new Update();
    objectMap.forEach(update::set);

    return mongoOperations.findAndModify(
            Query.query(Criteria.where("_id").is(user.getId())), update, User.class);

2.而转换成map,可以用spring自带的jackson

ObjectMapper m = new ObjectMapper();
Map<String,Object> props = m.convertValue(myBean, Map.class);

3.需要注意的,对于基本类型的字段,有默认值的,所以推荐使用Object类型,如别用int 用Integer。null不占用mongodb的空间,即不会存储。所以从这方面也是好的。全部代码如下,注意笔者用的Reative版:

 public Mono<UpdateResult> updatePartial(MySeries mySeries) {
        if (null == mySeries || null == mySeries.getSrcId()) {
            return null;
        }
        ObjectMapper m = new ObjectMapper();
        Map<String, Object> objectMap = m.convertValue(mySeries, Map.class);
        objectMap.values().removeIf(o -> {
            if (null == o) {
                return true;
            }
            var cls = o.getClass();
            if (cls == ArrayList.class && ((ArrayList) o).size() <= 0) {
                return true;
            }
            if (cls == String.class && StringUtils.isEmpty(o)) {
                return true;
            }
      
            return false;
        });
        Update update = new Update();
        objectMap.forEach(update::set);
        Query q = new Query();
        q.addCriteria(Criteria.where("srcId").is(mySeries.getSrcId()));
        return template.updateFirst(q, update, MySeries.class);

    }