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

来源:互联网 发布:域名注册管理机构 编辑:程序博客网 时间:2024/05/20 05:24

一、问题定义:

由于业务需求,需要实现给一条记录分配一个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;@Servicepublic 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

原创粉丝点击