惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
H
Help Net Security
Jina AI
Jina AI
V
V2EX
G
Google Developers Blog
B
Blog
GbyAI
GbyAI
U
Unit 42
爱范儿
爱范儿
腾讯CDC
Engineering at Meta
Engineering at Meta
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 三生石上(FineUI控件)
宝玉的分享
宝玉的分享
小众软件
小众软件
D
DataBreaches.Net
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - Franky
博客园 - 聂微东
The Cloudflare Blog
I
InfoQ
Microsoft Azure Blog
Microsoft Azure Blog
Hugging Face - Blog
Hugging Face - Blog
大猫的无限游戏
大猫的无限游戏

Mobility

Flint:让 skill 成为个人资产 从薅 token 到管 skill:我的 pks 工具落地实践 把笔记、微信读书、知乎装进 Obsidian:我基于llm-wiki知识中枢搭建实录 免费AI视频生成器:我如何用零成本做出带旁白字幕的多场景AI视频 Agnes免费模型真能白嫖视频?我改造了ViMax来试试 教你薅token(二):构建agent无关的skills管理工作流 教你薅token:构建agent无关的AI工作流 用 AI Agent 完成 Hexo 主题迁移:从 Next 到 Butterfly 的全自动化实践 Vercel封禁163邮箱后,我是怎么恢复博客的 用LLM管理安全开发规范:一次llm-wiki实践 Vaadin框架教程:Java工程师的前端开发秘籍 hexo多语言方案总结及最佳实践 知乎增强工具-评论时间精确到秒 怎么理解数据库的四个隔离级别 kubernetes是什么-实用向教程 怎么更科学的用知乎摸鱼 读书笔记《系统之美》,如何面对现实中的复杂问题 分布式系统设计中的通用方法 高并发解决方案很难吗?轻松聊清楚高并发设计 SSP,DSP,RTB,ADX都是什么? 讲讲互联网广告的概念与发展 从redolog,undolog到隔离级别,刨根问底,讲清楚事务和ACID java项目低学习成本使用kubernetes的实践经验 剧变中的2021-一个中年工程师的年终总结 kubernetes环境下做金丝雀发布的一种思路 prometheus教程: 一篇文章讲懂prometheus 实现一个简单的java版本高性能获取ip地址所属国家工具 iterm2配置ssh书签, 实现记住密码和自动登录 怎样做一个好的技术分享 云原生究竟是什么 读书笔记 稻盛和夫《干法》-思考应该怎样去工作
高并发下作余额扣减的一些经验
流沙 · 2018-08-25 · via Mobility

前一段时间参加了优化一个老的计费系统,学习了一些高并发下做余额扣减的常用手段,也做了一些尝试,因此在这里总结记录一下, 在高并发下对。

问题描述

对于一个计费系统来说,并发问题事实上分为两类,一类是应用并发高,也就是纯粹的用户量大,访问量多,这类问题和一般的高并发问题没有区别,用分布式等手段就可以解决;另外一类问题则是一般分布式手段无法解决的用户高并发问题,也是本文要着重说的。
这类问题源自对某些高频账号,大量的并发访问,会导致瓶颈首先出现在某些数据库记录上,大量操作由于无法竞争到数据库的行锁而导致等待,这些等待中的操作又会占用其他资源,最终导致系统不可用。
针对这类问题,下边介绍一些常用的处理办法。

不设置余额字段

由于对于一个稳定的计费来说,一定是会记录计费流水明细的,所以完全可以不设置余额字段,而采用根据流水明细计算的方式来获取余额。
不过这种方法不是万能的,比如拿广告业务的计费系统来说,频率非常高,而每次的金额很小,这时候想通过计算求和去算余额,显然是不现实的。

合并与拆分

这是两种方式,因为有一些相似之处,都是要降低对单条数据库记录的访问压力,所以也就放到一起说了。
合并,就是对单个账号的数次请求作合并处理,再往数据库写,这样就等于降低了数倍的压力。
拆分,则是把一个主账号拆分成数个子账号,然后把请求分配到各个子账号上,这样单个账号的压力就小了。然后再用其他手段把子账号的数据合并成主账号数据,返回给用户。

减少行锁占用时间

这是个代码层面的优化,前面说了,高频账号之所以会导致系统的性能问题,就是因为要竞争行锁,所以,如果我们能减少每次请求占用行锁的时间,系统性能也就会大幅度提升了。
所以,首先,要尽量加快从获取行锁到事务提交这个阶段的运行速度,将不必要的操作,尤其是一些耗时的操作,放到其他地方执行,比如获取行锁之前或者事务以外。
然后,尽量避免用

1
select ... for update

的方式去获取行锁,二是采用下面这种方式:

1
update xxx set amount=amount-1 where id=x and amount>=1

如果业务层面允许余额扣减成负数的话,就可以不使用where条件中对金额的校验;否则就需要在将要把余额扣成负数时不去更新数据库,并在程序中返回异常。

限流

既然高频账号是单账号的并发达到一定程度后才会导致系统的性能问题,所以我们就可以强制控制这个并发量,使它永远保持在系统可接受的范围内。

缓存

缓存也是一个常用的解决高频账号的方法,在缓存中对余额作操作,然后定时向数据库内同步。

上边介绍了很多方法,当然每一种都会有它的适用条件,也会有其局限性。比如像合并啊,限流啊,实际上都会造成延迟扣费,而在延迟的这个时间段内,可能账户余额已经耗尽,所以如果是严格余额不能为负也不能丢弃记录的业务场景下,其实并不适合用这种方式。
所以说,最重要的还是根据业务场景选择合适的方案。

原文地址: https://lichuanyang.top/posts/56940/