交易系统热点账户问题(一)

目录

一、热点账户

二、热点账户常见问题

三、纯修改余额方式及其特点


一、热点账户

        热点账户就是高频进行扣款、入账的账户,也就是热点账户该条数据为热点数据,会被频繁更新。一般热点账户分为两种,一种是频繁扣款的热点账户,另外一种是频繁入账的热点账户。

二、热点账户常见问题

        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

    欢迎转载,转载请注明出处,谢谢。            

     

猜你喜欢

转载自blog.csdn.net/laoxilaoxi_/article/details/80963905