高并发下解决mongodb数据重复问题

时间:2021-6-4 作者:qvyue

需求背景:实现一个钱包系统的充值和提现功能,需要确保同一商户下交易单号唯一,以防止同一时间重复充值问题。

前提说明:使用python加上mongotea,通过orm的方式操作数据库

model介绍

from mongotea.models.base import BaseModel

class SettlementTrade(BaseModel, StateFieldOperation):
    """结算单
    """
    __collection__ = 'trade'
    logger = logging.getLogger(__name__)
    indexes = [
        {
            'fields': [('trade_no', -1), ('merchant_id', -1)],
            'unique': True,
        },
    ]
    structure = {
        # ID
        '_id': ObjectId,
        
        # 商户ID
        'merchant_id': ObjectId,
        
        # 用户ID
        'account_id': ObjectId,

        # 交易金额(分)
        'money': int,
        
        # 状态(1:待交易, 50:交易中, 100:交易完成, -100:交易失败, -101:交易关闭)
        'state': int,
        
        # 单据类型(10:充值单, 30:提现单)
        'trade_type': int,
        
        # 交易单号
        'trade_no': str,
    }

开始实现方案:biz编写查询条件来判断交易单号是否重复,代码如下

spec = {
    'trade_no': trade_no,
    'merchant_id': merchant_id,
}
record = db.SettlementTrade.find_one(spec)
if record:
    logger.info(u'>>>>> Trade no already exist.'.format(trade_no))
    raise BizRuntimeError(zh_message=u'交易单号重复,请稍后重试!')

但是在高并发的情况中是拦不住订单重复的问题,在数据表中还没有某个交易单号数据时,两个交易单号相同的写入请求同时进来了,他们同时做了判断,都得到当前字段不存在的结果,此时两个连接对象都将会做插入的操作,这样导致数据表中同时存在两个交易单号相同的订单。针对以上场景我们可以巧妙借用mongodb的唯一索引来解决并发问题,具体修改方案如下:
1、首先给collection创建组合唯一索引

db.trade.ensureIndex({"trade_no":-1, "merchant_id": -1},{"unique":true})

2、代码捕获DuplicateKeyError异常反馈给api调用者

try:
    doc = {
        'merchant_id': merchant_id,
        'money': money,
        'trade_no': trade_no,
        'trade_type': RECHARGE
    }
    record = db.Trade()
    record.update(doc)
    record.save()
except DuplicateKeyError as e:
     logger.exception(e)
    raise BizRuntimeError(zh_message=u'交易单号重复,请稍后重试!')
return record
声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:qvyue@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。