美文网首页
上账务系统余额并发更新问题记录

上账务系统余额并发更新问题记录

作者: linux服务器开发 | 来源:发表于2018-10-24 15:19 被阅读24次

某电商平台,某天线上用户报bug说账户余额信息与交易流水对不上。可以认为是数据库并发更新问题,由此定位出具体原因,并给出解决方案。

问题现象

场景描述

线上账务系统,在定时结算给卖家钱时,且高并发量的情况下,出现提现x元(假设当前用户余额为x元)余额为0后,再转入该账户一笔钱(假设为y元),结果账户余额变为了x+y 元,导致用户余额错误。 ps:账户余额的变更都是在事务中update的

环境说明

mysql5.7 + innodb,事务隔离级别是REPEATABLE-READ

场景模拟

我们简化下线上的数据结构,进行场景模拟。 数据表如下:

‘账户主表’

CREATE TABLE user (

uid int(11) NOT NULL COMMENT '类型id+自增序列',

name varchar(32) DEFAULT NULL,

PRIMARY KEY (uid)

) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='账户主表'

‘账户余额明细表’

CREATE TABLE user_account (

uid int(11) NOT NULL,

amount decimal(19,4) DEFAULT 0 COMMENT '账户余额',

PRIMARY KEY (uid)

) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='账户余额明细表'

账户类型配置

