高并发解决方案
引言
在关于高并发的思考一文里面,我提出了高并发会导致的问题,在那边文章里最后的结论是高并发在确保底层存储一致性的情况下,最好通过设计来避免。但实际上业务之间的耦合可能往往无法提前预料到,相互的影响要是很频繁的话,实际上很难通过设计来确保。那么我们是不是就需要一种全局锁或者类似的方案来解决这一情况。
redis 的乐观锁
在 redis 中,实际上存在一种解决方法。就是通过 WATCH、MULTI、EXEC 命令来处理。原理就是 WATCH 来监控某一个变量,然后 MULTI 批量提交一系列的命令已经修改该变量,然后 ECEC 运行。因为 WATCH 的特性,一旦该变量被修改,那么后续的事务就不会运行。因为 MULTI、EXEC 事务的特性,一系列命令会被原子的执行,不会被其他命令插入。
注意点:
- 在事务执行前,要是被 redis 检测出错误,那么就不会执行事务。
- 但如果一些在事务执行中暴露的错误会被忽略,会继续执行事务,比如对 string 做 list 的操作等等(因为这种错误一般在测试环境发现)。
不足: - WATCH 时机是应该什么时候呢?如果是在业务开始,那么稍微并发一下,碰撞的概率就会大幅度增加,不断的重试也会导致碰撞。如果是在后面,那么这个代码不确定性会大幅增加也不利于维护。
- 错误。这很明显,redis 不管编程问题。那么一旦测试遗漏,出错了的话,很难恢复(这个在兼容处理的时候,感觉出错的可能性还是很高的)。
- 局限性。基于 redis 的处理,而一般后面还有一层持久化的关系型存储。这一层在这里无法兼顾到。
seata
本质上是两阶段提交协议。
- 先开启本地事务,尝试获取全局锁(超时时间),然后提交事务,插入滚动日志,结果上报。
- 若收到回滚请求开启本地事务,根据日志回滚,上报结果。若收到提交请求,放入异步任务队列,提交结果。
SEATA 提供的长事务解决方案是 Saga 模式,但它的隔离性还是不足。实际上之前讨论的问题仍然存在,但是通过机制来降低了概率,在大部分场景可以正常。但是要求写补偿服务,即回滚接口。