目录
一、热点账户
二、热点账户常见问题
三、纯修改余额方式及其特点
热点账户就是高频进行扣款、入账的账户,也就是热点账户该条数据为热点数据,会被频繁更新。一般热点账户分为两种,一种是频繁扣款的热点账户,另外一种是频繁入账的热点账户。
1、性能瓶颈问题
2、数据库压力问题
3、成功率问题
1. 乐观锁
操作方式:
查询账户数据:
SELECT BALANCE, STATUS, VERSION, … FROM ACCOUNT WHERE ID = ?
计算余额:
POST_BALANCE = BALANCE + AMOUNT
或者
POST_BALANCE = BALANCE - AMOUNT
更新账户余额:
UPDATE …. BALANCE = POST_BALANCE,VERSION = VERSION +1 WHERE ID = ? AND VERSION = ?
更新返回1,更新成功,返回0,更新失败,需抛出异常,回滚事务
插入账户历史:
INSERT …
优点:
不会存在阻塞,响应时间快;
数据库没什么压力;
在内存里可以完成很多复杂操作;
缺点:
成功率不高,真的存在并发时,失败的请求比较多;
有效的性能依然不高;
一般应对方案:
采用重试的方式,立即重试三次,以提高成功率
个人看法:
这种重试在真正的有量的时候基本没啥作用,相反会徒增数据库的请求量,鄙人觉得这种重试只能解决请求量较小的时候的并发,比如突然同时进来两笔同一个账户的请求,处理失败的话进行重试是可以解决问题的;但是一瞬间进来200笔,甚至更多的话,这种重试没啥作用了。
2. 悲观锁
操作方式:
查询账户数据:
SELECT BALANCE … FROM ACCOUNT FOR UPDATE WITH RS
计算余额:
POST_BALANCE = BALANCE + AMOUNT
或者
POST_BALANCE = BALANCE - AMOUNT
更新账户余额:
UPDATE …. BALANCE = POST_BALANCE,VERSION = VERSION +1 WHERE ID = ?
插入账户历史:
INSERT …
优点:
成功率高;
性能好;
在内存里可以完成很多复杂操作(余额签名);
缺点:
会存在阻塞,响应时间长;
数据库压力大;
一般应对方案:
采用信号量做热点账户资源使用限制,可以控制数据库压力,为数据库分压,且保持在一个客观的性能水平。
个人看法:
这种方式能解决大部分的热点账户问题,也是本人之前采取的方式,不过偶尔会存在的超时问题也仅仅是一两笔,其余的都会被信号量拒绝了。
3.数据库行级锁1
操作方式:
更新余额:
入账:
UPDATE BALANCE = BALANCE +AMOUNT WHERE ID = ?
扣款:
UPDATE BALANCE = BALANCE - AMOUNT WHERE ID = ? AND BALANCE > AMOUNT
读取账户数据:(读取数据是为了在账户历史插入的时候保留发生后余额)
SELECT * FROM ACCOUNT WHERE ID = ? WITH CS
插入数据
INSERT ...
优点:
成功率高;
性能好(相对于2);
数据库压力也会小(相对于2);
相应时间也小(相对于悲观锁);
缺点:
一些复杂的操作无法在内存完成了(余额签名)
一般应对方案:
复杂的操作异步化,延迟也就是毫秒级别,或者舍弃签名
个人看法:
这种方式与悲观锁相比好了很多,数据库压力小,性能高了,许增加上单账户限流或者信号量,防止单账户暴涨的量把数据库压爆。
4.数据库行级锁2
操作方式:
更新余额:
入账:
UPDATE BALANCE = BALANCE +AMOUNT WHERE ID = ?
扣款:
UPDATE BALANCE = BALANCE - AMOUNT WHERE ID = ? AND BALANCE > AMOUNT
插入数据
INSERT …
异步更新发生后余额:(或者根据业务情况不需要该步骤)
UPDATE ACCOUNT_HISTORY SET POST_BALANCE = ? WHERE ACCOUNT_ID =?
优点:
成功率高;
性能好(相对于3);
数据库压力也会小(相对3);
相应时间也小(相对3);
缺点:
代码复杂度高,需要异步化一些处理;
个人看法:
一些非核心部分的修改及操作,不需要就去掉,需要的话那就异步处理下。
以上为本人一些粗浅的看法及实践,如有错误或者不恰当处,还望海涵,帮忙指出,也欢迎留言讨论,邮箱地址[email protected],下次一起讨论一下更新余额方式之外的热点账户的一些看法。
环境:Spring + Mybatis + DB2
欢迎转载,转载请注明出处,谢谢。