使用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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值