一个系统过载的案例及其解决办法

系统出现过载现象(或问题)的缘由和场景有不少, 这里并不试图概括总结; 而是如题, 就一个特定的案例, 分享一些过载保护的实践办法.数据库

案例

系统R须要经过轮询(读取)数据库中存储的记录状态, 进行一些业务补偿的操做, 然后再对数据库的状态进行更新.并发

轮询机制实现并未采用定时周期的方式, 而是采用 请求 - 响应 - 再请求 的事件驱动方式.单元测试

在压测的时候发现, 数据库访问(不管读写)大量失败, 错误日志狂刷.测试

分析

经排查, 因为数据库被多个系统公用, 其它系统的一些SQL执行耗时超长, 资源占用过多, 致使系统R的数据库访问请求超时失败, 而失败后的不断重试, 变本加厉, 最终数据库撑不住挂了.线程

相似的典型场景, 如 秒杀 :日志

海量并发请求下, 系统已经出现过载, 请求响应过缓, 而用户耐不住, 则不断尝试刷新页面, 寄指望于请求被响应. 可事与愿违, 系统在没有保护的状况下, 将持续超过载的状态, 直到 宕机.code

解决

面对过载, 可能最容易被想到的是 扩容. 是的, 它是一种不下降服务质量的必不可少的预防方案, 但咱们不得不接受一个事实:事件

系统的容量(或处理能力)始终是有限的, 即便不考虑成本的扩容, 也只能应付你能够预见程度, 一旦有个"万一", 系统仍将崩溃.资源

若服务能够降级, 则 限流 是可以应对"万一"的良方, 下面就 限流 这个思路, 分享几个办法.请求

定时

将轮询改用定时来实现, 就能够保证稳定的访问节奏, 加之一旦支持动态修改定时周期时间, 即可以根据实际状况灵活调整节奏了.

不只作到了 限流 , 更是作到了 流控, 不错.

能够实际应用中会发现, 初始的定时周期很难找到合适的值, 须要在处理实时性和请求量之间平衡.

我的不太喜欢用定时方案, 它不只让单元测试结果不稳定, 还浪费线程.

休息

请求 - 响应 - 再请求 的事件驱动的轮询方式, 有个好处: 就是在正常状况下, 让访问节奏由数据库说了算, 即数据库响应快, 轮询则快, 反之则放缓. 只惋惜异常的状况下, 就如案例中那样.

休息很好理解:

若人累了, 就不要继续强撑, 让身体休息一会(踹口气), 再恢复工做.

同理, 当数据库访问失败后, 优雅地拒绝掉随后若干次请求, 让数据库休息一会.

如何作到优雅? 就本案例而言则是, 读取失败后的若干次请求迅速返回空集合. 总之, 拒绝能够是除抛异常外, 任何对处理逻辑有意义的默认NULL结果.

若干次究竟是多少次? 这与选择定时时间同样是须要平衡的.

补偿

本案例中轮询的读取失败返回空集合是个好的休息办法, 可要是状态更新失败呢? 无法返回结果, 只能抛异常, 但异常又会致使重试, 这不只没让数据库休息, 且可能致使大量ERROR Stacktrace日志输出.

不抛异常, 而将异常转换为一次失败记录(日志), 这些记录能够用来对数据库状态的不一致进行补偿操做, 具体如何补偿, 什么时机开始补偿, 这里就不展开细说了, 它们都须要由业务场景来决定.


写在最后, 上述三种办法都不是银弹, 也并不是互斥, 彻底能够根据状况结合使用的.

相关文章
相关标签/搜索