我们在做架构设计的时候,会提到几个关键词:高性能、高可用、可扩展、安全性、伸缩性、低成本等等。对于用户量不大、并发量不高的系统,我们没必要去追求高性能,甚至连架构设计都可以免了。
那么什么样的系统需要做性能优化呢?当你发现系统响应越来越慢,慢到已经影响到用户体验的时候;
网站性能优化的手段:
1、 web前端优化;
减少http请求;
使用浏览器缓存;
静态资源压缩;
减少cookie传输;
CDN加速;
反向代理;
2、 应用服务性能优化;
分布式缓存,通过添加缓存来提高应用层的响应效率;
消息异步化:线程、队列等等;
集群服务;
代码优化:事务粒度调整、算法优化等;
3、 数据库层优化;
表结构优化、SQL优化,索引等等;
我们先看看交易所币币交易的业务流程:
拿委托来说,原有的逻辑如下:
用户登录后发起委托申请;
后台进行用户状态、资金密码可用、货币状态等校验;
校验通过后将委托单入库并冻结用户可用余额;
将委托单发送给撮合队列进行撮合;
通过梳理委托业务流程,我们发现委托的性能瓶颈主要在数据库层面,包括第二步的校验和第三步的数据持久化。而第四步的入队列操作比较简单,简单来说就只是一个队列消息发送,原则上并不会产生性能瓶颈。
第二步的校验如何进行优化?校验是必须的,不校验是不可能的,这辈子都不可能。那那那怎么办?
校验数据读缓存。
首先,用户登录时已经将用户信息放入SESSION,用户状态校验直接拿SESSION信息进行比对就可以了。考虑到用户登录后用户状态信息可能会调整,那么在调整后需要将用户信息及时更新到SESSION。另外,测试在做登录压测的时候,发现登录接口的吞吐量一直上不去,查表发现用户表数据量比较大,登录是通过手机号码进行登录的,所以我们对用户表的手机号码列加了唯一索引。
资金密码可用的校验需要查用户密码策略表进行交易。用户密码策略基本属于较少变更的信息,可以将密码策略加入常驻缓存(一直放在缓存)。另外,为提高用户密码策略表的查询效率,对密码策略表创建用户ID和货币ID联合索引。
我们直接将货币信息、货币对信息加入常驻缓存,后台货币有调整时及时更新至缓存,所以货币状态的校验也改成了缓存读取数据并做校验。
第三步的数据持久化怎么办?第三步操作还涉及到用户可用余额的校验,用户可用余额校验必须要在用户发起委托申请时来做,看来这一步不能省。那还有优化的空间么?
我们借鉴了互金资产交易系统中防超投的处理方案,将用户可用余额添加到缓存。有人会问,如果数据库数据和缓存数据不一致怎么办?解决方案是缓存操作和数据库操作都保持同步,如果不能同步更新,那至少也需要保证缓存数据和数据库数据的最终一致。
具体到委托申请这块,我们先冻结缓存中的用户可用余额,然后将委托单加入撮合队列,在进行撮合的时候再将冻结金额持久化到数据库。
简单总结一下委托下单的优化点:
数据校验读缓存,以减少频繁查库带来的数据库压力;
数据持久化先入队列,延迟写入数据库,以降低数据库的压力;
为数据库表添加必要的索引,提高查询效率;
接下来,我们看下挂单撮合的业务流程:
冻结可用金额持久化和委托单持久化;
从对方队列队首取出委托单进行撮合;
撮合成功后将撮合结果添加到撮合持久化队列;
我们以挂买委托单为例来了解一下撮合的操作流程:
1、在挂买委托单过来之后,从卖队列(所有未撮合完成的卖委托单组成的集合)中弹出队首的卖委托单。
2、如果无卖委托单或者卖委托单的价格高于买委托单的价格,则不进行撮合,将买委托单加入买队列集合。如果卖委托不为空,将卖委托重新加入卖队列。
3、如果卖委托单价格低于或者等于买委托单价格,则进行撮合,撮合成交量取买卖委托单剩余挂单量的最小值。
4、撮合完成的委托单不再加入队列,未完成的需要重新加入队列,加入队列的规则如下:
买队列中按价格从高到低排列,如果买委托队列为空或队首的挂单价小于当前订单挂单价,将该单加入对队首,否则遍历插入相应位置。
卖队列中按价格从低到高排列,如果卖委托队列为空或队首的挂单价大于当前订单挂单价,将该单加入对队首,否则遍历插入相应位置。
第一步的可用冻结和委托单持久化操作原本是在委托申请时入库的,现在移到这里排队入库。这里没有较为明显的优化点,要么继续将持久化操作后置,要么是改为批量入库。
第二步操作是内存的操作,优化主要集中在算法上;
第三步操作和委托的第四步操作类似,仅仅是消息入队列,原则上不会产生性能瓶颈。
下面重点讲一下第二步撮合操作在算法上的优化:
1、原有的挂买、挂卖未撮合完成的委托单都放在本地内存中,为了更好的支持集群服务,我们首先将买卖队列由本地缓存改为Redis的List类型缓存;
2、将撮合队列由原来的单个队列按业务拆分成挂买撮合、挂卖撮合和撤单三个队列,队列拆分类似于服务的横向扩展,可以在一定程度上提高系统的吞吐量,提升队列的处理能力,防止队列中消息堆积过于严重拖慢了整个服务的处理速度;
3、挂买撮合、挂卖撮合开启多线程服务,每个队列开启10个线程,支持单机环境的并发操作;
4、未撮合完成的委托单入缓存的优化,在第一次改版中,我们借助于Redis的List集合的加入、弹出等单线程操作,取得了很好的效果。但是在高并发场景下,会出现可以撮合却未进行撮合等问题。
5、针对产生的问题,先是加了自动撮合定时器来自动撮合价格合适的买卖委托单,但是效果不是很明显。
6、我们分析买卖队列数据,发现部分委托单存在排序错乱的情况。我们的算法其实没什么问题,但是在高并发下确实存在该问题。针对这个问题,大家首先想到的是加锁,通过锁来控制队列的进出,进而保证队列集合按价格顺序排列。但是这里又必须支持集群,如果加锁,势必会影响性能。那怎么办?有没有什么办法,既能支持多线程服务,还不需要加锁?
7、启用lua脚本,将委托单的入队列操作单独抽取出来,改为lua脚本实现。
简单总结一下撮合操作的优化点:
队列拆分将操作频繁的队列(如撮合队列)按业务拆分;
队列多线程 买卖队列开启多线程服务;
使用lua脚本 提高委托单入队列的效率;
然后,我们来看看撮合持久化的业务如何做持久化?
原有的撮合持久化操作,是一个个排队消费处理的。
撮合持久化的第一次优化是将队列中的持久化改为批量处理,如货币资金变更、资金日志、交易记录等等。
压测过后发现消息堆积仍然比较严重,然后尝试将撮合持久化改为多线程处理,发现效果不是很明显,并且偶有死锁产生,这就说明通过开启多线程提高撮合持久化处理能力是行不通的。那么还有没有其他的办法呢?
我们知道消息推送分为两类:推(Push)模式和拉(Pull)模式。RabbitMQ默认的消息推送模式是Push模式。
推模式是长连接模式,能做到实时处理,提高响应速度。推模式缺点也比较明显,一次只能处理一个请求。
而拉模式和推模式刚好相反,不能做到消息实时处理,可以一次拉取多个消息。我们的持久化操作对实时性要求不是那么高,可以通过一次拉取并处理多个消息来提高系统的并发量,进而在一定程度上减少消息堆积的量。
我们在撮合持久化消费者端开启一个线程服务,用来消费撮合持久化队列。线程的消息推送模式改为拉模式,每次拉取20个消息,处理完毕休眠一段时间。休眠时间的长短根据队列中消息有无来进行调整,当队列中没有消息时,让线程休眠时间长一点,比如5s;当队列中有消息堆积时,让线程休眠时间短一点或者不休眠;
简单总结一下撮合持久化的优化点:
数据持久化单改批,但是批量操作的量不要设置的太大;
消息推送模式推改拉,提高并发处理能力;
性能优化总结:
分布式缓存,通过添加缓存来提高应用层的响应效率;
消息异步化:线程、队列等等;
集群服务;
代码优化:事务粒度调整、算法优化等;
相关推荐
具体内容包括:基于用户体验的性能优化要素、前端性能优化实战、网站性能分析、服务端性能优化、TCP优化、DNS优化、CDN优化、大型网站性能监控体系、大型网站容量评估、高性能系统架构模式、大促保障体系、数据分析...
在《Linux性能优化实战》案例中,我们深入探讨了如何利用Linux系统工具和技术来提升系统的运行效率和性能。Linux作为一款开源操作系统,其强大的可定制性和丰富的工具集使其成为性能优化的理想平台。以下是一些核心...
### 携程H5性能优化实战 #### 一、H5概述与发展 - **HTML5定义**:HTML5是HTML最新的修订版本,由万维网联盟(W3C)于2014年10月完成标准制定。其目标在于替代1999年的HTML4.01和XHTML1.0标准,以适应互联网应用的...
性能优化手册是一套java性能学习研究小技巧,包含内容:Java性能优化、JVM性能优化、服务器性能优化、数据库性能优化、前端性能优化等。 内容包括但不限于: String 性能优化的 3 个小技巧 HashMap 7 种遍历方式...
在讨论手机淘宝Hybrid App性能优化实战这一主题之前,我们首先需要了解几个关键的技术点和概念,这些是理解如何优化Hybrid App性能的基础。 1. H5生命周期触发及页面加载进度 H5页面在Hybrid App中是指内嵌的网页,...
### 大型网站性能优化实战从前端网络CDN到后端大促的全链路性能优化 #### 一、基于用户体验的性能优化要素 在现代互联网应用中,用户体验是衡量一个网站成功与否的重要标准之一。良好的用户体验不仅仅体现在美观的...
### Java性能优化实战知识点概述 #### 一、理论分析篇 **1.1 性能优化的衡量指标及注意事项** - **衡量指标**: 包括响应时间、吞吐量、资源利用率等。 - **注意事项**: 在进行性能优化时,需确保优化方案不会引入...
本实战指南将探讨从前端网络到后端大促的全链路性能优化策略,旨在提升网站的整体效率。 前端网络性能优化主要关注减少页面加载时间。CDN(Content Delivery Network)是一个关键的工具,它通过在全球部署多个节点,...
根据提供的文件信息,我们可以推断出这是一本关于Java程序性能优化的书籍,作者是葛一鸣,并提供了该书PDF版本的下载链接。虽然没有具体的书籍内容,但基于标题、描述以及通常这类书籍会涉及的主题,我们可以总结出...
05-一线大厂Redis高并发缓存架构实战与性能优化_ev.rar05-一线大厂Redis高并发缓存架构实战与性能优化_ev.rar05-一线大厂Redis高并发缓存架构实战与性能优化_ev.rar05-一线大厂Redis高并发缓存架构实战与性能优化_ev...
在"笔记-8b、实战项目-性能优化实战2"中,主要讲述了两个核心的优化策略:业务分析和文档处理改进。 首先,性能优化的基础是对业务的深入理解。在本案例中,性能优化的切入点是缓存数据结构的选择。业务涉及到从...
│ 开篇词 Java 性能优化,是进阶高级架构师的炼金石.mp4 │ 02 理论分析:性能优化有章可循,谈谈常用的切入点.mp4 │ 03 深入剖析:哪些资源,容易成为瓶颈?.mp4 │ 04 工具实践:如何获取代码性能数据?....
在Java性能优化实战的21讲中,涵盖了Java开发中至关重要的性能调优技术,旨在提升应用程序的效率、稳定性和可扩展性。以下是对这些关键知识点的详细解析: 1. **JVM内存模型**:理解Java虚拟机(JVM)的内存结构是...
iOS性能优化 iOS是苹果公司开发的移动操作系统,广泛应用于iPhone、iPad、iPod touch等苹果设备上。随着移动设备的普及,为移动设备提供流畅稳定用户体验的重要性日益凸显,而性能优化是实现这一目标的关键步骤。...
Java秒杀系统方案优化-高性能高并发实战 Java秒杀系统方案优化-高性能高并发实战
### 阿里巴巴Java性能调优实战-最新经验总结 #### 一、引言 在互联网技术高速发展的今天,高性能、高可用性的系统成为企业的核心竞争力之一。特别是在大规模、高并发的应用场景下,如何确保系统的稳定运行并提供...
MySQL 表和索引优化实战 MySQL 表和索引优化实战是指在 MySQL 数据库中对表和索引进行优化,以提高查询效率和存储空间利用率。本文将从 MySQL 表和索引的基本概念出发,探讨 MySQL 表和索引优化的实战经验和技巧。...
对 Java 工程师而言,性能优化能力决定了你能否进入大厂或成为一名高级工程师。但性能优化的能力却极难提升,如果你缺乏正确的方法论和实战演练,则很容易事倍功半。 视频大小:1.2G
《GPU高性能编程CUDA实战-代码》是一份针对CUDA编程技术的实践教程,旨在帮助开发者深入理解和应用GPU(图形处理器)的并行计算能力。CUDA,全称Compute Unified Device Architecture,是NVIDIA公司推出的一种用于...
阿里巴巴Java性能调优实战