最近优化一个接口的性能,发现是为了保证数据一致性,需要依赖数据库事务。所以MySQL成为了性能的瓶颈。
业务需求:
扣除用户的积分,兑换库存礼品。
1、礼品不能超发。
2、如礼品发放成功,必须成功扣减用户的积分,保证经济利益不受损害。
3、用户礼品对换有次数限制。
代码示例:
@启动事务
foo1(){
查询礼品信息和数量()
判断礼品是否存在,return
查询用户的积分()
判断用户积分是否够兑换礼品,return
foo2(礼品价值,兑换数量)
}
foo2(礼品价值,兑换数量) {
统计用户礼品兑换次数()
判断兑换数量,return
用户积分更新(礼品价值*兑换数量)
礼品数量更新(兑换数量)
}
从代码中看,foo1被包含在整个数据库事务中。一个方法执行,所以的SQL查询都被阻塞。
在这5条SQL中,foo2中的两次更新操作必须在一起执行。foo1中的查询SQL可以不包含在数据库事务中。查询结果,作为更新时的判断条件,就可以保证查询的数据在更新之前没有被别人操作过。统计语句的结果,需要在程序中进行逻辑判断。在更新时,无法对统计结果是否改变进行有效的判断,除非再次统计数据。
通过上面的分析对代码进行了适当的改造:
foo1(){
查询礼品信息和数量()
判断礼品是否存在,return
查询用户的积分()
判断用户积分是否够兑换礼品,return
foo2(礼品价值,兑换数量,用户当前积分)
}
@启动事务
foo2(礼品价值,兑换数量,用户当前积分) {
统计用户礼品兑换次数()
判断兑换数量,return
用户积分更新(礼品价值*兑换数量,用户当前积分)
礼品数量更新(兑换数量)
}
1、礼品数量更新,并没有判断是否与之前的数量相等,因为只要不超发就可以。
2、当用户进行积分更新时,where条件要判断当前的积分和之前查询出的积分是否一致。这里其实对业务逻辑增加了一些限制。既用户在进行积分兑换时,不能执行其它的操作。这个逻辑限制本身也符合真实业务场景,对用户操作体验没有影响。
大家会发现瑞很多技术问题在网上都有现成的答案,并且是很多年以前的答案。但是在程序中还会犯一样的错误,也许是我们老了,新一代的程序员在成长过程还需要不断的重新摸索。
网友评论