mongo——事务详解

    mongo事务的本质是给自己写操作执行到的每一行记录加上一个写锁,而如果同时另一个事务也开始修改当前行时,会尝试去获取这个锁,然后阻塞规定时间,如果还没有获取到就会抛出org.springframework.data.mongodb.UncategorizedMongoDbException: Command failed with error 112 (WriteConflict): 'WriteConflict’的异常。而这个锁会等到事务提交,才会释放。需要注意的时,读操作是没有锁的,所以当你对某一行增加事务后,还是能读取改行数据的。

    需要注意的是,当有多个操作时,不管这多个操作时同一个表还是跨表跨库,这多个操作所要操作的多行其实时多个锁,比如事务A有a,b两个操作,事务B有b,a两个操作。当A事务和B事务一起执行,他们都会顺利的执行完自己的第一个操作,等到A执行b的时候发现b的锁被B事务拿着,这是就会等直到超时,B事务同样会因为获取a操作的锁而陷入等到到超时,除非其中一个因为先超时导致锁释放,让另外一个有机会完成事务。

    上面说到当执行某一行操作时会去获取锁,如果持续获取不到锁,等到了超时时间就会抛出writeConflict,这个超时时间默认是5毫秒,可以看到是相当的短,这个参数是可以进行调整的,以下是该参数的集中调整方式:

方式一:使用这个可以在线修改这个值

db.adminCommand( { setParameter: 1, maxTransactionLockRequestTimeoutMillis: 3000 } );
方式二:启动的时候加入参数

mongod --setParameter maxTransactionLockRequestTimeoutMillis=3000
方式三:在(/etc/mongod.cnf)中加入一下配置

setParameter = maxTransactionLockRequestTimeoutMillis=3000
其中3000为最大锁等待时间,可自由调配

writeConflict优化

减少事务的时间浪费

    当我们开发的时候,对于需要加事务的地方,我们总是喜欢在函数外面想都不想套一个@Transactional,对于一个并发量不大的系统来说,这样是没有问题的。而当并发量上来之后,粗暴的直接加@Transactional就会造成系统大量的writeConflict,其实根本原因就是因为一些大函数,里面包含了大量的各种计算,逻辑判断,和服务交互行为,这往往会造成你的事务长达几百毫秒或者一两秒之久,但是其中真正的数据库操作可能耗时不过几毫秒或者几十毫秒(几百毫秒或者一两秒对于用户来说并不是一个较大的延迟,但对于数据库来说缺是一个很长时间的耽误),这就造成大事务对数据库产生极大的冗余。所以对于一个并发高的系统来说,我们需要去精细化思考我们的事务范围,而不是简单粗暴的直接加个大事务。

避免大事务和事务等待时间调节

    上面有讲到事务获取锁时会等待一定时间,超过最大等待时间,就writeConflict了,所以我们在设计上就要避免大事务,比如事务中不能有过多操作,批处理之类的一次性更新几十几百甚至几千条数据的操作不能加事务。另外可以根据上面的方法对最大等待时间进行调节,但是不能调节的过大,上面说到最大可以3秒钟,但是这可能会导致服务器大量的堵塞,所以需要自己去尝试调整这个参数。

高频数据和低频数据分事务

    比如我们的用户表和日志表就可以分开事务,用户扣钱走一个事务,其他不重要的事情通过mq异步处理,这样你只要将扣钱和发mq这两个操作放在一个事务里就好了。其他的操作,在异步里另外作为一个事务进行处理。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值