CREATE TABLE user_conf (

type_id int(11) NOT NULL, description varchar(32) DEFAULT NULL COMMENT '类型描述', PRIMARY KEY (type_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='账户类型配置'

具体数据为:

select * from user;

+-------+------+ | uid | name | +-------+------+

| 10001 | a |

| 10002 | b |

select * from user_account;

+-------+----------+ | uid | amount | +-------+----------+

| 10001 | 10.0000 |

| 10002 | 108.9900 |

select * from user_conf;

+---------+--------------+ | type_id | description | +---------+--------------+

| 100 | 外部账户 |

| 200 | 内部账户 |

模拟提现(即余额减)和入账(即余额加)并发操作的事务如下:

session1-提现10元session2-入账20元begin;select description from user_conf where type_id = 100;select * from user where uid = 10001 for update; // user表用来做互斥select amount from user_account where uid = 10001; // 10.00begin;select description from user_conf where type_id = 100;select * from user where uid = 10001 for update; // wating//watingupdate user_account set amount = 0.00 where uid = 10001;commit;拿到锁select amount from user_account where uid = 10001; //10.00入账20元,代码中计算后应该为30元update user_account set amount = 30.00 where uid = 10001;commit;

问题出现了,后面再查询该用户余额为30元,即用户提现的10元未反映在余额中

原因定位

熟悉mysql的同学或许已经知道问题是由REPEATABLE-READ隔离级别下快照读导致。

具体解释:

RR级别下,第一次读操作会生成快照,对于可见性来说,只有当第一次读之前其他事务提交的修改和自己的修改可见,其他的均不可见。

官网文档:https://dev.mysql.com/doc/refman/5.7/en/glossary.html snapshot A representation of data at a particular time, which remains the same even as changes are committed by other transactions.

With REPEATABLE READ isolation level, the snapshot is based on the time when the first read operation is performed.

可见性原理

回到上述模拟场景中,session2在sql语句select description from user_conf where type_id = 100; 时已生成快照,虽然session1提交了,但仍然不可见,导致并发更新问题。

另外,开启事务后,SELECT … FOR UPDATE 是不会生成快照的,大家可自行实验

解决方案

方案一

将REPEATABLE-READ隔离级别改为READ-COMMITTED,这样即能看到最新提交的数据。

方案二

在读’账户余额明细表’user_account 的时候加 for update,这样会 1.强制读该行记录的最新版本数据,2.且若其他事务未commit,本事务将阻塞,保证串行更新

方案三

延时生成快照。开启事务后,首先就通过user表做互斥,直接for update加锁,针对多个事务并发更新即变为串行。

附:定位过程

针对上报bug用户,查询其交易流水明细与余额变更明细,确认账务存在问题

查询账务系统近几天是否有上线变更,检查无

拉取账务数据库mysql general log,找到并发更新的两个事务session

查询数据库设置的隔离级别为RR,查询应用数据库连接池配置即session的隔离级别未配置,采用数据库配置

确认由RR级别导致(当然也可以认为是代码问题导致)

确认是一个月前账务系统分库分表上线,改用其他连接池且未设置session隔离级别。而之前是有配置session的隔离级别为READ-COMMITTED。

延伸思考

mysql RR级别适用的业务场景是什么,应该怎么选择? 有兴趣或有见解的同学可以留言回复或私信~~

在这里给大家提供一个学习交流的平台,java架构师群: 867748702

具有1-5工作经验的,面对目前流行的技术不知从何下手,需要突破技术瓶颈的可以加群。

在公司待久了,过得很安逸,但跳槽时面试碰壁。需要在短时间内进修、跳槽拿高薪的可以加群。

如果没有工作经验,但基础非常扎实,对java工作机制,常用设计思想,常用java开发框架掌握熟练的可以加群。

________________________________________________________________________________________________

加Java架构师进阶交流群获取Java工程化、高性能及分布式、高性能、深入浅出。高架构。

性能调优、Spring,MyBatis,Netty源码分析和大数据等多个知识点高级进阶干货的直播免费学习权限

都是大牛带飞 让你少走很多的弯路的 群号是: 867748702对了 小白勿进 最好是有开发经验

注:加群要求

1、具有工作经验的,面对目前流行的技术不知从何下手,需要突破技术瓶颈的可以加。

2、在公司待久了,过得很安逸,但跳槽时面试碰壁。需要在短时间内进修、跳槽拿高薪的可以加。

3、如果没有工作经验,但基础非常扎实,对java工作机制,常用设计思想,常用java开发框架掌握熟练的,可以加。

4、觉得自己很牛B,一般需求都能搞定。但是所学的知识点没有系统化,很难在技术领域继续突破的可以加。

5.阿里Java高级大牛直播讲解知识点,分享知识,多年工作经验的梳理和总结,带着大家全面、科学地建立自己的技术体系和技术认知!

相关文章

  • 2018-04-25

    线上账务系统余额并发更新问题记录 物联网技术周报第 134 期: 智能音箱 Alexa 与 Arduino 制作家...

  • 上账务系统余额并发更新问题记录

    某电商平台,某天线上用户报bug说账户余额信息与交易流水对不上。可以认为是数据库并发更新问题,由此定位出具体原因,...

  • PDCA案例-用友软件上查找余额缺少的位置

    事件:用友账务上银行存款余额差50元,查找出问题在哪里并改正 一. 计划plan: 7-9月的账务没问题,问题会在...

  • 支付宝是如何处理热点账户高并发转账问题的

    前言:方案设计前提 一般账务系统对账户的冲扣需要满足以下两点 1:更新账户表中的账户余额。 2:记录账户明细表中的...

  • 第九章 账务处理子系统设计

    第九章 账务处理子系统设计 第一节 账务处理子系统概述 [if !supportLists]一、[endif]账务...

  • 锁住余额,为何还会更新异常?

    背景 现有一个交易系统,每次交易都会更新余额。出账扣减余额,入账增加余额。为了保证资金安全,余额发生扣减时,需要比...

  • 学校类客户调查重点

    1.调查学校总资产,重点核实固定资产(房屋建筑物)、无形资产(土地)、教学设备投入,主要通过账务系统(科目余额表)...

  • 后端存储4

    如何保证账户系统中流水和余额数据一致? 账户系统用于记录每个用户的余额,为了保证数据的可追溯性,通常都是需要记录账...

  • 超全!事业单位的会计分录,终于找到了!

    一零余额账户用款额度国库集中收付 基本账务处理: 借:零余额账户用款额度 贷:财政补助收入 借:存货、事业支出等...

  • 核心银行系统 之十七 批量任务

    批量任务 账务处理子系统作为核心银行系统的系统内核,这使得账务处理子系统的设计和实现成为商业银行核心业务系统构建过...

网友评论

      本文标题:上账务系统余额并发更新问题记录

      本文链接:https://www.haomeiwen.com/subject/fmcxtqtx.html