使用mongodb的findAndModify命令来进行数据同步

一、问题定义:

由于业务需求,需要实现给一条记录分配一个int值的不重复id,由于是多实例部署的服务,所以如何进行数据同步,避免插入重复id成为关键。


二、解决过程:

1.一开始想到的是,当系统初始化的时候,读取mongo库,找到当前最大的id值,加载到内存,然后多线程之间通过AtomicInteger
进行调用,获取下一个要使用的id值。这样,虽然单实例可以很好的工作,并发也没有问题。但是多实例却无法工作,因为实例与实例之间无法进行协调分配id。

2.接着又想借助与让每个实例都有一个前缀来标识,然后再进行分配id,但是这样,服务重启等问题需要额外一些工作去保证数据一致性。

3.可以把整个业务表加锁,每次插入让其id自增,但是会很影响性能。


三、最终方案:

最终发现了mongo db中有一个原子操作函数findAndModify,它可以指定将获取某个键并同时进行增长一定的值。完全符合我们的场景需求,所以新建一个表,用来记录当前的id值,然后即使多个实例之间想要获取id值,只要调用此函数即可,便可安全获得自增的唯一id值。保证多个服务之间通过原子操作这个表进行了id的同步,避免了id值的重复分配。

使用方式如下,我用的是spring boot,先定义一个mongo表的映射实体:

import lombok.Data;
import org.springframework.data.mongodb.core.mapping.Document;

@Data
@Document(collection = "sequence")
public class SequenceId {

    private String collName;

    private Integer qid;
}


然后调用findAndModify原子操作,获取新的id值,用于分配:

package com.baidu.aibot.backoffice.console.faq.dao;

import com.baidu.aibot.backoffice.console.faq.vo.SequenceId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.FindAndModifyOptions;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class QidMongoDao {

    private static Logger logger = LoggerFactory.getLogger(QidMongoDao.class);

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 获取下一段自增ID
     *
     * @param collName 集合名称,要同步的ID是用在哪里的
     * @param num 增长num大小
     * @return 最后的id值
     */
    private SequenceId getNextId(String collName, int num) {
        Query query = new Query(Criteria.where("collName").is(collName));
        Update update = new Update();
        update.inc("qid", num);
        FindAndModifyOptions options = new FindAndModifyOptions();
        // 先查询,如果没有符合条件的,会执行插入,插入的值是查询值 + 更新值
        options.upsert(true);
        // 返回当前最新值
        options.returnNew(true);
        SequenceId seq = mongoTemplate.findAndModify(query, update, options, SequenceId.class);
        return seq;
    }
}

 当前mongo db已经实现了文档级别的锁,性能得到了较大提高,如下是不同版本的mongo所使用的锁的级别:

Version < 2.2 : 只支持进程级锁,一个Mongod实例一个锁。
2.8 >Version >= 2.2 : 支持库级锁,一个db一把锁。
大于3.0.0 支持文档级别的锁。


四、参考资料

http://www.leftso.com/blog/268.html

http://blog.csdn.net/qq_16313365/article/details/72781469

猜你喜欢

转载自blog.csdn.net/j754379117/article/details/78606914
今日推荐